--- /srv/rebuilderd/tmp/rebuilderd0uoXMA/inputs/libjs-openlayers_2.13.1+ds2-11_all.deb +++ /srv/rebuilderd/tmp/rebuilderd0uoXMA/out/libjs-openlayers_2.13.1+ds2-11_all.deb ├── file list │ @@ -1,3 +1,3 @@ │ -rw-r--r-- 0 0 0 4 2025-03-06 18:35:30.000000 debian-binary │ -rw-r--r-- 0 0 0 3684 2025-03-06 18:35:30.000000 control.tar.xz │ --rw-r--r-- 0 0 0 710500 2025-03-06 18:35:30.000000 data.tar.xz │ +-rw-r--r-- 0 0 0 717360 2025-03-06 18:35:30.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 {} │ │ │ │ @@ -52,37 +52,14 @@ │ │ │ │ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS │ │ │ │ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN │ │ │ │ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) │ │ │ │ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE │ │ │ │ * POSSIBILITY OF SUCH DAMAGE. │ │ │ │ */ │ │ │ │ /* ====================================================================== │ │ │ │ - Rico/license.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @license Apache 2 │ │ │ │ - * │ │ │ │ - * Contains portions of Rico │ │ │ │ - * │ │ │ │ - * Copyright 2005 Sabre Airline Solutions │ │ │ │ - * │ │ │ │ - * Licensed under the Apache License, Version 2.0 (the "License"); you │ │ │ │ - * may not use this file except in compliance with the License. You │ │ │ │ - * may obtain a copy of the License at │ │ │ │ - * │ │ │ │ - * http://www.apache.org/licenses/LICENSE-2.0 │ │ │ │ - * │ │ │ │ - * Unless required by applicable law or agreed to in writing, software │ │ │ │ - * distributed under the License is distributed on an "AS IS" BASIS, │ │ │ │ - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or │ │ │ │ - * implied. See the License for the specific language governing │ │ │ │ - * permissions and limitations under the License. │ │ │ │ - */ │ │ │ │ -/* ====================================================================== │ │ │ │ OpenLayers/SingleFile.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ @@ -540,14 +517,37 @@ │ │ │ │ OpenLayers.Util.extend(OpenLayers.Console, console); │ │ │ │ break; │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ })(); │ │ │ │ /* ====================================================================== │ │ │ │ + Rico/license.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @license Apache 2 │ │ │ │ + * │ │ │ │ + * Contains portions of Rico │ │ │ │ + * │ │ │ │ + * Copyright 2005 Sabre Airline Solutions │ │ │ │ + * │ │ │ │ + * Licensed under the Apache License, Version 2.0 (the "License"); you │ │ │ │ + * may not use this file except in compliance with the License. You │ │ │ │ + * may obtain a copy of the License at │ │ │ │ + * │ │ │ │ + * http://www.apache.org/licenses/LICENSE-2.0 │ │ │ │ + * │ │ │ │ + * Unless required by applicable law or agreed to in writing, software │ │ │ │ + * distributed under the License is distributed on an "AS IS" BASIS, │ │ │ │ + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or │ │ │ │ + * implied. See the License for the specific language governing │ │ │ │ + * permissions and limitations under the License. │ │ │ │ + */ │ │ │ │ +/* ====================================================================== │ │ │ │ OpenLayers/BaseTypes.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ @@ -5083,14 +5083,697 @@ │ │ │ │ return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); │ │ │ │ }, │ │ │ │ _hasSingleTextChild: function(el) { │ │ │ │ return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; │ │ │ │ } │ │ │ │ }; │ │ │ │ /* ====================================================================== │ │ │ │ + OpenLayers/Spherical.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/SingleFile.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Namespace: Spherical │ │ │ │ + * The OpenLayers.Spherical namespace includes utility functions for │ │ │ │ + * calculations on the basis of a spherical earth (ignoring ellipsoidal │ │ │ │ + * effects), which is accurate enough for most purposes. │ │ │ │ + * │ │ │ │ + * Relevant links: │ │ │ │ + * * http://www.movable-type.co.uk/scripts/latlong.html │ │ │ │ + * * http://code.google.com/apis/maps/documentation/javascript/reference.html#spherical │ │ │ │ + */ │ │ │ │ + │ │ │ │ +OpenLayers.Spherical = OpenLayers.Spherical || {}; │ │ │ │ + │ │ │ │ +OpenLayers.Spherical.DEFAULT_RADIUS = 6378137; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * APIFunction: computeDistanceBetween │ │ │ │ + * Computes the distance between two LonLats. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * from - {} 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)); │ │ │ │ +}; │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * 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/Util/vendorPrefix.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/SingleFile.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +OpenLayers.Util = OpenLayers.Util || {}; │ │ │ │ +/** │ │ │ │ + * Namespace: OpenLayers.Util.vendorPrefix │ │ │ │ + * A collection of utility functions to detect vendor prefixed features │ │ │ │ + */ │ │ │ │ +OpenLayers.Util.vendorPrefix = (function() { │ │ │ │ + "use strict"; │ │ │ │ + │ │ │ │ + var VENDOR_PREFIXES = ["", "O", "ms", "Moz", "Webkit"], │ │ │ │ + divStyle = document.createElement("div").style, │ │ │ │ + cssCache = {}, │ │ │ │ + jsCache = {}; │ │ │ │ + │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: domToCss │ │ │ │ + * Converts a upper camel case DOM style property name to a CSS property │ │ │ │ + * i.e. transformOrigin -> transform-origin │ │ │ │ + * or WebkitTransformOrigin -> -webkit-transform-origin │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * prefixedDom - {String} The property to convert │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The CSS property │ │ │ │ + */ │ │ │ │ + function domToCss(prefixedDom) { │ │ │ │ + if (!prefixedDom) { │ │ │ │ + return null; │ │ │ │ + } │ │ │ │ + return prefixedDom. │ │ │ │ + replace(/([A-Z])/g, function(c) { │ │ │ │ + return "-" + c.toLowerCase(); │ │ │ │ + }). │ │ │ │ + replace(/^ms-/, "-ms-"); │ │ │ │ + } │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: css │ │ │ │ + * Detect which property is used for a CSS property │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * property - {String} The standard (unprefixed) CSS property name │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The standard CSS property, prefixed property or null if not │ │ │ │ + * supported │ │ │ │ + */ │ │ │ │ + function css(property) { │ │ │ │ + if (cssCache[property] === undefined) { │ │ │ │ + var domProperty = property. │ │ │ │ + replace(/(-[\s\S])/g, function(c) { │ │ │ │ + return c.charAt(1).toUpperCase(); │ │ │ │ + }); │ │ │ │ + var prefixedDom = style(domProperty); │ │ │ │ + cssCache[property] = domToCss(prefixedDom); │ │ │ │ + } │ │ │ │ + return cssCache[property]; │ │ │ │ + } │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: js │ │ │ │ + * Detect which property is used for a JS property/method │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * obj - {Object} The object to test on │ │ │ │ + * property - {String} The standard (unprefixed) JS property name │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The standard JS property, prefixed property or null if not │ │ │ │ + * supported │ │ │ │ + */ │ │ │ │ + function js(obj, property) { │ │ │ │ + if (jsCache[property] === undefined) { │ │ │ │ + var tmpProp, │ │ │ │ + i = 0, │ │ │ │ + l = VENDOR_PREFIXES.length, │ │ │ │ + prefix, │ │ │ │ + isStyleObj = (typeof obj.cssText !== "undefined"); │ │ │ │ + │ │ │ │ + jsCache[property] = null; │ │ │ │ + for (; i < l; i++) { │ │ │ │ + prefix = VENDOR_PREFIXES[i]; │ │ │ │ + if (prefix) { │ │ │ │ + if (!isStyleObj) { │ │ │ │ + // js prefix should be lower-case, while style │ │ │ │ + // properties have upper case on first character │ │ │ │ + prefix = prefix.toLowerCase(); │ │ │ │ + } │ │ │ │ + tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1); │ │ │ │ + } else { │ │ │ │ + tmpProp = property; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (obj[tmpProp] !== undefined) { │ │ │ │ + jsCache[property] = tmpProp; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return jsCache[property]; │ │ │ │ + } │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: style │ │ │ │ + * Detect which property is used for a DOM style property │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * property - {String} The standard (unprefixed) style property name │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The standard style property, prefixed property or null if not │ │ │ │ + * supported │ │ │ │ + */ │ │ │ │ + function style(property) { │ │ │ │ + return js(divStyle, property); │ │ │ │ + } │ │ │ │ + │ │ │ │ + return { │ │ │ │ + css: css, │ │ │ │ + js: js, │ │ │ │ + style: style, │ │ │ │ + │ │ │ │ + // used for testing │ │ │ │ + cssCache: cssCache, │ │ │ │ + jsCache: jsCache │ │ │ │ + }; │ │ │ │ +}()); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Animation.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/SingleFile.js │ │ │ │ + * @requires OpenLayers/Util/vendorPrefix.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Namespace: OpenLayers.Animation │ │ │ │ + * A collection of utility functions for executing methods that repaint a │ │ │ │ + * portion of the browser window. These methods take advantage of the │ │ │ │ + * browser's scheduled repaints where requestAnimationFrame is available. │ │ │ │ + */ │ │ │ │ +OpenLayers.Animation = (function(window) { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: isNative │ │ │ │ + * {Boolean} true if a native requestAnimationFrame function is available │ │ │ │ + */ │ │ │ │ + var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame"); │ │ │ │ + var isNative = !!(requestAnimationFrame); │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: requestFrame │ │ │ │ + * Schedule a function to be called at the next available animation frame. │ │ │ │ + * Uses the native method where available. Where requestAnimationFrame is │ │ │ │ + * not available, setTimeout will be called with a 16ms delay. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * callback - {Function} The function to be called at the next animation frame. │ │ │ │ + * element - {DOMElement} Optional element that visually bounds the animation. │ │ │ │ + */ │ │ │ │ + var requestFrame = (function() { │ │ │ │ + var request = window[requestAnimationFrame] || │ │ │ │ + function(callback, element) { │ │ │ │ + window.setTimeout(callback, 16); │ │ │ │ + }; │ │ │ │ + // bind to window to avoid illegal invocation of native function │ │ │ │ + return function(callback, element) { │ │ │ │ + request.apply(window, [callback, element]); │ │ │ │ + }; │ │ │ │ + })(); │ │ │ │ + │ │ │ │ + // private variables for animation loops │ │ │ │ + var counter = 0; │ │ │ │ + var loops = {}; │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: start │ │ │ │ + * Executes a method with in series for some │ │ │ │ + * duration. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * callback - {Function} The function to be called at the next animation frame. │ │ │ │ + * duration - {Number} Optional duration for the loop. If not provided, the │ │ │ │ + * animation loop will execute indefinitely. │ │ │ │ + * element - {DOMElement} Optional element that visually bounds the animation. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Number} Identifier for the animation loop. Used to stop animations with │ │ │ │ + * . │ │ │ │ + */ │ │ │ │ + function start(callback, duration, element) { │ │ │ │ + duration = duration > 0 ? duration : Number.POSITIVE_INFINITY; │ │ │ │ + var id = ++counter; │ │ │ │ + var start = +new Date; │ │ │ │ + loops[id] = function() { │ │ │ │ + if (loops[id] && +new Date - start <= duration) { │ │ │ │ + callback(); │ │ │ │ + if (loops[id]) { │ │ │ │ + requestFrame(loops[id], element); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + delete loops[id]; │ │ │ │ + } │ │ │ │ + }; │ │ │ │ + requestFrame(loops[id], element); │ │ │ │ + return id; │ │ │ │ + } │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: stop │ │ │ │ + * Terminates an animation loop started with . │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * id - {Number} Identifier returned from . │ │ │ │ + */ │ │ │ │ + function stop(id) { │ │ │ │ + delete loops[id]; │ │ │ │ + } │ │ │ │ + │ │ │ │ + return { │ │ │ │ + isNative: isNative, │ │ │ │ + requestFrame: requestFrame, │ │ │ │ + start: start, │ │ │ │ + stop: stop │ │ │ │ + }; │ │ │ │ + │ │ │ │ +})(window); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Tween.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Animation.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Namespace: OpenLayers.Tween │ │ │ │ + */ │ │ │ │ +OpenLayers.Tween = OpenLayers.Class({ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: easing │ │ │ │ + * {(Function)} Easing equation used for the animation │ │ │ │ + * Defaultly set to OpenLayers.Easing.Expo.easeOut │ │ │ │ + */ │ │ │ │ + easing: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: begin │ │ │ │ + * {Object} Values to start the animation with │ │ │ │ + */ │ │ │ │ + begin: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: finish │ │ │ │ + * {Object} Values to finish the animation with │ │ │ │ + */ │ │ │ │ + finish: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: duration │ │ │ │ + * {int} duration of the tween (number of steps) │ │ │ │ + */ │ │ │ │ + duration: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: callbacks │ │ │ │ + * {Object} An object with start, eachStep and done properties whose values │ │ │ │ + * are functions to be call during the animation. They are passed the │ │ │ │ + * current computed value as argument. │ │ │ │ + */ │ │ │ │ + callbacks: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: time │ │ │ │ + * {int} Step counter │ │ │ │ + */ │ │ │ │ + time: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: minFrameRate │ │ │ │ + * {Number} The minimum framerate for animations in frames per second. After │ │ │ │ + * each step, the time spent in the animation is compared to the calculated │ │ │ │ + * time at this frame rate. If the animation runs longer than the calculated │ │ │ │ + * time, the next step is skipped. Default is 30. │ │ │ │ + */ │ │ │ │ + minFrameRate: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: startTime │ │ │ │ + * {Number} The timestamp of the first execution step. Used for skipping │ │ │ │ + * frames │ │ │ │ + */ │ │ │ │ + startTime: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: animationId │ │ │ │ + * {int} Loop id returned by OpenLayers.Animation.start │ │ │ │ + */ │ │ │ │ + animationId: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: playing │ │ │ │ + * {Boolean} Tells if the easing is currently playing │ │ │ │ + */ │ │ │ │ + playing: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Tween │ │ │ │ + * Creates a Tween. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * easing - {(Function)} easing function method to use │ │ │ │ + */ │ │ │ │ + initialize: function(easing) { │ │ │ │ + this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: start │ │ │ │ + * Plays the Tween, and calls the callback method on each step │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * begin - {Object} values to start the animation with │ │ │ │ + * finish - {Object} values to finish the animation with │ │ │ │ + * duration - {int} duration of the tween (number of steps) │ │ │ │ + * options - {Object} hash of options (callbacks (start, eachStep, done), │ │ │ │ + * minFrameRate) │ │ │ │ + */ │ │ │ │ + start: function(begin, finish, duration, options) { │ │ │ │ + this.playing = true; │ │ │ │ + this.begin = begin; │ │ │ │ + this.finish = finish; │ │ │ │ + this.duration = duration; │ │ │ │ + this.callbacks = options.callbacks; │ │ │ │ + this.minFrameRate = options.minFrameRate || 30; │ │ │ │ + this.time = 0; │ │ │ │ + this.startTime = new Date().getTime(); │ │ │ │ + OpenLayers.Animation.stop(this.animationId); │ │ │ │ + this.animationId = null; │ │ │ │ + if (this.callbacks && this.callbacks.start) { │ │ │ │ + this.callbacks.start.call(this, this.begin); │ │ │ │ + } │ │ │ │ + this.animationId = OpenLayers.Animation.start( │ │ │ │ + OpenLayers.Function.bind(this.play, this) │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: stop │ │ │ │ + * Stops the Tween, and calls the done callback │ │ │ │ + * Doesn't do anything if animation is already finished │ │ │ │ + */ │ │ │ │ + stop: function() { │ │ │ │ + if (!this.playing) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.callbacks && this.callbacks.done) { │ │ │ │ + this.callbacks.done.call(this, this.finish); │ │ │ │ + } │ │ │ │ + OpenLayers.Animation.stop(this.animationId); │ │ │ │ + this.animationId = null; │ │ │ │ + this.playing = false; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: play │ │ │ │ + * Calls the appropriate easing method │ │ │ │ + */ │ │ │ │ + play: function() { │ │ │ │ + var value = {}; │ │ │ │ + for (var i in this.begin) { │ │ │ │ + var b = this.begin[i]; │ │ │ │ + var f = this.finish[i]; │ │ │ │ + if (b == null || f == null || isNaN(b) || isNaN(f)) { │ │ │ │ + throw new TypeError('invalid value for Tween'); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var c = f - b; │ │ │ │ + value[i] = this.easing.apply(this, [this.time, b, c, this.duration]); │ │ │ │ + } │ │ │ │ + this.time++; │ │ │ │ + │ │ │ │ + if (this.callbacks && this.callbacks.eachStep) { │ │ │ │ + // skip frames if frame rate drops below threshold │ │ │ │ + if ((new Date().getTime() - this.startTime) / this.time <= 1000 / this.minFrameRate) { │ │ │ │ + this.callbacks.eachStep.call(this, value); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.time > this.duration) { │ │ │ │ + this.stop(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Create empty functions for all easing methods. │ │ │ │ + */ │ │ │ │ + CLASS_NAME: "OpenLayers.Tween" │ │ │ │ +}); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Namespace: OpenLayers.Easing │ │ │ │ + * │ │ │ │ + * Credits: │ │ │ │ + * Easing Equations by Robert Penner, │ │ │ │ + */ │ │ │ │ +OpenLayers.Easing = { │ │ │ │ + /** │ │ │ │ + * Create empty functions for all easing methods. │ │ │ │ + */ │ │ │ │ + CLASS_NAME: "OpenLayers.Easing" │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Namespace: OpenLayers.Easing.Linear │ │ │ │ + */ │ │ │ │ +OpenLayers.Easing.Linear = { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: easeIn │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * t - {Float} time │ │ │ │ + * b - {Float} beginning position │ │ │ │ + * c - {Float} total change │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} │ │ │ │ + */ │ │ │ │ + easeIn: function(t, b, c, d) { │ │ │ │ + return c * t / d + b; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: easeOut │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * t - {Float} time │ │ │ │ + * b - {Float} beginning position │ │ │ │ + * c - {Float} total change │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} │ │ │ │ + */ │ │ │ │ + easeOut: function(t, b, c, d) { │ │ │ │ + return c * t / d + b; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: easeInOut │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * t - {Float} time │ │ │ │ + * b - {Float} beginning position │ │ │ │ + * c - {Float} total change │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} │ │ │ │ + */ │ │ │ │ + easeInOut: function(t, b, c, d) { │ │ │ │ + return c * t / d + b; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Easing.Linear" │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Namespace: OpenLayers.Easing.Expo │ │ │ │ + */ │ │ │ │ +OpenLayers.Easing.Expo = { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: easeIn │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * t - {Float} time │ │ │ │ + * b - {Float} beginning position │ │ │ │ + * c - {Float} total change │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} │ │ │ │ + */ │ │ │ │ + easeIn: function(t, b, c, d) { │ │ │ │ + return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: easeOut │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * t - {Float} time │ │ │ │ + * b - {Float} beginning position │ │ │ │ + * c - {Float} total change │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} │ │ │ │ + */ │ │ │ │ + easeOut: function(t, b, c, d) { │ │ │ │ + return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: easeInOut │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * t - {Float} time │ │ │ │ + * b - {Float} beginning position │ │ │ │ + * c - {Float} total change │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} │ │ │ │ + */ │ │ │ │ + easeInOut: function(t, b, c, d) { │ │ │ │ + if (t == 0) return b; │ │ │ │ + if (t == d) return b + c; │ │ │ │ + if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b; │ │ │ │ + return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Easing.Expo" │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Namespace: OpenLayers.Easing.Quad │ │ │ │ + */ │ │ │ │ +OpenLayers.Easing.Quad = { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: easeIn │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * t - {Float} time │ │ │ │ + * b - {Float} beginning position │ │ │ │ + * c - {Float} total change │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} │ │ │ │ + */ │ │ │ │ + easeIn: function(t, b, c, d) { │ │ │ │ + return c * (t /= d) * t + b; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: easeOut │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * t - {Float} time │ │ │ │ + * b - {Float} beginning position │ │ │ │ + * c - {Float} total change │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} │ │ │ │ + */ │ │ │ │ + easeOut: function(t, b, c, d) { │ │ │ │ + return -c * (t /= d) * (t - 2) + b; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: easeInOut │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * t - {Float} time │ │ │ │ + * b - {Float} beginning position │ │ │ │ + * c - {Float} total change │ │ │ │ + * d - {Float} duration of the transition │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} │ │ │ │ + */ │ │ │ │ + easeInOut: function(t, b, c, d) { │ │ │ │ + if ((t /= d / 2) < 1) return c / 2 * t * t + b; │ │ │ │ + return -c / 2 * ((--t) * (t - 2) - 1) + b; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Easing.Quad" │ │ │ │ +}; │ │ │ │ +/* ====================================================================== │ │ │ │ OpenLayers/Feature.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ @@ -6279,14 +6962,432 @@ │ │ │ │ * {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/StyleMap.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Style.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.StyleMap │ │ │ │ + */ │ │ │ │ +OpenLayers.StyleMap = OpenLayers.Class({ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: styles │ │ │ │ + * {Object} Hash of {}, keyed by names of well known │ │ │ │ + * rendering intents (e.g. "default", "temporary", "select", "delete"). │ │ │ │ + */ │ │ │ │ + styles: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: extendDefault │ │ │ │ + * {Boolean} if true, every render intent will extend the symbolizers │ │ │ │ + * specified for the "default" intent at rendering time. Otherwise, every │ │ │ │ + * rendering intent will be treated as a completely independent style. │ │ │ │ + */ │ │ │ │ + extendDefault: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.StyleMap │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * style - {Object} Optional. Either a style hash, or a style object, or │ │ │ │ + * a hash of style objects (style hashes) keyed by rendering │ │ │ │ + * intent. If just one style hash or style object is passed, │ │ │ │ + * this will be used for all known render intents (default, │ │ │ │ + * select, temporary) │ │ │ │ + * options - {Object} optional hash of additional options for this │ │ │ │ + * instance │ │ │ │ + */ │ │ │ │ + initialize: function(style, options) { │ │ │ │ + this.styles = { │ │ │ │ + "default": new OpenLayers.Style( │ │ │ │ + OpenLayers.Feature.Vector.style["default"]), │ │ │ │ + "select": new OpenLayers.Style( │ │ │ │ + OpenLayers.Feature.Vector.style["select"]), │ │ │ │ + "temporary": new OpenLayers.Style( │ │ │ │ + OpenLayers.Feature.Vector.style["temporary"]), │ │ │ │ + "delete": new OpenLayers.Style( │ │ │ │ + OpenLayers.Feature.Vector.style["delete"]) │ │ │ │ + }; │ │ │ │ + │ │ │ │ + // take whatever the user passed as style parameter and convert it │ │ │ │ + // into parts of stylemap. │ │ │ │ + if (style instanceof OpenLayers.Style) { │ │ │ │ + // user passed a style object │ │ │ │ + this.styles["default"] = style; │ │ │ │ + this.styles["select"] = style; │ │ │ │ + this.styles["temporary"] = style; │ │ │ │ + this.styles["delete"] = style; │ │ │ │ + } else if (typeof style == "object") { │ │ │ │ + for (var key in style) { │ │ │ │ + if (style[key] instanceof OpenLayers.Style) { │ │ │ │ + // user passed a hash of style objects │ │ │ │ + this.styles[key] = style[key]; │ │ │ │ + } else if (typeof style[key] == "object") { │ │ │ │ + // user passsed a hash of style hashes │ │ │ │ + this.styles[key] = new OpenLayers.Style(style[key]); │ │ │ │ + } else { │ │ │ │ + // user passed a style hash (i.e. symbolizer) │ │ │ │ + this.styles["default"] = new OpenLayers.Style(style); │ │ │ │ + this.styles["select"] = new OpenLayers.Style(style); │ │ │ │ + this.styles["temporary"] = new OpenLayers.Style(style); │ │ │ │ + this.styles["delete"] = new OpenLayers.Style(style); │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + for (var key in this.styles) { │ │ │ │ + this.styles[key].destroy(); │ │ │ │ + } │ │ │ │ + this.styles = null; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: createSymbolizer │ │ │ │ + * Creates the symbolizer for a feature for a render intent. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {} The feature to evaluate the rules │ │ │ │ + * of the intended style against. │ │ │ │ + * intent - {String} The intent determines the symbolizer that will be │ │ │ │ + * used to draw the feature. Well known intents are "default" │ │ │ │ + * (for just drawing the features), "select" (for selected │ │ │ │ + * features) and "temporary" (for drawing features). │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} symbolizer hash │ │ │ │ + */ │ │ │ │ + createSymbolizer: function(feature, intent) { │ │ │ │ + if (!feature) { │ │ │ │ + feature = new OpenLayers.Feature.Vector(); │ │ │ │ + } │ │ │ │ + if (!this.styles[intent]) { │ │ │ │ + intent = "default"; │ │ │ │ + } │ │ │ │ + feature.renderIntent = intent; │ │ │ │ + var defaultSymbolizer = {}; │ │ │ │ + if (this.extendDefault && intent != "default") { │ │ │ │ + defaultSymbolizer = this.styles["default"].createSymbolizer(feature); │ │ │ │ + } │ │ │ │ + return OpenLayers.Util.extend(defaultSymbolizer, │ │ │ │ + this.styles[intent].createSymbolizer(feature)); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: addUniqueValueRules │ │ │ │ + * Convenience method to create comparison rules for unique values of a │ │ │ │ + * property. The rules will be added to the style object for a specified │ │ │ │ + * rendering intent. This method is a shortcut for creating something like │ │ │ │ + * the "unique value legends" familiar from well known desktop GIS systems │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * renderIntent - {String} rendering intent to add the rules to │ │ │ │ + * property - {String} values of feature attributes to create the │ │ │ │ + * rules for │ │ │ │ + * symbolizers - {Object} Hash of symbolizers, keyed by the desired │ │ │ │ + * property values │ │ │ │ + * context - {Object} An optional object with properties that │ │ │ │ + * symbolizers' property values should be evaluated │ │ │ │ + * against. If no context is specified, feature.attributes │ │ │ │ + * will be used │ │ │ │ + */ │ │ │ │ + addUniqueValueRules: function(renderIntent, property, symbolizers, context) { │ │ │ │ + var rules = []; │ │ │ │ + for (var value in symbolizers) { │ │ │ │ + rules.push(new OpenLayers.Rule({ │ │ │ │ + symbolizer: symbolizers[value], │ │ │ │ + context: context, │ │ │ │ + filter: new OpenLayers.Filter.Comparison({ │ │ │ │ + type: OpenLayers.Filter.Comparison.EQUAL_TO, │ │ │ │ + property: property, │ │ │ │ + value: value │ │ │ │ + }) │ │ │ │ + })); │ │ │ │ + } │ │ │ │ + this.styles[renderIntent].addRules(rules); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.StyleMap" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/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/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. */ │ │ │ │ @@ -6519,625 +7620,721 @@ │ │ │ │ options.context = this.context && OpenLayers.Util.extend({}, this.context); │ │ │ │ return new OpenLayers.Rule(options); │ │ │ │ }, │ │ │ │ │ │ │ │ CLASS_NAME: "OpenLayers.Rule" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Util/vendorPrefix.js │ │ │ │ + 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/SingleFile.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ */ │ │ │ │ │ │ │ │ -OpenLayers.Util = OpenLayers.Util || {}; │ │ │ │ /** │ │ │ │ - * Namespace: OpenLayers.Util.vendorPrefix │ │ │ │ - * A collection of utility functions to detect vendor prefixed features │ │ │ │ + * Class: OpenLayers.Symbolizer │ │ │ │ + * Base class representing a symbolizer used for feature rendering. │ │ │ │ */ │ │ │ │ -OpenLayers.Util.vendorPrefix = (function() { │ │ │ │ - "use strict"; │ │ │ │ - │ │ │ │ - var VENDOR_PREFIXES = ["", "O", "ms", "Moz", "Webkit"], │ │ │ │ - divStyle = document.createElement("div").style, │ │ │ │ - cssCache = {}, │ │ │ │ - jsCache = {}; │ │ │ │ +OpenLayers.Symbolizer = OpenLayers.Class({ │ │ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: domToCss │ │ │ │ - * Converts a upper camel case DOM style property name to a CSS property │ │ │ │ - * i.e. transformOrigin -> transform-origin │ │ │ │ - * or WebkitTransformOrigin -> -webkit-transform-origin │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * prefixedDom - {String} The property to convert │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The CSS property │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - function domToCss(prefixedDom) { │ │ │ │ - if (!prefixedDom) { │ │ │ │ - return null; │ │ │ │ - } │ │ │ │ - return prefixedDom. │ │ │ │ - replace(/([A-Z])/g, function(c) { │ │ │ │ - return "-" + c.toLowerCase(); │ │ │ │ - }). │ │ │ │ - replace(/^ms-/, "-ms-"); │ │ │ │ - } │ │ │ │ + zIndex: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: css │ │ │ │ - * Detect which property is used for a CSS property │ │ │ │ + * Constructor: OpenLayers.Symbolizer │ │ │ │ + * Instances of this class are not useful. See one of the subclasses. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * property - {String} The standard (unprefixed) CSS property name │ │ │ │ + * config - {Object} An object containing properties to be set on the │ │ │ │ + * symbolizer. Any documented symbolizer property can be set at │ │ │ │ + * construction. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} The standard CSS property, prefixed property or null if not │ │ │ │ - * supported │ │ │ │ + * A new symbolizer. │ │ │ │ */ │ │ │ │ - function css(property) { │ │ │ │ - if (cssCache[property] === undefined) { │ │ │ │ - var domProperty = property. │ │ │ │ - replace(/(-[\s\S])/g, function(c) { │ │ │ │ - return c.charAt(1).toUpperCase(); │ │ │ │ - }); │ │ │ │ - var prefixedDom = style(domProperty); │ │ │ │ - cssCache[property] = domToCss(prefixedDom); │ │ │ │ - } │ │ │ │ - return cssCache[property]; │ │ │ │ - } │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Util.extend(this, config); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: js │ │ │ │ - * Detect which property is used for a JS property/method │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {Object} The object to test on │ │ │ │ - * property - {String} The standard (unprefixed) JS property name │ │ │ │ + /** │ │ │ │ + * APIMethod: clone │ │ │ │ + * Create a copy of this symbolizer. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {String} The standard JS property, prefixed property or null if not │ │ │ │ - * supported │ │ │ │ + * Returns a symbolizer of the same type with the same properties. │ │ │ │ */ │ │ │ │ - function js(obj, property) { │ │ │ │ - if (jsCache[property] === undefined) { │ │ │ │ - var tmpProp, │ │ │ │ - i = 0, │ │ │ │ - l = VENDOR_PREFIXES.length, │ │ │ │ - prefix, │ │ │ │ - isStyleObj = (typeof obj.cssText !== "undefined"); │ │ │ │ - │ │ │ │ - jsCache[property] = null; │ │ │ │ - for (; i < l; i++) { │ │ │ │ - prefix = VENDOR_PREFIXES[i]; │ │ │ │ - if (prefix) { │ │ │ │ - if (!isStyleObj) { │ │ │ │ - // js prefix should be lower-case, while style │ │ │ │ - // properties have upper case on first character │ │ │ │ - prefix = prefix.toLowerCase(); │ │ │ │ - } │ │ │ │ - tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1); │ │ │ │ - } else { │ │ │ │ - tmpProp = property; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (obj[tmpProp] !== undefined) { │ │ │ │ - jsCache[property] = tmpProp; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return jsCache[property]; │ │ │ │ - } │ │ │ │ + clone: function() { │ │ │ │ + var Type = eval(this.CLASS_NAME); │ │ │ │ + return new Type(OpenLayers.Util.extend({}, this)); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: style │ │ │ │ - * Detect which property is used for a DOM style property │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * property - {String} The standard (unprefixed) style property name │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The standard style property, prefixed property or null if not │ │ │ │ - * supported │ │ │ │ - */ │ │ │ │ - function style(property) { │ │ │ │ - return js(divStyle, property); │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer" │ │ │ │ │ │ │ │ - return { │ │ │ │ - css: css, │ │ │ │ - js: js, │ │ │ │ - style: style, │ │ │ │ +}); │ │ │ │ │ │ │ │ - // used for testing │ │ │ │ - cssCache: cssCache, │ │ │ │ - jsCache: jsCache │ │ │ │ - }; │ │ │ │ -}()); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Animation.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/SingleFile.js │ │ │ │ - * @requires OpenLayers/Util/vendorPrefix.js │ │ │ │ + * @requires OpenLayers/Symbolizer.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Namespace: OpenLayers.Animation │ │ │ │ - * A collection of utility functions for executing methods that repaint a │ │ │ │ - * portion of the browser window. These methods take advantage of the │ │ │ │ - * browser's scheduled repaints where requestAnimationFrame is available. │ │ │ │ + * Class: OpenLayers.Symbolizer.Point │ │ │ │ + * A symbolizer used to render point features. │ │ │ │ */ │ │ │ │ -OpenLayers.Animation = (function(window) { │ │ │ │ +OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: isNative │ │ │ │ - * {Boolean} true if a native requestAnimationFrame function is available │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame"); │ │ │ │ - var isNative = !!(requestAnimationFrame); │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: requestFrame │ │ │ │ - * Schedule a function to be called at the next available animation frame. │ │ │ │ - * Uses the native method where available. Where requestAnimationFrame is │ │ │ │ - * not available, setTimeout will be called with a 16ms delay. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * callback - {Function} The function to be called at the next animation frame. │ │ │ │ - * element - {DOMElement} Optional element that visually bounds the animation. │ │ │ │ + * APIProperty: strokeOpacity │ │ │ │ + * {Number} Stroke opacity (0-1). │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ */ │ │ │ │ - var requestFrame = (function() { │ │ │ │ - var request = window[requestAnimationFrame] || │ │ │ │ - function(callback, element) { │ │ │ │ - window.setTimeout(callback, 16); │ │ │ │ - }; │ │ │ │ - // bind to window to avoid illegal invocation of native function │ │ │ │ - return function(callback, element) { │ │ │ │ - request.apply(window, [callback, element]); │ │ │ │ - }; │ │ │ │ - })(); │ │ │ │ │ │ │ │ - // private variables for animation loops │ │ │ │ - var counter = 0; │ │ │ │ - var loops = {}; │ │ │ │ + /** │ │ │ │ + * APIProperty: strokeWidth │ │ │ │ + * {Number} Pixel stroke width. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: start │ │ │ │ - * Executes a method with in series for some │ │ │ │ - * duration. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * callback - {Function} The function to be called at the next animation frame. │ │ │ │ - * duration - {Number} Optional duration for the loop. If not provided, the │ │ │ │ - * animation loop will execute indefinitely. │ │ │ │ - * element - {DOMElement} Optional element that visually bounds the animation. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Number} Identifier for the animation loop. Used to stop animations with │ │ │ │ - * . │ │ │ │ + * APIProperty: strokeLinecap │ │ │ │ + * {String} Stroke cap type ("butt", "round", or "square"). │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ */ │ │ │ │ - function start(callback, duration, element) { │ │ │ │ - duration = duration > 0 ? duration : Number.POSITIVE_INFINITY; │ │ │ │ - var id = ++counter; │ │ │ │ - var start = +new Date; │ │ │ │ - loops[id] = function() { │ │ │ │ - if (loops[id] && +new Date - start <= duration) { │ │ │ │ - callback(); │ │ │ │ - if (loops[id]) { │ │ │ │ - requestFrame(loops[id], element); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - delete loops[id]; │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - requestFrame(loops[id], element); │ │ │ │ - return id; │ │ │ │ - } │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: stop │ │ │ │ - * Terminates an animation loop started with . │ │ │ │ + * Property: strokeDashstyle │ │ │ │ + * {String} Stroke dash style according to the SLD spec. Note that the │ │ │ │ + * OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot", │ │ │ │ + * "longdash", "longdashdot", or "solid") will not work in SLD, but │ │ │ │ + * most SLD patterns will render correctly in OpenLayers. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: fillColor │ │ │ │ + * {String} RGB hex fill color (e.g. "#ff0000" for red). │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: fillOpacity │ │ │ │ + * {Number} Fill opacity (0-1). │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: pointRadius │ │ │ │ + * {Number} Pixel point radius. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: externalGraphic │ │ │ │ + * {String} Url to an external graphic that will be used for rendering │ │ │ │ + * points. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: graphicWidth │ │ │ │ + * {Number} Pixel width for sizing an external graphic. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: graphicHeight │ │ │ │ + * {Number} Pixel height for sizing an external graphic. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: graphicOpacity │ │ │ │ + * {Number} Opacity (0-1) for an external graphic. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: graphicXOffset │ │ │ │ + * {Number} Pixel offset along the positive x axis for displacing an │ │ │ │ + * external graphic. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: graphicYOffset │ │ │ │ + * {Number} Pixel offset along the positive y axis for displacing an │ │ │ │ + * external graphic. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: rotation │ │ │ │ + * {Number} The rotation of a graphic in the clockwise direction about its │ │ │ │ + * center point (or any point off center as specified by │ │ │ │ + * 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: │ │ │ │ - * id - {Number} Identifier returned from . │ │ │ │ + * config - {Object} An object containing properties to be set on the │ │ │ │ + * symbolizer. Any documented symbolizer property can be set at │ │ │ │ + * construction. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * A new point symbolizer. │ │ │ │ */ │ │ │ │ - function stop(id) { │ │ │ │ - delete loops[id]; │ │ │ │ - } │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - return { │ │ │ │ - isNative: isNative, │ │ │ │ - requestFrame: requestFrame, │ │ │ │ - start: start, │ │ │ │ - stop: stop │ │ │ │ - }; │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Point" │ │ │ │ + │ │ │ │ +}); │ │ │ │ │ │ │ │ -})(window); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Tween.js │ │ │ │ + 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/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Animation.js │ │ │ │ + * @requires OpenLayers/Symbolizer.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Namespace: OpenLayers.Tween │ │ │ │ + * Class: OpenLayers.Symbolizer.Line │ │ │ │ + * A symbolizer used to render line features. │ │ │ │ */ │ │ │ │ -OpenLayers.Tween = OpenLayers.Class({ │ │ │ │ +OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: easing │ │ │ │ - * {(Function)} Easing equation used for the animation │ │ │ │ - * Defaultly set to OpenLayers.Easing.Expo.easeOut │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - easing: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: begin │ │ │ │ - * {Object} Values to start the animation with │ │ │ │ + * APIProperty: strokeOpacity │ │ │ │ + * {Number} Stroke opacity (0-1). │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ */ │ │ │ │ - begin: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: finish │ │ │ │ - * {Object} Values to finish the animation with │ │ │ │ + * APIProperty: strokeWidth │ │ │ │ + * {Number} Pixel stroke width. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ */ │ │ │ │ - finish: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: duration │ │ │ │ - * {int} duration of the tween (number of steps) │ │ │ │ + * APIProperty: strokeLinecap │ │ │ │ + * {String} Stroke cap type ("butt", "round", or "square"). │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ */ │ │ │ │ - duration: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: callbacks │ │ │ │ - * {Object} An object with start, eachStep and done properties whose values │ │ │ │ - * are functions to be call during the animation. They are passed the │ │ │ │ - * current computed value as argument. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - callbacks: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: time │ │ │ │ - * {int} Step counter │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - time: null, │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Line" │ │ │ │ + │ │ │ │ +}); │ │ │ │ + │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Symbolizer/Polygon.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Symbolizer.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Symbolizer.Polygon │ │ │ │ + * A symbolizer used to render line features. │ │ │ │ + */ │ │ │ │ +OpenLayers.Symbolizer.Polygon = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: 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. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - minFrameRate: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: startTime │ │ │ │ - * {Number} The timestamp of the first execution step. Used for skipping │ │ │ │ - * frames │ │ │ │ + * APIProperty: strokeOpacity │ │ │ │ + * {Number} Stroke opacity (0-1). │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ */ │ │ │ │ - startTime: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: animationId │ │ │ │ - * {int} Loop id returned by OpenLayers.Animation.start │ │ │ │ + * APIProperty: strokeWidth │ │ │ │ + * {Number} Pixel stroke width. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ */ │ │ │ │ - animationId: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: playing │ │ │ │ - * {Boolean} Tells if the easing is currently playing │ │ │ │ + * APIProperty: strokeLinecap │ │ │ │ + * {String} Stroke cap type ("butt", "round", or "square"). │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ */ │ │ │ │ - playing: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Tween │ │ │ │ - * Creates a Tween. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * easing - {(Function)} easing function method to use │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - initialize: function(easing) { │ │ │ │ - this.easing = (easing) ? easing : OpenLayers.Easing.Expo.easeOut; │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: start │ │ │ │ - * Plays the Tween, and calls the callback method on each step │ │ │ │ + * APIProperty: fillColor │ │ │ │ + * {String} RGB hex fill color (e.g. "#ff0000" for red). │ │ │ │ * │ │ │ │ - * 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) │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ */ │ │ │ │ - start: function(begin, finish, duration, options) { │ │ │ │ - this.playing = true; │ │ │ │ - this.begin = begin; │ │ │ │ - this.finish = finish; │ │ │ │ - this.duration = duration; │ │ │ │ - this.callbacks = options.callbacks; │ │ │ │ - this.minFrameRate = options.minFrameRate || 30; │ │ │ │ - this.time = 0; │ │ │ │ - this.startTime = new Date().getTime(); │ │ │ │ - OpenLayers.Animation.stop(this.animationId); │ │ │ │ - this.animationId = null; │ │ │ │ - if (this.callbacks && this.callbacks.start) { │ │ │ │ - this.callbacks.start.call(this, this.begin); │ │ │ │ - } │ │ │ │ - this.animationId = OpenLayers.Animation.start( │ │ │ │ - OpenLayers.Function.bind(this.play, this) │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: stop │ │ │ │ - * Stops the Tween, and calls the done callback │ │ │ │ - * Doesn't do anything if animation is already finished │ │ │ │ + * APIProperty: fillOpacity │ │ │ │ + * {Number} Fill opacity (0-1). │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ */ │ │ │ │ - stop: function() { │ │ │ │ - if (!this.playing) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.callbacks && this.callbacks.done) { │ │ │ │ - this.callbacks.done.call(this, this.finish); │ │ │ │ - } │ │ │ │ - OpenLayers.Animation.stop(this.animationId); │ │ │ │ - this.animationId = null; │ │ │ │ - this.playing = false; │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: play │ │ │ │ - * Calls the appropriate easing method │ │ │ │ + * Constructor: OpenLayers.Symbolizer.Polygon │ │ │ │ + * Create a symbolizer for rendering polygons. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * config - {Object} An object containing properties to be set on the │ │ │ │ + * symbolizer. Any documented symbolizer property can be set at │ │ │ │ + * construction. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * A new polygon symbolizer. │ │ │ │ */ │ │ │ │ - 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'); │ │ │ │ - } │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var c = f - b; │ │ │ │ - value[i] = this.easing.apply(this, [this.time, b, c, this.duration]); │ │ │ │ - } │ │ │ │ - this.time++; │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.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); │ │ │ │ - } │ │ │ │ - } │ │ │ │ +}); │ │ │ │ │ │ │ │ - if (this.time > this.duration) { │ │ │ │ - this.stop(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Symbolizer/Text.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/Symbolizer.js │ │ │ │ */ │ │ │ │ -OpenLayers.Easing = { │ │ │ │ - /** │ │ │ │ - * Create empty functions for all easing methods. │ │ │ │ - */ │ │ │ │ - CLASS_NAME: "OpenLayers.Easing" │ │ │ │ -}; │ │ │ │ │ │ │ │ /** │ │ │ │ - * Namespace: OpenLayers.Easing.Linear │ │ │ │ + * Class: OpenLayers.Symbolizer.Text │ │ │ │ + * A symbolizer used to render text labels for features. │ │ │ │ */ │ │ │ │ -OpenLayers.Easing.Linear = { │ │ │ │ +OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Function: easeIn │ │ │ │ + /** │ │ │ │ + * APIProperty: label │ │ │ │ + * {String} The text for the label. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * t - {Float} time │ │ │ │ - * b - {Float} beginning position │ │ │ │ - * c - {Float} total change │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Float} │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ */ │ │ │ │ - easeIn: function(t, b, c, d) { │ │ │ │ - return c * t / d + b; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Function: easeOut │ │ │ │ + /** │ │ │ │ + * APIProperty: fontFamily │ │ │ │ + * {String} The font family for the label. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * t - {Float} time │ │ │ │ - * b - {Float} beginning position │ │ │ │ - * c - {Float} total change │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Float} │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: fontSize │ │ │ │ + * {String} The font size for the label. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: fontWeight │ │ │ │ + * {String} The font weight for the label. │ │ │ │ + * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ */ │ │ │ │ - easeOut: function(t, b, c, d) { │ │ │ │ - return c * t / d + b; │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: easeInOut │ │ │ │ + * Property: fontStyle │ │ │ │ + * {String} The font style for the label. │ │ │ │ * │ │ │ │ + * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Symbolizer.Text │ │ │ │ + * Create a symbolizer for rendering text labels. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * t - {Float} time │ │ │ │ - * b - {Float} beginning position │ │ │ │ - * c - {Float} total change │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ + * config - {Object} An object containing properties to be set on the │ │ │ │ + * symbolizer. Any documented symbolizer property can be set at │ │ │ │ + * construction. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Float} │ │ │ │ + * A new text symbolizer. │ │ │ │ */ │ │ │ │ - easeInOut: function(t, b, c, d) { │ │ │ │ - return c * t / d + b; │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Easing.Linear" │ │ │ │ -}; │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Text" │ │ │ │ + │ │ │ │ +}); │ │ │ │ + │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Symbolizer/Raster.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Namespace: OpenLayers.Easing.Expo │ │ │ │ + * @requires OpenLayers/Symbolizer.js │ │ │ │ */ │ │ │ │ -OpenLayers.Easing.Expo = { │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Symbolizer.Raster │ │ │ │ + * A symbolizer used to render raster images. │ │ │ │ + */ │ │ │ │ +OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: easeIn │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Symbolizer.Raster │ │ │ │ + * Create a symbolizer for rendering rasters. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * t - {Float} time │ │ │ │ - * b - {Float} beginning position │ │ │ │ - * c - {Float} total change │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ + * config - {Object} An object containing properties to be set on the │ │ │ │ + * symbolizer. Any documented symbolizer property can be set at │ │ │ │ + * construction. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Float} │ │ │ │ + * A new raster symbolizer. │ │ │ │ */ │ │ │ │ - easeIn: function(t, b, c, d) { │ │ │ │ - return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b; │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Raster" │ │ │ │ + │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Style2.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Rule.js │ │ │ │ + * @requires OpenLayers/Symbolizer/Point.js │ │ │ │ + * @requires OpenLayers/Symbolizer/Line.js │ │ │ │ + * @requires OpenLayers/Symbolizer/Polygon.js │ │ │ │ + * @requires OpenLayers/Symbolizer/Text.js │ │ │ │ + * @requires OpenLayers/Symbolizer/Raster.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Style2 │ │ │ │ + * This class represents a collection of rules for rendering features. │ │ │ │ + */ │ │ │ │ +OpenLayers.Style2 = OpenLayers.Class({ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Function: easeOut │ │ │ │ - * │ │ │ │ + * Property: id │ │ │ │ + * {String} A unique id for this session. │ │ │ │ + */ │ │ │ │ + id: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: name │ │ │ │ + * {String} Style identifier. │ │ │ │ + */ │ │ │ │ + name: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: title │ │ │ │ + * {String} Title of this style. │ │ │ │ + */ │ │ │ │ + title: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: description │ │ │ │ + * {String} Description of this style. │ │ │ │ + */ │ │ │ │ + description: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: layerName │ │ │ │ + * {} 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: │ │ │ │ - * t - {Float} time │ │ │ │ - * b - {Float} beginning position │ │ │ │ - * c - {Float} total change │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ + * config - {Object} An object containing properties to be set on the │ │ │ │ + * style. Any documented properties may be set at construction. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Float} │ │ │ │ + * {} A new style object. │ │ │ │ */ │ │ │ │ - easeOut: function(t, b, c, d) { │ │ │ │ - return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b; │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Util.extend(this, config); │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * nullify references to prevent circular references and memory leaks │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + for (var i = 0, len = this.rules.length; i < len; i++) { │ │ │ │ + this.rules[i].destroy(); │ │ │ │ + } │ │ │ │ + delete this.rules; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: easeInOut │ │ │ │ + * APIMethod: clone │ │ │ │ + * Clones this style. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * t - {Float} time │ │ │ │ - * b - {Float} beginning position │ │ │ │ - * c - {Float} total change │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ - * │ │ │ │ * Returns: │ │ │ │ - * {Float} │ │ │ │ + * {} Clone of this style. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + clone: function() { │ │ │ │ + var config = OpenLayers.Util.extend({}, this); │ │ │ │ + // clone rules │ │ │ │ + if (this.rules) { │ │ │ │ + config.rules = []; │ │ │ │ + for (var i = 0, len = this.rules.length; i < len; ++i) { │ │ │ │ + config.rules.push(this.rules[i].clone()); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return new OpenLayers.Style2(config); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Easing.Expo" │ │ │ │ -}; │ │ │ │ + CLASS_NAME: "OpenLayers.Style2" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + 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. */ │ │ │ │ + │ │ │ │ │ │ │ │ /** │ │ │ │ - * Namespace: OpenLayers.Easing.Quad │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ + * @requires OpenLayers/Style.js │ │ │ │ */ │ │ │ │ -OpenLayers.Easing.Quad = { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Function: easeIn │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * t - {Float} time │ │ │ │ - * b - {Float} beginning position │ │ │ │ - * c - {Float} total change │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Filter │ │ │ │ + * This class represents an OGC Filter. │ │ │ │ + */ │ │ │ │ +OpenLayers.Filter = OpenLayers.Class({ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Filter │ │ │ │ + * This class represents a generic filter. │ │ │ │ * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Float} │ │ │ │ + * {} │ │ │ │ */ │ │ │ │ - easeIn: function(t, b, c, d) { │ │ │ │ - return c * (t /= d) * t + b; │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Remove reference to anything added. │ │ │ │ + */ │ │ │ │ + destroy: function() {}, │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Function: easeOut │ │ │ │ + * APIMethod: evaluate │ │ │ │ + * Evaluates this filter in a specific context. Instances or subclasses │ │ │ │ + * are supposed to override this method. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * t - {Float} time │ │ │ │ - * b - {Float} beginning position │ │ │ │ - * c - {Float} total change │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ - * │ │ │ │ + * context - {Object} Context to use in evaluating the filter. If a vector │ │ │ │ + * feature is provided, the feature.attributes will be used as context. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Float} │ │ │ │ + * {Boolean} The filter applies. │ │ │ │ */ │ │ │ │ - easeOut: function(t, b, c, d) { │ │ │ │ - return -c * (t /= d) * (t - 2) + b; │ │ │ │ + evaluate: function(context) { │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: easeInOut │ │ │ │ + * APIMethod: clone │ │ │ │ + * Clones this filter. Should be implemented by subclasses. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * t - {Float} time │ │ │ │ - * b - {Float} beginning position │ │ │ │ - * c - {Float} total change │ │ │ │ - * d - {Float} duration of the transition │ │ │ │ + * Returns: │ │ │ │ + * {} Clone of this filter. │ │ │ │ + */ │ │ │ │ + clone: function() { │ │ │ │ + return null; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: toString │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Float} │ │ │ │ + * {String} Include in your build to get a CQL │ │ │ │ + * representation of the filter returned. Otherwise "[Object object]" │ │ │ │ + * will be returned. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + toString: function() { │ │ │ │ + var string; │ │ │ │ + if (OpenLayers.Format && OpenLayers.Format.CQL) { │ │ │ │ + string = OpenLayers.Format.CQL.prototype.write(this); │ │ │ │ + } else { │ │ │ │ + string = Object.prototype.toString.call(this); │ │ │ │ + } │ │ │ │ + return string; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Easing.Quad" │ │ │ │ -}; │ │ │ │ + CLASS_NAME: "OpenLayers.Filter" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ 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 │ │ │ │ @@ -12933,249 +14130,553 @@ │ │ │ │ } │ │ │ │ return bounds; │ │ │ │ }, │ │ │ │ │ │ │ │ CLASS_NAME: "OpenLayers.Layer" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/HTTPRequest.js │ │ │ │ + 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/Layer.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Events.js │ │ │ │ + * @requires OpenLayers/Icon.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.HTTPRequest │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - │ │ │ │ + * 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.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ +OpenLayers.Marker = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Property: icon │ │ │ │ + * {} The icon used by this marker. │ │ │ │ */ │ │ │ │ - URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2, │ │ │ │ + icon: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: url │ │ │ │ - * {Array(String) or String} This is either an array of url strings or │ │ │ │ - * a single url string. │ │ │ │ + * Property: lonlat │ │ │ │ + * {} location of object │ │ │ │ */ │ │ │ │ - url: null, │ │ │ │ + lonlat: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: params │ │ │ │ - * {Object} Hashtable of key/value parameters │ │ │ │ + * Property: events │ │ │ │ + * {} the event handler. │ │ │ │ */ │ │ │ │ - params: null, │ │ │ │ + events: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: reproject │ │ │ │ - * *Deprecated*. See http://docs.openlayers.org/library/spherical_mercator.html │ │ │ │ - * for information on the replacement for this functionality. │ │ │ │ - * {Boolean} Whether layer should reproject itself based on base layer │ │ │ │ - * locations. This allows reprojection onto commercial layers. │ │ │ │ - * Default is false: Most layers can't reproject, but layers │ │ │ │ - * which can create non-square geographic pixels can, like WMS. │ │ │ │ - * │ │ │ │ + * Property: map │ │ │ │ + * {} the map this marker is attached to │ │ │ │ */ │ │ │ │ - reproject: false, │ │ │ │ + map: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Layer.HTTPRequest │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Marker │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * url - {Array(String) or String} │ │ │ │ - * params - {Object} │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * lonlat - {} the position of this marker │ │ │ │ + * icon - {} the icon for this marker │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + initialize: function(lonlat, icon) { │ │ │ │ + this.lonlat = lonlat; │ │ │ │ + │ │ │ │ + var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon(); │ │ │ │ + if (this.icon == null) { │ │ │ │ + this.icon = newIcon; │ │ │ │ + } else { │ │ │ │ + this.icon.url = newIcon.url; │ │ │ │ + this.icon.size = newIcon.size; │ │ │ │ + this.icon.offset = newIcon.offset; │ │ │ │ + this.icon.calculateOffset = newIcon.calculateOffset; │ │ │ │ } │ │ │ │ + this.events = new OpenLayers.Events(this, this.icon.imageDiv); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ * APIMethod: destroy │ │ │ │ + * Destroy the marker. You must first remove the marker from any │ │ │ │ + * layer which it has been added to, or you will get buggy behavior. │ │ │ │ + * (This can not be done within the marker since the marker does not │ │ │ │ + * know which layer it is attached to.) │ │ │ │ */ │ │ │ │ destroy: function() { │ │ │ │ - this.url = null; │ │ │ │ - this.params = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ + // 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; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: clone │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * Calls draw on the icon, and returns that output. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * obj - {Object} │ │ │ │ + * px - {} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {} An exact clone of this │ │ │ │ - * │ │ │ │ + * {DOMElement} A new DOM Image with this marker's icon set at the │ │ │ │ + * location passed-in │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ + draw: function(px) { │ │ │ │ + return this.icon.draw(px); │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.HTTPRequest(this.name, │ │ │ │ - this.url, │ │ │ │ - this.params, │ │ │ │ - this.getOptions()); │ │ │ │ + /** │ │ │ │ + * Method: erase │ │ │ │ + * Erases any drawn elements for this marker. │ │ │ │ + */ │ │ │ │ + erase: function() { │ │ │ │ + if (this.icon != null) { │ │ │ │ + this.icon.erase(); │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ + /** │ │ │ │ + * Method: moveTo │ │ │ │ + * Move the marker to the new location. │ │ │ │ + * │ │ │ │ + * 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 ((px != null) && (this.icon != null)) { │ │ │ │ + this.icon.moveTo(px); │ │ │ │ + } │ │ │ │ + this.lonlat = this.map.getLonLatFromLayerPx(px); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ + /** │ │ │ │ + * APIMethod: isDrawn │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Whether or not the marker is drawn. │ │ │ │ + */ │ │ │ │ + isDrawn: function() { │ │ │ │ + var isDrawn = (this.icon && this.icon.isDrawn()); │ │ │ │ + return isDrawn; │ │ │ │ + }, │ │ │ │ │ │ │ │ - return obj; │ │ │ │ + /** │ │ │ │ + * Method: onScreen │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Whether or not the marker is currently visible on screen. │ │ │ │ + */ │ │ │ │ + onScreen: function() { │ │ │ │ + │ │ │ │ + var onScreen = false; │ │ │ │ + if (this.map) { │ │ │ │ + var screenBounds = this.map.getExtent(); │ │ │ │ + onScreen = screenBounds.containsLonLat(this.lonlat); │ │ │ │ + } │ │ │ │ + return onScreen; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: inflate │ │ │ │ + * Englarges the markers icon by the specified ratio. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * inflate - {float} the ratio to enlarge the marker by (passing 2 │ │ │ │ + * will double the size). │ │ │ │ + */ │ │ │ │ + inflate: function(inflate) { │ │ │ │ + if (this.icon) { │ │ │ │ + this.icon.setSize({ │ │ │ │ + w: this.icon.size.w * inflate, │ │ │ │ + h: this.icon.size.h * inflate │ │ │ │ + }); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setUrl │ │ │ │ + * Method: setOpacity │ │ │ │ + * Change the opacity of the marker by changin the opacity of │ │ │ │ + * its icon │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * newUrl - {String} │ │ │ │ + * opacity - {float} Specified as fraction (0.4, etc) │ │ │ │ */ │ │ │ │ - setUrl: function(newUrl) { │ │ │ │ - this.url = newUrl; │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ + this.icon.setOpacity(opacity); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: mergeNewParams │ │ │ │ + * Method: setUrl │ │ │ │ + * Change URL of the Icon Image. │ │ │ │ + * │ │ │ │ + * url - {String} │ │ │ │ + */ │ │ │ │ + setUrl: function(url) { │ │ │ │ + this.icon.setUrl(url); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: display │ │ │ │ + * Hide or show the icon │ │ │ │ * │ │ │ │ + * display - {Boolean} │ │ │ │ + */ │ │ │ │ + display: function(display) { │ │ │ │ + this.icon.display(display); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Marker" │ │ │ │ +}); │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Function: defaultIcon │ │ │ │ + * Creates a default . │ │ │ │ + * │ │ │ │ + * 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/Protocol.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Protocol │ │ │ │ + * Abstract vector layer protocol class. Not to be instantiated directly. Use │ │ │ │ + * one of the protocol subclasses instead. │ │ │ │ + */ │ │ │ │ +OpenLayers.Protocol = OpenLayers.Class({ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: format │ │ │ │ + * {} The format used by this protocol. │ │ │ │ + */ │ │ │ │ + format: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: options │ │ │ │ + * {Object} Any options sent to the constructor. │ │ │ │ + */ │ │ │ │ + options: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: autoDestroy │ │ │ │ + * {Boolean} The creator of the protocol can set autoDestroy to false │ │ │ │ + * to fully control when the protocol is destroyed. Defaults to │ │ │ │ + * true. │ │ │ │ + */ │ │ │ │ + autoDestroy: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: defaultFilter │ │ │ │ + * {} Optional default filter to read requests │ │ │ │ + */ │ │ │ │ + defaultFilter: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Protocol │ │ │ │ + * Abstract class for vector protocols. Create instances of a subclass. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * newParams - {Object} │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.options = options; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: mergeWithDefaultFilter │ │ │ │ + * Merge filter passed to the read method with the default one │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * redrawn: {Boolean} whether the layer was actually redrawn. │ │ │ │ + * Parameters: │ │ │ │ + * filter - {} │ │ │ │ */ │ │ │ │ - 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" │ │ │ │ + mergeWithDefaultFilter: function(filter) { │ │ │ │ + var merged; │ │ │ │ + if (filter && this.defaultFilter) { │ │ │ │ + merged = new OpenLayers.Filter.Logical({ │ │ │ │ + type: OpenLayers.Filter.Logical.AND, │ │ │ │ + filters: [this.defaultFilter, filter] │ │ │ │ }); │ │ │ │ + } else { │ │ │ │ + merged = filter || this.defaultFilter || undefined; │ │ │ │ } │ │ │ │ - return ret; │ │ │ │ + return merged; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: redraw │ │ │ │ - * Redraws the layer. Returns true if the layer was redrawn, false if not. │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up the protocol. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.options = null; │ │ │ │ + this.format = null; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Construct a request for reading new features. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * force - {Boolean} Force redraw by adding random parameter. │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The layer was redrawn. │ │ │ │ + * {} An │ │ │ │ + * object, the same object will be passed to the callback function passed │ │ │ │ + * if one exists in the options object. │ │ │ │ */ │ │ │ │ - redraw: function(force) { │ │ │ │ - if (force) { │ │ │ │ - return this.mergeNewParams({ │ │ │ │ - "_olSalt": Math.random() │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - return OpenLayers.Layer.prototype.redraw.apply(this, []); │ │ │ │ - } │ │ │ │ + read: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + options.filter = this.mergeWithDefaultFilter(options.filter); │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIMethod: create │ │ │ │ + * Construct a request for writing newly created features. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * paramString - {String} │ │ │ │ - * urls - {Array(String)} │ │ │ │ - * │ │ │ │ + * features - {Array({})} or │ │ │ │ + * {} │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} An entry from the urls array, deterministically selected based │ │ │ │ - * on the paramString. │ │ │ │ + * {} An │ │ │ │ + * object, the same object will be passed to the callback function passed │ │ │ │ + * if one exists in the options object. │ │ │ │ */ │ │ │ │ - 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)]; │ │ │ │ - }, │ │ │ │ + create: function() {}, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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. │ │ │ │ + /** │ │ │ │ + * APIMethod: update │ │ │ │ + * Construct a request updating modified features. │ │ │ │ * │ │ │ │ - * 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: │ │ │ │ + * features - {Array({})} or │ │ │ │ + * {} │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {} An │ │ │ │ + * object, the same object will be passed to the callback function passed │ │ │ │ + * if one exists in the options object. │ │ │ │ + */ │ │ │ │ + update: function() {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: delete │ │ │ │ + * Construct a request deleting a removed feature. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * newParams - {Object} │ │ │ │ - * altUrl - {String} Use this as the url instead of the layer's url │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} │ │ │ │ + * feature - {} │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {} An │ │ │ │ + * object, the same object will be passed to the callback function passed │ │ │ │ + * if one exists in the options object. │ │ │ │ */ │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ + "delete": function() {}, │ │ │ │ │ │ │ │ - // if not altUrl passed in, use layer's url │ │ │ │ - var url = altUrl || this.url; │ │ │ │ + /** │ │ │ │ + * APIMethod: commit │ │ │ │ + * Go over the features and for each take action │ │ │ │ + * based on the feature state. Possible actions are create, │ │ │ │ + * update and delete. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array({})} │ │ │ │ + * options - {Object} Object whose possible keys are "create", "update", │ │ │ │ + * "delete", "callback" and "scope", the values referenced by the │ │ │ │ + * first three are objects as passed to the "create", "update", and │ │ │ │ + * "delete" methods, the value referenced by the "callback" key is │ │ │ │ + * a function which is called when the commit operation is complete │ │ │ │ + * using the scope referenced by the "scope" key. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array({})} An array of │ │ │ │ + * objects. │ │ │ │ + */ │ │ │ │ + commit: function() {}, │ │ │ │ │ │ │ │ - // 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: abort │ │ │ │ + * Abort an ongoing request. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * response - {} │ │ │ │ + */ │ │ │ │ + abort: function(response) {}, │ │ │ │ │ │ │ │ - // 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: createCallback │ │ │ │ + * Returns a function that applies the given public method with resp and │ │ │ │ + * options arguments. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * method - {Function} The method to be applied by the callback. │ │ │ │ + * response - {} The protocol response object. │ │ │ │ + * options - {Object} Options sent to the protocol method │ │ │ │ + */ │ │ │ │ + createCallback: function(method, response, options) { │ │ │ │ + return OpenLayers.Function.bind(function() { │ │ │ │ + method.apply(this, [response, options]); │ │ │ │ + }, this); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // 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); │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol" │ │ │ │ +}); │ │ │ │ │ │ │ │ - return OpenLayers.Util.urlAppend(url, paramsString); │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Protocol.Response │ │ │ │ + * Protocols return Response objects to their users. │ │ │ │ + */ │ │ │ │ +OpenLayers.Protocol.Response = OpenLayers.Class({ │ │ │ │ + /** │ │ │ │ + * Property: code │ │ │ │ + * {Number} - OpenLayers.Protocol.Response.SUCCESS or │ │ │ │ + * OpenLayers.Protocol.Response.FAILURE │ │ │ │ + */ │ │ │ │ + code: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: requestType │ │ │ │ + * {String} The type of request this response corresponds to. Either │ │ │ │ + * "create", "read", "update" or "delete". │ │ │ │ + */ │ │ │ │ + requestType: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: last │ │ │ │ + * {Boolean} - true if this is the last response expected in a commit, │ │ │ │ + * false otherwise, defaults to true. │ │ │ │ + */ │ │ │ │ + last: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: features │ │ │ │ + * {Array({})} or {} │ │ │ │ + * The features returned in the response by the server. Depending on the │ │ │ │ + * protocol's read payload, either features or data will be populated. │ │ │ │ + */ │ │ │ │ + features: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: data │ │ │ │ + * {Object} │ │ │ │ + * The data returned in the response by the server. Depending on the │ │ │ │ + * protocol's read payload, either features or data will be populated. │ │ │ │ + */ │ │ │ │ + data: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: reqFeatures │ │ │ │ + * {Array({})} or {} │ │ │ │ + * The features provided by the user and placed in the request by the │ │ │ │ + * protocol. │ │ │ │ + */ │ │ │ │ + reqFeatures: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: priv │ │ │ │ + */ │ │ │ │ + priv: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: error │ │ │ │ + * {Object} The error object in case a service exception was encountered. │ │ │ │ + */ │ │ │ │ + error: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Protocol.Response │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.HTTPRequest" │ │ │ │ + /** │ │ │ │ + * Method: success │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} - true on success, false otherwise │ │ │ │ + */ │ │ │ │ + success: function() { │ │ │ │ + return this.code > 0; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.Response" │ │ │ │ }); │ │ │ │ + │ │ │ │ +OpenLayers.Protocol.Response.SUCCESS = 1; │ │ │ │ +OpenLayers.Protocol.Response.FAILURE = 0; │ │ │ │ /* ====================================================================== │ │ │ │ OpenLayers/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 │ │ │ │ @@ -13468,2819 +14969,1285 @@ │ │ │ │ clear: function(draw) { │ │ │ │ // to be extended by subclasses │ │ │ │ }, │ │ │ │ │ │ │ │ CLASS_NAME: "OpenLayers.Tile" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Tile/Image.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/Tile.js │ │ │ │ - * @requires OpenLayers/Animation.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.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.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. │ │ │ │ * │ │ │ │ - * Inherits from: │ │ │ │ - * - │ │ │ │ */ │ │ │ │ -OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {} An events object that handles all │ │ │ │ - * events on the tile. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * tile.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types (in addition to the events): │ │ │ │ - * beforeload - Triggered before an image is prepared for loading, when the │ │ │ │ - * url for the image is known already. Listeners may call on │ │ │ │ - * the tile instance. If they do so, that image will be used and no new │ │ │ │ - * one will be created. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: url │ │ │ │ - * {String} The URL of the image being requested. No default. Filled in by │ │ │ │ - * layer.getURL() function. May be modified by loadstart listeners. │ │ │ │ - */ │ │ │ │ - url: null, │ │ │ │ +OpenLayers.Renderer = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: imgDiv │ │ │ │ - * {HTMLImageElement} The image for this tile. │ │ │ │ + * Property: container │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - imgDiv: null, │ │ │ │ + container: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: frame │ │ │ │ - * {DOMElement} The image element is appended to the frame. Any gutter on │ │ │ │ - * the image will be hidden behind the frame. If no gutter is set, │ │ │ │ - * this will be null. │ │ │ │ + * Property: root │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - frame: null, │ │ │ │ + root: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: imageReloadAttempts │ │ │ │ - * {Integer} Attempts to load the image. │ │ │ │ + * Property: extent │ │ │ │ + * {} │ │ │ │ */ │ │ │ │ - imageReloadAttempts: null, │ │ │ │ + extent: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: layerAlphaHack │ │ │ │ - * {Boolean} True if the png alpha hack needs to be applied on the layer's div. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - layerAlphaHack: null, │ │ │ │ + locked: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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. │ │ │ │ + /** │ │ │ │ + * Property: size │ │ │ │ + * {} │ │ │ │ */ │ │ │ │ - asyncRequestId: null, │ │ │ │ + size: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: maxGetUrlLength │ │ │ │ - * {Number} If set, requests that would result in GET urls with more │ │ │ │ - * characters than the number provided will be made using form-encoded │ │ │ │ - * HTTP POST. It is good practice to avoid urls that are longer than 2048 │ │ │ │ - * characters. │ │ │ │ - * │ │ │ │ - * Caution: │ │ │ │ - * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most │ │ │ │ - * Opera versions do not fully support this option. On all browsers, │ │ │ │ - * transition effects are not supported if POST requests are used. │ │ │ │ + * Property: resolution │ │ │ │ + * {Float} cache of current map resolution │ │ │ │ */ │ │ │ │ - maxGetUrlLength: null, │ │ │ │ + resolution: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: canvasContext │ │ │ │ - * {CanvasRenderingContext2D} A canvas context associated with │ │ │ │ - * the tile image. │ │ │ │ + * Property: map │ │ │ │ + * {} Reference to the map -- this is set in Vector's setMap() │ │ │ │ */ │ │ │ │ - canvasContext: null, │ │ │ │ + map: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: crossOriginKeyword │ │ │ │ - * The value of the crossorigin keyword to use when loading images. This is │ │ │ │ - * only relevant when using for tiles from remote │ │ │ │ - * origins and should be set to either 'anonymous' or 'use-credentials' │ │ │ │ - * for servers that send Access-Control-Allow-Origin headers with their │ │ │ │ - * tiles. │ │ │ │ - */ │ │ │ │ - crossOriginKeyword: null, │ │ │ │ - │ │ │ │ - /** TBD 3.0 - reorder the parameters to the init function to remove │ │ │ │ - * URL. the getUrl() function on the layer gets called on │ │ │ │ - * each draw(), so no need to specify it here. │ │ │ │ + * Property: featureDx │ │ │ │ + * {Number} Feature offset in x direction. Will be calculated for and │ │ │ │ + * applied to the current feature while rendering (see │ │ │ │ + * ). │ │ │ │ */ │ │ │ │ + featureDx: 0, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Tile.Image │ │ │ │ - * Constructor for a new instance. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Renderer │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * layer - {} layer that the tile will go in. │ │ │ │ - * position - {} │ │ │ │ - * bounds - {} │ │ │ │ - * url - {} Deprecated. Remove me in 3.0. │ │ │ │ - * size - {} │ │ │ │ - * options - {Object} │ │ │ │ + * containerID - {} │ │ │ │ + * options - {Object} options for this renderer. See sublcasses for │ │ │ │ + * supported options. │ │ │ │ */ │ │ │ │ - initialize: function(layer, position, bounds, url, size, options) { │ │ │ │ - OpenLayers.Tile.prototype.initialize.apply(this, arguments); │ │ │ │ - │ │ │ │ - this.url = url; //deprecated remove me │ │ │ │ - │ │ │ │ - this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack(); │ │ │ │ - │ │ │ │ - if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) { │ │ │ │ - // only create frame if it's needed │ │ │ │ - this.frame = document.createElement("div"); │ │ │ │ - this.frame.style.position = "absolute"; │ │ │ │ - this.frame.style.overflow = "hidden"; │ │ │ │ - } │ │ │ │ - if (this.maxGetUrlLength != null) { │ │ │ │ - OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame); │ │ │ │ - } │ │ │ │ + initialize: function(containerID, options) { │ │ │ │ + this.container = OpenLayers.Util.getElement(containerID); │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ + /** │ │ │ │ * 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); │ │ │ │ + this.container = null; │ │ │ │ + this.extent = null; │ │ │ │ + this.size = null; │ │ │ │ + this.resolution = null; │ │ │ │ + this.map = null; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ - * Check that a tile should be drawn, and draw it. │ │ │ │ + * APIMethod: supported │ │ │ │ + * This should be overridden by specific subclasses │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned │ │ │ │ - * false. │ │ │ │ + * {Boolean} Whether or not the browser supports the renderer class │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments); │ │ │ │ - if (shouldDraw) { │ │ │ │ - // The layer's reproject option is deprecated. │ │ │ │ - if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { │ │ │ │ - // getBoundsFromBaseLayer is defined in deprecated.js. │ │ │ │ - this.bounds = this.getBoundsFromBaseLayer(this.position); │ │ │ │ - } │ │ │ │ - if (this.isLoading) { │ │ │ │ - //if we're already loading, send 'reload' instead of 'loadstart'. │ │ │ │ - this._loadEvent = "reload"; │ │ │ │ - } else { │ │ │ │ - this.isLoading = true; │ │ │ │ - this._loadEvent = "loadstart"; │ │ │ │ - } │ │ │ │ - this.renderTile(); │ │ │ │ - this.positionTile(); │ │ │ │ - } else if (shouldDraw === false) { │ │ │ │ - this.unload(); │ │ │ │ - } │ │ │ │ - return shouldDraw; │ │ │ │ + supported: function() { │ │ │ │ + return false; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: renderTile │ │ │ │ - * Internal function to actually initialize the image tile, │ │ │ │ - * position it correctly, and set its url. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ + 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: 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. │ │ │ │ + * 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 - {} │ │ │ │ */ │ │ │ │ - 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"; │ │ │ │ + setSize: function(size) { │ │ │ │ + this.size = size.clone(); │ │ │ │ + this.resolution = null; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: getResolution │ │ │ │ + * Uses cached copy of resolution if available to minimize computing │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} The current map's resolution │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + getResolution: function() { │ │ │ │ + this.resolution = this.resolution || this.map.getResolution(); │ │ │ │ + return this.resolution; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getImage │ │ │ │ - * Returns or creates and returns the tile image. │ │ │ │ + * Method: drawFeature │ │ │ │ + * Draw the feature. The optional style argument can be used │ │ │ │ + * to override the feature's own style. This method should only │ │ │ │ + * be called from layer.drawFeature(). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {} │ │ │ │ + * style - {} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true if the feature has been drawn completely, false if not, │ │ │ │ + * undefined if the feature had no geometry │ │ │ │ */ │ │ │ │ - getImage: function() { │ │ │ │ - if (!this.imgDiv) { │ │ │ │ - this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false); │ │ │ │ + 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 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; │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - 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 rendered; │ │ │ │ } │ │ │ │ } │ │ │ │ - │ │ │ │ - return this.imgDiv; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setImage │ │ │ │ - * Sets the image element for this tile. This method should only be called │ │ │ │ - * from beforeload listeners. │ │ │ │ + * 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 │ │ │ │ - * img - {HTMLImageElement} The image to use for this tile. │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {} Bounds of the feature │ │ │ │ + * worldBounds - {} Bounds of the world │ │ │ │ */ │ │ │ │ - setImage: function(img) { │ │ │ │ - this.imgDiv = img; │ │ │ │ + calculateFeatureDx: function(bounds, worldBounds) { │ │ │ │ + this.featureDx = 0; │ │ │ │ + if (worldBounds) { │ │ │ │ + var worldWidth = worldBounds.getWidth(), │ │ │ │ + rendererCenterX = (this.extent.left + this.extent.right) / 2, │ │ │ │ + featureCenterX = (bounds.left + bounds.right) / 2, │ │ │ │ + worldsAway = Math.round((featureCenterX - rendererCenterX) / worldWidth); │ │ │ │ + this.featureDx = worldsAway * worldWidth; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Method: drawGeometry │ │ │ │ + * │ │ │ │ + * Draw a geometry. This should only be called from the renderer itself. │ │ │ │ + * Use layer.drawFeature() from outside the renderer. │ │ │ │ + * virtual function │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {} │ │ │ │ + */ │ │ │ │ + drawGeometry: function(geometry, style, featureId) {}, │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: initImage │ │ │ │ - * Creates the content for the frame on the tile. │ │ │ │ + * Method: drawText │ │ │ │ + * Function for drawing text labels. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * featureId - {String} │ │ │ │ + * style - │ │ │ │ + * location - {} │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + drawText: function(featureId, style, location) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setImgSrc │ │ │ │ - * Sets the source for the tile image │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * url - {String} or undefined to hide the image │ │ │ │ + * Method: removeText │ │ │ │ + * Function for removing text labels. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * featureId - {String} │ │ │ │ */ │ │ │ │ - setImgSrc: function(url) { │ │ │ │ - var img = this.imgDiv; │ │ │ │ - if (url) { │ │ │ │ - img.style.visibility = 'hidden'; │ │ │ │ - img.style.opacity = 0; │ │ │ │ - // don't set crossOrigin if the url is a data URL │ │ │ │ - if (this.crossOriginKeyword) { │ │ │ │ - if (url.substr(0, 5) !== 'data:') { │ │ │ │ - img.setAttribute("crossorigin", this.crossOriginKeyword); │ │ │ │ - } else { │ │ │ │ - img.removeAttribute("crossorigin"); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - img.src = url; │ │ │ │ - } else { │ │ │ │ - // Remove reference to the image, and leave it to the browser's │ │ │ │ - // caching and garbage collection. │ │ │ │ - this.stopLoading(); │ │ │ │ - this.imgDiv = null; │ │ │ │ - if (img.parentNode) { │ │ │ │ - img.parentNode.removeChild(img); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + removeText: function(featureId) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getTile │ │ │ │ - * Get the tile's markup. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} The tile's markup │ │ │ │ + * Method: clear │ │ │ │ + * Clear all vectors from the renderer. │ │ │ │ + * virtual function. │ │ │ │ */ │ │ │ │ - getTile: function() { │ │ │ │ - return this.frame ? this.frame : this.getImage(); │ │ │ │ - }, │ │ │ │ + clear: function() {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: getFeatureIdFromEvent │ │ │ │ + * Returns a feature id from an event on the renderer. │ │ │ │ + * How this happens is specific to the renderer. This should be │ │ │ │ + * called from layer.getFeatureFromEvent(). │ │ │ │ + * Virtual function. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} The markup, or undefined if the tile has no image │ │ │ │ - * or if it's currently loading. │ │ │ │ + * {String} A feature id or undefined. │ │ │ │ */ │ │ │ │ - createBackBuffer: function() { │ │ │ │ - if (!this.imgDiv || this.isLoading) { │ │ │ │ - return; │ │ │ │ + getFeatureIdFromEvent: function(evt) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: eraseFeatures │ │ │ │ + * This is called by the layer to erase features │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array()} │ │ │ │ + */ │ │ │ │ + eraseFeatures: function(features) { │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ + features = [features]; │ │ │ │ } │ │ │ │ - var backBuffer; │ │ │ │ - if (this.frame) { │ │ │ │ - backBuffer = this.frame.cloneNode(false); │ │ │ │ - backBuffer.appendChild(this.imgDiv); │ │ │ │ - } else { │ │ │ │ - backBuffer = this.imgDiv; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + var feature = features[i]; │ │ │ │ + this.eraseGeometry(feature.geometry, feature.id); │ │ │ │ + this.removeText(feature.id); │ │ │ │ } │ │ │ │ - this.imgDiv = null; │ │ │ │ - return backBuffer; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onImageLoad │ │ │ │ - * Handler for the image onload event │ │ │ │ + * Method: eraseGeometry │ │ │ │ + * Remove a geometry from the renderer (by id). │ │ │ │ + * virtual function. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {} │ │ │ │ + * featureId - {String} │ │ │ │ */ │ │ │ │ - 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')"; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + eraseGeometry: function(geometry, featureId) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onImageError │ │ │ │ - * Handler for the image onerror event │ │ │ │ + * 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 │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + moveRoot: function(renderer) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: stopLoading │ │ │ │ - * Stops a loading sequence so won't be executed. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - stopLoading: function() { │ │ │ │ - OpenLayers.Event.stopObservingElement(this.imgDiv); │ │ │ │ - window.clearTimeout(this._loadTimeout); │ │ │ │ - delete this._loadTimeout; │ │ │ │ + getRenderLayerId: function() { │ │ │ │ + return this.container.id; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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) │ │ │ │ - * │ │ │ │ + * Method: applyDefaultSymbolizer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * symbolizer - {Object} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} │ │ │ │ + * {Object} │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + 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.Tile.Image" │ │ │ │ - │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer" │ │ │ │ }); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Tile.Image.IMAGE │ │ │ │ - * {HTMLImageElement} The image for a tile. │ │ │ │ +/** │ │ │ │ + * 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.Tile.Image.IMAGE = (function() { │ │ │ │ - var img = new Image(); │ │ │ │ - img.className = "olTileImage"; │ │ │ │ - // avoid image gallery menu in IE6 │ │ │ │ - img.galleryImg = "no"; │ │ │ │ - return img; │ │ │ │ -}()); │ │ │ │ +OpenLayers.Renderer.defaultSymbolizer = { │ │ │ │ + fillColor: "#000000", │ │ │ │ + strokeColor: "#000000", │ │ │ │ + strokeWidth: 2, │ │ │ │ + fillOpacity: 1, │ │ │ │ + strokeOpacity: 1, │ │ │ │ + pointRadius: 0, │ │ │ │ + labelAlign: 'cm' │ │ │ │ +}; │ │ │ │ + │ │ │ │ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Renderer.symbol │ │ │ │ + * Coordinate arrays for well known (named) symbols. │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.symbol = { │ │ │ │ + "star": [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301, │ │ │ │ + 303, 215, 231, 161, 321, 161, 350, 75 │ │ │ │ + ], │ │ │ │ + "cross": [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4, │ │ │ │ + 4, 0 │ │ │ │ + ], │ │ │ │ + "x": [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0], │ │ │ │ + "square": [0, 0, 0, 1, 1, 1, 1, 0, 0, 0], │ │ │ │ + "triangle": [0, 10, 10, 10, 5, 0, 0, 10] │ │ │ │ +}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/Grid.js │ │ │ │ + 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/Layer/HTTPRequest.js │ │ │ │ - * @requires OpenLayers/Tile/Image.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.Grid │ │ │ │ - * Base class for layers that use a lattice of tiles. Create a new grid │ │ │ │ - * layer with the constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - │ │ │ │ + * 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.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", │ │ │ │ +OpenLayers.Format = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Property: options │ │ │ │ + * {Object} A reference to options passed to the constructor. │ │ │ │ */ │ │ │ │ - tileOptions: null, │ │ │ │ + options: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: tileClass │ │ │ │ - * {} The tile class to use for this layer. │ │ │ │ - * Defaults is OpenLayers.Tile.Image. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - tileClass: OpenLayers.Tile.Image, │ │ │ │ + externalProjection: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: grid │ │ │ │ - * {Array(Array())} This is an array of rows, each row is │ │ │ │ - * an array of tiles. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - grid: null, │ │ │ │ + internalProjection: 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. │ │ │ │ + * APIProperty: data │ │ │ │ + * {Object} When is true, this is the parsed string sent to │ │ │ │ + * . │ │ │ │ */ │ │ │ │ - ratio: 1.5, │ │ │ │ + data: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIProperty: keepData │ │ │ │ + * {Object} Maintain a reference () to the most recently read data. │ │ │ │ + * Default is false. │ │ │ │ */ │ │ │ │ - buffer: 0, │ │ │ │ + keepData: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: transitionEffect │ │ │ │ - * {String} The transition effect to use when the map is zoomed. │ │ │ │ - * Two posible values: │ │ │ │ + * Constructor: OpenLayers.Format │ │ │ │ + * Instances of this class are not useful. See one of the subclasses. │ │ │ │ * │ │ │ │ - * "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. │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object with properties to set on the │ │ │ │ + * format │ │ │ │ * │ │ │ │ - * Using "resize" on non-opaque layers can cause undesired visual │ │ │ │ - * effects. Set transitionEffect to null in this case. │ │ │ │ + * 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 │ │ │ │ */ │ │ │ │ - transitionEffect: "resize", │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.options = options; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: numLoadingTiles │ │ │ │ - * {Integer} How many tiles are still loading? │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up. │ │ │ │ */ │ │ │ │ - numLoadingTiles: 0, │ │ │ │ + destroy: function() {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: serverResolutions │ │ │ │ - * {Array(Number}} This property is documented in subclasses as │ │ │ │ - * an API property. │ │ │ │ + * 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 │ │ │ │ */ │ │ │ │ - serverResolutions: null, │ │ │ │ + read: function(data) { │ │ │ │ + throw new Error('Read not implemented.'); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: loading │ │ │ │ - * {Boolean} Indicates if tiles are being loaded. │ │ │ │ + * Method: write │ │ │ │ + * Accept an object, and return a string. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * object - {Object} Object to be serialized │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A string representation of the object. │ │ │ │ */ │ │ │ │ - loading: false, │ │ │ │ + write: function(object) { │ │ │ │ + throw new Error('Write not implemented.'); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: backBuffer │ │ │ │ - * {DOMElement} The back buffer. │ │ │ │ - */ │ │ │ │ - backBuffer: null, │ │ │ │ + CLASS_NAME: "OpenLayers.Format" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Handler.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: gridResolution │ │ │ │ - * {Number} The resolution of the current grid. Used for backbuffer and │ │ │ │ - * client zoom. This property is updated every time the grid is │ │ │ │ - * initialized. │ │ │ │ - */ │ │ │ │ - gridResolution: null, │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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: backBufferResolution │ │ │ │ - * {Number} The resolution of the current back buffer. This property is │ │ │ │ - * updated each time a back buffer is created. │ │ │ │ - */ │ │ │ │ - backBufferResolution: null, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Events.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: backBufferLonLat │ │ │ │ - * {Object} The top-left corner of the current back buffer. Includes lon │ │ │ │ - * and lat properties. This object is updated each time a back buffer │ │ │ │ - * is created. │ │ │ │ - */ │ │ │ │ - backBufferLonLat: null, │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Handler │ │ │ │ + * Base class to construct a higher-level handler for event sequences. All │ │ │ │ + * handlers have activate and deactivate methods. In addition, they have │ │ │ │ + * methods named like browser events. When a handler is activated, any │ │ │ │ + * additional methods named like a browser event is registered as a │ │ │ │ + * listener for the corresponding event. When a handler is deactivated, │ │ │ │ + * those same methods are unregistered as event listeners. │ │ │ │ + * │ │ │ │ + * Handlers also typically have a callbacks object with keys named like │ │ │ │ + * the abstracted events or event sequences that they are in charge of │ │ │ │ + * handling. The controls that wrap handlers define the methods that │ │ │ │ + * correspond to these abstract events - so instead of listening for │ │ │ │ + * individual browser events, they only listen for the abstract events │ │ │ │ + * defined by the handler. │ │ │ │ + * │ │ │ │ + * Handlers are created by controls, which ultimately have the responsibility │ │ │ │ + * of making changes to the the state of the application. Handlers │ │ │ │ + * themselves may make temporary changes, but in general are expected to │ │ │ │ + * return the application in the same state that they found it. │ │ │ │ + */ │ │ │ │ +OpenLayers.Handler = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: 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: id │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - backBufferTimerId: null, │ │ │ │ + id: 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. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - removeBackBufferDelay: null, │ │ │ │ + control: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: className │ │ │ │ - * {String} Name of the class added to the layer div. If not set in the │ │ │ │ - * options passed to the constructor then className defaults to │ │ │ │ - * "olLayerGridSingleTile" for single tile layers (see ), │ │ │ │ - * and "olLayerGrid" for non single tile layers. │ │ │ │ - * │ │ │ │ - * Note: │ │ │ │ - * │ │ │ │ - * The displaying of tiles is not animated by default for single tile │ │ │ │ - * layers - OpenLayers' default theme (style.css) includes this: │ │ │ │ - * (code) │ │ │ │ - * .olLayerGrid .olTileImage { │ │ │ │ - * -webkit-transition: opacity 0.2s linear; │ │ │ │ - * -moz-transition: opacity 0.2s linear; │ │ │ │ - * -o-transition: opacity 0.2s linear; │ │ │ │ - * transition: opacity 0.2s linear; │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ - * To animate tile displaying for any grid layer the following │ │ │ │ - * CSS rule can be used: │ │ │ │ - * (code) │ │ │ │ - * .olTileImage { │ │ │ │ - * -webkit-transition: opacity 0.2s linear; │ │ │ │ - * -moz-transition: opacity 0.2s linear; │ │ │ │ - * -o-transition: opacity 0.2s linear; │ │ │ │ - * transition: opacity 0.2s linear; │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ - * In that case, to avoid flash effects, │ │ │ │ - * should not be zero. │ │ │ │ + * Property: map │ │ │ │ + * {} │ │ │ │ */ │ │ │ │ - className: null, │ │ │ │ + map: 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. │ │ │ │ + * 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. │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * Example: │ │ │ │ + * (code) │ │ │ │ + * // handler only responds if the Shift key is down │ │ │ │ + * handler.keyMask = OpenLayers.Handler.MOD_SHIFT; │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * // handler only responds if Ctrl-Shift is down │ │ │ │ + * handler.keyMask = OpenLayers.Handler.MOD_SHIFT | │ │ │ │ + * OpenLayers.Handler.MOD_CTRL; │ │ │ │ + * (end) │ │ │ │ */ │ │ │ │ + keyMask: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: gridLayout │ │ │ │ - * {Object} Object containing properties tilelon, tilelat, startcol, │ │ │ │ - * startrow │ │ │ │ + * Property: active │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - gridLayout: null, │ │ │ │ + active: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - rowSign: null, │ │ │ │ + evt: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: transitionendEvents │ │ │ │ - * {Array} Event names for transitionend │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - transitionendEvents: [ │ │ │ │ - 'transitionend', 'webkitTransitionEnd', 'otransitionend', │ │ │ │ - 'oTransitionEnd' │ │ │ │ - ], │ │ │ │ + touch: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.Grid │ │ │ │ - * Create a new grid layer │ │ │ │ + * Constructor: OpenLayers.Handler │ │ │ │ + * Construct a handler. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * url - {String} │ │ │ │ - * params - {Object} │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * 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(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; │ │ │ │ - }, │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.control = control; │ │ │ │ + this.callbacks = callbacks; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: initProperties │ │ │ │ - * Set any properties that depend on the value of singleTile. │ │ │ │ - * Currently sets removeBackBufferDelay and className │ │ │ │ - */ │ │ │ │ - initProperties: function() { │ │ │ │ - if (this.options.removeBackBufferDelay === undefined) { │ │ │ │ - this.removeBackBufferDelay = this.singleTile ? 0 : 2500; │ │ │ │ + var map = this.map || control.map; │ │ │ │ + if (map) { │ │ │ │ + this.setMap(map); │ │ │ │ } │ │ │ │ │ │ │ │ - if (this.options.className === undefined) { │ │ │ │ - this.className = this.singleTile ? 'olLayerGridSingleTile' : │ │ │ │ - 'olLayerGrid'; │ │ │ │ - } │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ * Method: setMap │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {} The map. │ │ │ │ */ │ │ │ │ setMap: function(map) { │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map); │ │ │ │ - OpenLayers.Element.addClass(this.div, this.className); │ │ │ │ + this.map = map; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: removeMap │ │ │ │ - * Called when the layer is removed from the 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. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * map - {} The map. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The keyMask matches the keys down on an event. │ │ │ │ */ │ │ │ │ - removeMap: function(map) { │ │ │ │ - this.removeBackBuffer(); │ │ │ │ + checkModifiers: function(evt) { │ │ │ │ + if (this.keyMask == null) { │ │ │ │ + return true; │ │ │ │ + } │ │ │ │ + /* calculate the keyboard modifier mask for this event */ │ │ │ │ + var keyModifiers = │ │ │ │ + (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) | │ │ │ │ + (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) | │ │ │ │ + (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) | │ │ │ │ + (evt.metaKey ? OpenLayers.Handler.MOD_META : 0); │ │ │ │ + │ │ │ │ + /* if it differs from the handler object's key mask, │ │ │ │ + bail out of the event handler */ │ │ │ │ + return (keyModifiers == this.keyMask); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Deconstruct the layer and clear the grid. │ │ │ │ + * APIMethod: activate │ │ │ │ + * Turn on the handler. Returns false if the handler was already active. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The handler was activated. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.removeBackBuffer(); │ │ │ │ - this.clearGrid(); │ │ │ │ - │ │ │ │ - this.grid = null; │ │ │ │ - this.tileSize = null; │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); │ │ │ │ + activate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + // register for event handlers defined on this class. │ │ │ │ + var events = OpenLayers.Events.prototype.BROWSER_EVENTS; │ │ │ │ + for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ + if (this[events[i]]) { │ │ │ │ + this.register(events[i], this[events[i]]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.active = true; │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: 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: deactivate │ │ │ │ + * Turn off the handler. Returns false if the handler was already inactive. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * newParams - {Object} │ │ │ │ - * │ │ │ │ * Returns: │ │ │ │ - * redrawn: {Boolean} whether the layer was actually redrawn. │ │ │ │ + * {Boolean} The handler was deactivated. │ │ │ │ */ │ │ │ │ + deactivate: function() { │ │ │ │ + if (!this.active) { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + // unregister event handlers defined on this class. │ │ │ │ + var events = OpenLayers.Events.prototype.BROWSER_EVENTS; │ │ │ │ + for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ + if (this[events[i]]) { │ │ │ │ + this.unregister(events[i], this[events[i]]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.touch = false; │ │ │ │ + this.active = false; │ │ │ │ + return true; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clearGrid │ │ │ │ - * Go through and remove all tiles from the grid, calling │ │ │ │ - * destroy() on each of them to kill circular references │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + 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]]); │ │ │ │ } │ │ │ │ } │ │ │ │ - this.grid = []; │ │ │ │ - this.gridResolution = null; │ │ │ │ - this.gridLayout = null; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: addOptions │ │ │ │ - * │ │ │ │ + * Method: callback │ │ │ │ + * Trigger the control's named callback with the given arguments │ │ │ │ + * │ │ │ │ * 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. │ │ │ │ + * 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). │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + callback: function(name, args) { │ │ │ │ + if (name && this.callbacks[name]) { │ │ │ │ + this.callbacks[name].apply(this.control, args); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {Object} Is this ever used? │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {} An exact clone of this OpenLayers.Layer.Grid │ │ │ │ + * Method: register │ │ │ │ + * register an event on the map │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + 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); │ │ │ │ + }, │ │ │ │ │ │ │ │ - return obj; │ │ │ │ + /** │ │ │ │ + * Method: unregister │ │ │ │ + * unregister an event from the map │ │ │ │ + */ │ │ │ │ + unregister: function(name, method) { │ │ │ │ + this.map.events.unregister(name, this, method); │ │ │ │ + this.map.events.unregister(name, this, this.setEvent); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: 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: 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: │ │ │ │ - * bounds - {} │ │ │ │ - * zoomChanged - {Boolean} │ │ │ │ - * dragging - {Boolean} │ │ │ │ + * evt - {Event} The browser event. │ │ │ │ */ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments); │ │ │ │ + setEvent: function(evt) { │ │ │ │ + this.evt = evt; │ │ │ │ + return true; │ │ │ │ + }, │ │ │ │ │ │ │ │ - bounds = bounds || this.map.getExtent(); │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * Deconstruct the handler. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + // unregister event listeners │ │ │ │ + this.deactivate(); │ │ │ │ + // eliminate circular references │ │ │ │ + this.control = this.map = null; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (bounds != null) { │ │ │ │ + CLASS_NAME: "OpenLayers.Handler" │ │ │ │ +}); │ │ │ │ │ │ │ │ - // if grid is empty or zoom has changed, we *must* re-tile │ │ │ │ - var forceReTile = !this.grid.length || zoomChanged; │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Handler.MOD_NONE │ │ │ │ + * If set as the , returns false if any key is down. │ │ │ │ + */ │ │ │ │ +OpenLayers.Handler.MOD_NONE = 0; │ │ │ │ │ │ │ │ - // total bounds of the tiles │ │ │ │ - var tilesBounds = this.getTilesBounds(); │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Handler.MOD_SHIFT │ │ │ │ + * If set as the , returns false if Shift is down. │ │ │ │ + */ │ │ │ │ +OpenLayers.Handler.MOD_SHIFT = 1; │ │ │ │ │ │ │ │ - // the new map resolution │ │ │ │ - var resolution = this.map.getResolution(); │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Handler.MOD_CTRL │ │ │ │ + * If set as the , returns false if Ctrl is down. │ │ │ │ + */ │ │ │ │ +OpenLayers.Handler.MOD_CTRL = 2; │ │ │ │ │ │ │ │ - // the server-supported resolution for the new map resolution │ │ │ │ - var serverResolution = this.getServerResolution(resolution); │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Handler.MOD_ALT │ │ │ │ + * If set as the , returns false if Alt is down. │ │ │ │ + */ │ │ │ │ +OpenLayers.Handler.MOD_ALT = 4; │ │ │ │ │ │ │ │ - if (this.singleTile) { │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Handler.MOD_META │ │ │ │ + * If set as the , returns false if Cmd is down. │ │ │ │ + */ │ │ │ │ +OpenLayers.Handler.MOD_META = 8; │ │ │ │ │ │ │ │ - // We want to redraw whenever even the slightest part of the │ │ │ │ - // current bounds is not contained by our tile. │ │ │ │ - // (thus, we do not specify partial -- its default is false) │ │ │ │ │ │ │ │ - if (forceReTile || │ │ │ │ - (!dragging && !tilesBounds.containsBounds(bounds))) { │ │ │ │ - │ │ │ │ - // In single tile mode with no transition effect, we insert │ │ │ │ - // a non-scaled backbuffer when the layer is moved. But if │ │ │ │ - // a zoom occurs right after a move, i.e. before the new │ │ │ │ - // image is received, we need to remove the backbuffer, or │ │ │ │ - // an ill-positioned image will be visible during the zoom │ │ │ │ - // transition. │ │ │ │ - │ │ │ │ - if (zoomChanged && this.transitionEffect !== 'resize') { │ │ │ │ - this.removeBackBuffer(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (!zoomChanged || this.transitionEffect === 'resize') { │ │ │ │ - this.applyBackBuffer(resolution); │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.initSingleTile(bounds); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - │ │ │ │ - // if the bounds have changed such that they are not even │ │ │ │ - // *partially* contained by our tiles (e.g. when user has │ │ │ │ - // programmatically panned to the other side of the earth on │ │ │ │ - // zoom level 18), then moveGriddedTiles could potentially have │ │ │ │ - // to run through thousands of cycles, so we want to reTile │ │ │ │ - // instead (thus, partial true). │ │ │ │ - forceReTile = forceReTile || │ │ │ │ - !tilesBounds.intersectsBounds(bounds, { │ │ │ │ - worldBounds: this.map.baseLayer.wrapDateLine && │ │ │ │ - this.map.getMaxExtent() │ │ │ │ - }); │ │ │ │ - │ │ │ │ - if (forceReTile) { │ │ │ │ - if (zoomChanged && (this.transitionEffect === 'resize' || │ │ │ │ - this.gridResolution === resolution)) { │ │ │ │ - this.applyBackBuffer(resolution); │ │ │ │ - } │ │ │ │ - this.initGriddedTiles(bounds); │ │ │ │ - } else { │ │ │ │ - this.moveGriddedTiles(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getTileData │ │ │ │ - * Given a map location, retrieve a tile and the pixel offset within that │ │ │ │ - * tile corresponding to the location. If there is not an existing │ │ │ │ - * tile in the grid that covers the given location, null will be │ │ │ │ - * returned. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * loc - {} map location │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Object with the following properties: tile ({}), │ │ │ │ - * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel │ │ │ │ - * offset from top left). │ │ │ │ - */ │ │ │ │ - getTileData: function(loc) { │ │ │ │ - var data = null, │ │ │ │ - x = loc.lon, │ │ │ │ - y = loc.lat, │ │ │ │ - numRows = this.grid.length; │ │ │ │ - │ │ │ │ - if (this.map && numRows) { │ │ │ │ - var res = this.map.getResolution(), │ │ │ │ - tileWidth = this.tileSize.w, │ │ │ │ - tileHeight = this.tileSize.h, │ │ │ │ - bounds = this.grid[0][0].bounds, │ │ │ │ - left = bounds.left, │ │ │ │ - top = bounds.top; │ │ │ │ - │ │ │ │ - if (x < left) { │ │ │ │ - // deal with multiple worlds │ │ │ │ - if (this.map.baseLayer.wrapDateLine) { │ │ │ │ - var worldWidth = this.map.getMaxExtent().getWidth(); │ │ │ │ - var worldsAway = Math.ceil((left - x) / worldWidth); │ │ │ │ - x += worldWidth * worldsAway; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - // tile distance to location (fractional number of tiles); │ │ │ │ - var dtx = (x - left) / (res * tileWidth); │ │ │ │ - var dty = (top - y) / (res * tileHeight); │ │ │ │ - // index of tile in grid │ │ │ │ - var col = Math.floor(dtx); │ │ │ │ - var row = Math.floor(dty); │ │ │ │ - if (row >= 0 && row < numRows) { │ │ │ │ - var tile = this.grid[row][col]; │ │ │ │ - if (tile) { │ │ │ │ - data = { │ │ │ │ - tile: tile, │ │ │ │ - // pixel index within tile │ │ │ │ - i: Math.floor((dtx - col) * tileWidth), │ │ │ │ - j: Math.floor((dty - row) * tileHeight) │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return data; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroyTile │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * tile - {} │ │ │ │ - */ │ │ │ │ - destroyTile: function(tile) { │ │ │ │ - this.removeTileMonitoringHooks(tile); │ │ │ │ - tile.destroy(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getServerResolution │ │ │ │ - * Return the closest server-supported resolution. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * resolution - {Number} The base resolution. If undefined the │ │ │ │ - * map resolution is used. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Number} The closest server resolution value. │ │ │ │ - */ │ │ │ │ - getServerResolution: function(resolution) { │ │ │ │ - var distance = Number.POSITIVE_INFINITY; │ │ │ │ - resolution = resolution || this.map.getResolution(); │ │ │ │ - if (this.serverResolutions && │ │ │ │ - OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) { │ │ │ │ - var i, newDistance, newResolution, serverResolution; │ │ │ │ - for (i = this.serverResolutions.length - 1; i >= 0; i--) { │ │ │ │ - newResolution = this.serverResolutions[i]; │ │ │ │ - newDistance = Math.abs(newResolution - resolution); │ │ │ │ - if (newDistance > distance) { │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - distance = newDistance; │ │ │ │ - serverResolution = newResolution; │ │ │ │ - } │ │ │ │ - resolution = serverResolution; │ │ │ │ - } │ │ │ │ - return resolution; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getServerZoom │ │ │ │ - * Return the zoom value corresponding to the best matching server │ │ │ │ - * resolution, taking into account and . │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Number} The closest server supported zoom. This is not the map zoom │ │ │ │ - * level, but an index of the server's resolutions array. │ │ │ │ - */ │ │ │ │ - getServerZoom: function() { │ │ │ │ - var resolution = this.getServerResolution(); │ │ │ │ - return this.serverResolutions ? │ │ │ │ - OpenLayers.Util.indexOf(this.serverResolutions, resolution) : │ │ │ │ - this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: applyBackBuffer │ │ │ │ - * Create, insert, scale and position a back buffer for the layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * resolution - {Number} The resolution to transition to. │ │ │ │ - */ │ │ │ │ - applyBackBuffer: function(resolution) { │ │ │ │ - if (this.backBufferTimerId !== null) { │ │ │ │ - this.removeBackBuffer(); │ │ │ │ - } │ │ │ │ - var backBuffer = this.backBuffer; │ │ │ │ - if (!backBuffer) { │ │ │ │ - backBuffer = this.createBackBuffer(); │ │ │ │ - if (!backBuffer) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (resolution === this.gridResolution) { │ │ │ │ - this.div.insertBefore(backBuffer, this.div.firstChild); │ │ │ │ - } else { │ │ │ │ - this.map.baseLayer.div.parentNode.insertBefore(backBuffer, this.map.baseLayer.div); │ │ │ │ - } │ │ │ │ - this.backBuffer = backBuffer; │ │ │ │ - │ │ │ │ - // set some information in the instance for subsequent │ │ │ │ - // calls to applyBackBuffer where the same back buffer │ │ │ │ - // is reused │ │ │ │ - var topLeftTileBounds = this.grid[0][0].bounds; │ │ │ │ - this.backBufferLonLat = { │ │ │ │ - lon: topLeftTileBounds.left, │ │ │ │ - lat: topLeftTileBounds.top │ │ │ │ - }; │ │ │ │ - this.backBufferResolution = this.gridResolution; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var ratio = this.backBufferResolution / resolution; │ │ │ │ - │ │ │ │ - // scale the tiles inside the back buffer │ │ │ │ - var tiles = backBuffer.childNodes, │ │ │ │ - tile; │ │ │ │ - for (var i = tiles.length - 1; i >= 0; --i) { │ │ │ │ - tile = tiles[i]; │ │ │ │ - tile.style.top = ((ratio * tile._i * tile._h) | 0) + 'px'; │ │ │ │ - tile.style.left = ((ratio * tile._j * tile._w) | 0) + 'px'; │ │ │ │ - tile.style.width = Math.round(ratio * tile._w) + 'px'; │ │ │ │ - tile.style.height = Math.round(ratio * tile._h) + 'px'; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // and position it (based on the grid's top-left corner) │ │ │ │ - var position = this.getViewPortPxFromLonLat( │ │ │ │ - this.backBufferLonLat, resolution); │ │ │ │ - var leftOffset = this.map.layerContainerOriginPx.x; │ │ │ │ - var topOffset = this.map.layerContainerOriginPx.y; │ │ │ │ - backBuffer.style.left = Math.round(position.x - leftOffset) + 'px'; │ │ │ │ - backBuffer.style.top = Math.round(position.y - topOffset) + 'px'; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: createBackBuffer │ │ │ │ - * Create a back buffer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} The DOM element for the back buffer, undefined if the │ │ │ │ - * grid isn't initialized yet. │ │ │ │ - */ │ │ │ │ - createBackBuffer: function() { │ │ │ │ - var backBuffer; │ │ │ │ - if (this.grid.length > 0) { │ │ │ │ - backBuffer = document.createElement('div'); │ │ │ │ - backBuffer.id = this.div.id + '_bb'; │ │ │ │ - backBuffer.className = 'olBackBuffer'; │ │ │ │ - backBuffer.style.position = 'absolute'; │ │ │ │ - var map = this.map; │ │ │ │ - backBuffer.style.zIndex = this.transitionEffect === 'resize' ? │ │ │ │ - this.getZIndex() - 1 : │ │ │ │ - // 'map-resize': │ │ │ │ - map.Z_INDEX_BASE.BaseLayer - │ │ │ │ - (map.getNumLayers() - map.getLayerIndex(this)); │ │ │ │ - for (var i = 0, lenI = this.grid.length; i < lenI; i++) { │ │ │ │ - for (var j = 0, lenJ = this.grid[i].length; j < lenJ; j++) { │ │ │ │ - var tile = this.grid[i][j], │ │ │ │ - markup = this.grid[i][j].createBackBuffer(); │ │ │ │ - if (markup) { │ │ │ │ - markup._i = i; │ │ │ │ - markup._j = j; │ │ │ │ - markup._w = tile.size.w; │ │ │ │ - markup._h = tile.size.h; │ │ │ │ - markup.id = tile.id + '_bb'; │ │ │ │ - backBuffer.appendChild(markup); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return backBuffer; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: removeBackBuffer │ │ │ │ - * Remove back buffer from DOM. │ │ │ │ - */ │ │ │ │ - removeBackBuffer: function() { │ │ │ │ - if (this._transitionElement) { │ │ │ │ - for (var i = this.transitionendEvents.length - 1; i >= 0; --i) { │ │ │ │ - OpenLayers.Event.stopObserving(this._transitionElement, │ │ │ │ - this.transitionendEvents[i], this._removeBackBuffer); │ │ │ │ - } │ │ │ │ - delete this._transitionElement; │ │ │ │ - } │ │ │ │ - if (this.backBuffer) { │ │ │ │ - if (this.backBuffer.parentNode) { │ │ │ │ - this.backBuffer.parentNode.removeChild(this.backBuffer); │ │ │ │ - } │ │ │ │ - this.backBuffer = null; │ │ │ │ - this.backBufferResolution = null; │ │ │ │ - if (this.backBufferTimerId !== null) { │ │ │ │ - window.clearTimeout(this.backBufferTimerId); │ │ │ │ - this.backBufferTimerId = null; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: moveByPx │ │ │ │ - * Move the layer based on pixel vector. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * dx - {Number} │ │ │ │ - * dy - {Number} │ │ │ │ - */ │ │ │ │ - moveByPx: function(dx, dy) { │ │ │ │ - if (!this.singleTile) { │ │ │ │ - this.moveGriddedTiles(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setTileSize │ │ │ │ - * Check if we are in singleTile mode and if so, set the size as a ratio │ │ │ │ - * of the map size (as specified by the layer's 'ratio' property). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * size - {} │ │ │ │ - */ │ │ │ │ - setTileSize: function(size) { │ │ │ │ - if (this.singleTile) { │ │ │ │ - size = this.map.getSize(); │ │ │ │ - size.h = parseInt(size.h * this.ratio, 10); │ │ │ │ - size.w = parseInt(size.w * this.ratio, 10); │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getTilesBounds │ │ │ │ - * Return the bounds of the tile grid. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {} A Bounds object representing the bounds of all the │ │ │ │ - * currently loaded tiles (including those partially or not at all seen │ │ │ │ - * onscreen). │ │ │ │ - */ │ │ │ │ - getTilesBounds: function() { │ │ │ │ - var bounds = null; │ │ │ │ - │ │ │ │ - var length = this.grid.length; │ │ │ │ - if (length) { │ │ │ │ - var bottomLeftTileBounds = this.grid[length - 1][0].bounds, │ │ │ │ - width = this.grid[0].length * bottomLeftTileBounds.getWidth(), │ │ │ │ - height = this.grid.length * bottomLeftTileBounds.getHeight(); │ │ │ │ - │ │ │ │ - bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, │ │ │ │ - bottomLeftTileBounds.bottom, │ │ │ │ - bottomLeftTileBounds.left + width, │ │ │ │ - bottomLeftTileBounds.bottom + height); │ │ │ │ - } │ │ │ │ - return bounds; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: initSingleTile │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {} │ │ │ │ - */ │ │ │ │ - initSingleTile: function(bounds) { │ │ │ │ - this.events.triggerEvent("retile"); │ │ │ │ - │ │ │ │ - //determine new tile bounds │ │ │ │ - var center = bounds.getCenterLonLat(); │ │ │ │ - var tileWidth = bounds.getWidth() * this.ratio; │ │ │ │ - var tileHeight = bounds.getHeight() * this.ratio; │ │ │ │ - │ │ │ │ - var tileBounds = │ │ │ │ - new OpenLayers.Bounds(center.lon - (tileWidth / 2), │ │ │ │ - center.lat - (tileHeight / 2), │ │ │ │ - center.lon + (tileWidth / 2), │ │ │ │ - center.lat + (tileHeight / 2)); │ │ │ │ - │ │ │ │ - var px = this.map.getLayerPxFromLonLat({ │ │ │ │ - lon: tileBounds.left, │ │ │ │ - lat: tileBounds.top │ │ │ │ - }); │ │ │ │ - │ │ │ │ - if (!this.grid.length) { │ │ │ │ - this.grid[0] = []; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var tile = this.grid[0][0]; │ │ │ │ - if (!tile) { │ │ │ │ - tile = this.addTile(tileBounds, px); │ │ │ │ - │ │ │ │ - this.addTileMonitoringHooks(tile); │ │ │ │ - tile.draw(); │ │ │ │ - this.grid[0][0] = tile; │ │ │ │ - } else { │ │ │ │ - tile.moveTo(tileBounds, px); │ │ │ │ - } │ │ │ │ - │ │ │ │ - //remove all but our single tile │ │ │ │ - this.removeExcessTiles(1, 1); │ │ │ │ - │ │ │ │ - // store the resolution of the grid │ │ │ │ - this.gridResolution = this.getServerResolution(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: calculateGridLayout │ │ │ │ - * Generate parameters for the grid layout. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {|Object} OpenLayers.Bounds or an │ │ │ │ - * object with a 'left' and 'top' properties. │ │ │ │ - * origin - {|Object} OpenLayers.LonLat or an │ │ │ │ - * object with a 'lon' and 'lat' properties. │ │ │ │ - * resolution - {Number} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Object containing properties tilelon, tilelat, startcol, │ │ │ │ - * startrow │ │ │ │ - */ │ │ │ │ - calculateGridLayout: function(bounds, origin, resolution) { │ │ │ │ - var tilelon = resolution * this.tileSize.w; │ │ │ │ - var tilelat = resolution * this.tileSize.h; │ │ │ │ - │ │ │ │ - var offsetlon = bounds.left - origin.lon; │ │ │ │ - var tilecol = Math.floor(offsetlon / tilelon) - this.buffer; │ │ │ │ - │ │ │ │ - var rowSign = this.rowSign; │ │ │ │ - │ │ │ │ - var offsetlat = rowSign * (origin.lat - bounds.top + tilelat); │ │ │ │ - var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat / tilelat) - this.buffer * rowSign; │ │ │ │ - │ │ │ │ - return { │ │ │ │ - tilelon: tilelon, │ │ │ │ - tilelat: tilelat, │ │ │ │ - startcol: tilecol, │ │ │ │ - startrow: tilerow │ │ │ │ - }; │ │ │ │ - │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getTileOrigin │ │ │ │ - * Determine the origin for aligning the grid of tiles. If a │ │ │ │ - * property is supplied, that will be returned. Otherwise, the origin │ │ │ │ - * will be derived from the layer's property. In this case, │ │ │ │ - * the tile origin will be the corner of the given by the │ │ │ │ - * property. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {} The tile origin. │ │ │ │ - */ │ │ │ │ - getTileOrigin: function() { │ │ │ │ - var origin = this.tileOrigin; │ │ │ │ - if (!origin) { │ │ │ │ - var extent = this.getMaxExtent(); │ │ │ │ - var edges = ({ │ │ │ │ - "tl": ["left", "top"], │ │ │ │ - "tr": ["right", "top"], │ │ │ │ - "bl": ["left", "bottom"], │ │ │ │ - "br": ["right", "bottom"] │ │ │ │ - })[this.tileOriginCorner]; │ │ │ │ - origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]); │ │ │ │ - } │ │ │ │ - return origin; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getTileBoundsForGridIndex │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * row - {Number} The row of the grid │ │ │ │ - * col - {Number} The column of the grid │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {} The bounds for the tile at (row, col) │ │ │ │ - */ │ │ │ │ - getTileBoundsForGridIndex: function(row, col) { │ │ │ │ - var origin = this.getTileOrigin(); │ │ │ │ - var tileLayout = this.gridLayout; │ │ │ │ - var tilelon = tileLayout.tilelon; │ │ │ │ - var tilelat = tileLayout.tilelat; │ │ │ │ - var startcol = tileLayout.startcol; │ │ │ │ - var startrow = tileLayout.startrow; │ │ │ │ - var rowSign = this.rowSign; │ │ │ │ - return new OpenLayers.Bounds( │ │ │ │ - origin.lon + (startcol + col) * tilelon, │ │ │ │ - origin.lat - (startrow + row * rowSign) * tilelat * rowSign, │ │ │ │ - origin.lon + (startcol + col + 1) * tilelon, │ │ │ │ - origin.lat - (startrow + (row - 1) * rowSign) * tilelat * rowSign │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: initGriddedTiles │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {} │ │ │ │ - */ │ │ │ │ - initGriddedTiles: function(bounds) { │ │ │ │ - this.events.triggerEvent("retile"); │ │ │ │ - │ │ │ │ - // work out mininum number of rows and columns; this is the number of │ │ │ │ - // tiles required to cover the viewport plus at least one for panning │ │ │ │ - │ │ │ │ - var viewSize = this.map.getSize(); │ │ │ │ - │ │ │ │ - var origin = this.getTileOrigin(); │ │ │ │ - var resolution = this.map.getResolution(), │ │ │ │ - serverResolution = this.getServerResolution(), │ │ │ │ - ratio = resolution / serverResolution, │ │ │ │ - tileSize = { │ │ │ │ - w: this.tileSize.w / ratio, │ │ │ │ - h: this.tileSize.h / ratio │ │ │ │ - }; │ │ │ │ - │ │ │ │ - var minRows = Math.ceil(viewSize.h / tileSize.h) + │ │ │ │ - 2 * this.buffer + 1; │ │ │ │ - var minCols = Math.ceil(viewSize.w / tileSize.w) + │ │ │ │ - 2 * this.buffer + 1; │ │ │ │ - │ │ │ │ - var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution); │ │ │ │ - this.gridLayout = tileLayout; │ │ │ │ - │ │ │ │ - var tilelon = tileLayout.tilelon; │ │ │ │ - var tilelat = tileLayout.tilelat; │ │ │ │ - │ │ │ │ - var layerContainerDivLeft = this.map.layerContainerOriginPx.x; │ │ │ │ - var layerContainerDivTop = this.map.layerContainerOriginPx.y; │ │ │ │ - │ │ │ │ - var tileBounds = this.getTileBoundsForGridIndex(0, 0); │ │ │ │ - var startPx = this.map.getViewPortPxFromLonLat( │ │ │ │ - new OpenLayers.LonLat(tileBounds.left, tileBounds.top) │ │ │ │ - ); │ │ │ │ - startPx.x = Math.round(startPx.x) - layerContainerDivLeft; │ │ │ │ - startPx.y = Math.round(startPx.y) - layerContainerDivTop; │ │ │ │ - │ │ │ │ - var tileData = [], │ │ │ │ - center = this.map.getCenter(); │ │ │ │ - │ │ │ │ - var rowidx = 0; │ │ │ │ - do { │ │ │ │ - var row = this.grid[rowidx]; │ │ │ │ - if (!row) { │ │ │ │ - row = []; │ │ │ │ - this.grid.push(row); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var colidx = 0; │ │ │ │ - do { │ │ │ │ - tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx); │ │ │ │ - var px = startPx.clone(); │ │ │ │ - px.x = px.x + colidx * Math.round(tileSize.w); │ │ │ │ - px.y = px.y + rowidx * Math.round(tileSize.h); │ │ │ │ - var tile = row[colidx]; │ │ │ │ - if (!tile) { │ │ │ │ - tile = this.addTile(tileBounds, px); │ │ │ │ - this.addTileMonitoringHooks(tile); │ │ │ │ - row.push(tile); │ │ │ │ - } else { │ │ │ │ - tile.moveTo(tileBounds, px, false); │ │ │ │ - } │ │ │ │ - var tileCenter = tileBounds.getCenterLonLat(); │ │ │ │ - tileData.push({ │ │ │ │ - tile: tile, │ │ │ │ - distance: Math.pow(tileCenter.lon - center.lon, 2) + │ │ │ │ - Math.pow(tileCenter.lat - center.lat, 2) │ │ │ │ - }); │ │ │ │ - │ │ │ │ - colidx += 1; │ │ │ │ - } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) || │ │ │ │ - colidx < minCols); │ │ │ │ - │ │ │ │ - rowidx += 1; │ │ │ │ - } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) || │ │ │ │ - rowidx < minRows); │ │ │ │ - │ │ │ │ - //shave off exceess rows and colums │ │ │ │ - this.removeExcessTiles(rowidx, colidx); │ │ │ │ - │ │ │ │ - var resolution = this.getServerResolution(); │ │ │ │ - // store the resolution of the grid │ │ │ │ - this.gridResolution = resolution; │ │ │ │ - │ │ │ │ - //now actually draw the tiles │ │ │ │ - tileData.sort(function(a, b) { │ │ │ │ - return a.distance - b.distance; │ │ │ │ - }); │ │ │ │ - for (var i = 0, ii = tileData.length; i < ii; ++i) { │ │ │ │ - tileData[i].tile.draw(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getMaxExtent │ │ │ │ - * Get this layer's maximum extent. (Implemented as a getter for │ │ │ │ - * potential specific implementations in sub-classes.) │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {} │ │ │ │ - */ │ │ │ │ - getMaxExtent: function() { │ │ │ │ - return this.maxExtent; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: addTile │ │ │ │ - * Create a tile, initialize it, and add it to the layer div. │ │ │ │ - * │ │ │ │ - * Parameters │ │ │ │ - * bounds - {} │ │ │ │ - * position - {} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {} The added OpenLayers.Tile │ │ │ │ - */ │ │ │ │ - addTile: function(bounds, position) { │ │ │ │ - var tile = new this.tileClass( │ │ │ │ - this, position, bounds, null, this.tileSize, this.tileOptions │ │ │ │ - ); │ │ │ │ - this.events.triggerEvent("addtile", { │ │ │ │ - tile: tile │ │ │ │ - }); │ │ │ │ - return tile; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: addTileMonitoringHooks │ │ │ │ - * This function takes a tile as input and adds the appropriate hooks to │ │ │ │ - * the tile so that the layer can keep track of the loading tiles. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * tile - {} │ │ │ │ - */ │ │ │ │ - addTileMonitoringHooks: function(tile) { │ │ │ │ - │ │ │ │ - var replacingCls = 'olTileReplacing'; │ │ │ │ - │ │ │ │ - tile.onLoadStart = function() { │ │ │ │ - //if that was first tile then trigger a 'loadstart' on the layer │ │ │ │ - if (this.loading === false) { │ │ │ │ - this.loading = true; │ │ │ │ - this.events.triggerEvent("loadstart"); │ │ │ │ - } │ │ │ │ - this.events.triggerEvent("tileloadstart", { │ │ │ │ - tile: tile │ │ │ │ - }); │ │ │ │ - this.numLoadingTiles++; │ │ │ │ - if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ - OpenLayers.Element.addClass(tile.getTile(), replacingCls); │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - │ │ │ │ - tile.onLoadEnd = function(evt) { │ │ │ │ - this.numLoadingTiles--; │ │ │ │ - var aborted = evt.type === 'unload'; │ │ │ │ - this.events.triggerEvent("tileloaded", { │ │ │ │ - tile: tile, │ │ │ │ - aborted: aborted │ │ │ │ - }); │ │ │ │ - if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ - var tileDiv = tile.getTile(); │ │ │ │ - if (OpenLayers.Element.getStyle(tileDiv, 'display') === 'none') { │ │ │ │ - var bufferTile = document.getElementById(tile.id + '_bb'); │ │ │ │ - if (bufferTile) { │ │ │ │ - bufferTile.parentNode.removeChild(bufferTile); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Element.removeClass(tileDiv, replacingCls); │ │ │ │ - } │ │ │ │ - //if that was the last tile, then trigger a 'loadend' on the layer │ │ │ │ - if (this.numLoadingTiles === 0) { │ │ │ │ - if (this.backBuffer) { │ │ │ │ - if (this.backBuffer.childNodes.length === 0) { │ │ │ │ - // no tiles transitioning, remove immediately │ │ │ │ - this.removeBackBuffer(); │ │ │ │ - } else { │ │ │ │ - // wait until transition has ended or delay has passed │ │ │ │ - this._transitionElement = aborted ? │ │ │ │ - this.div.lastChild : tile.imgDiv; │ │ │ │ - var transitionendEvents = this.transitionendEvents; │ │ │ │ - for (var i = transitionendEvents.length - 1; i >= 0; --i) { │ │ │ │ - OpenLayers.Event.observe(this._transitionElement, │ │ │ │ - transitionendEvents[i], │ │ │ │ - this._removeBackBuffer); │ │ │ │ - } │ │ │ │ - // the removal of the back buffer is delayed to prevent │ │ │ │ - // flash effects due to the animation of tile displaying │ │ │ │ - this.backBufferTimerId = window.setTimeout( │ │ │ │ - this._removeBackBuffer, this.removeBackBufferDelay │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.loading = false; │ │ │ │ - this.events.triggerEvent("loadend"); │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - │ │ │ │ - tile.onLoadError = function() { │ │ │ │ - this.events.triggerEvent("tileerror", { │ │ │ │ - tile: tile │ │ │ │ - }); │ │ │ │ - }; │ │ │ │ - │ │ │ │ - tile.events.on({ │ │ │ │ - "loadstart": tile.onLoadStart, │ │ │ │ - "loadend": tile.onLoadEnd, │ │ │ │ - "unload": tile.onLoadEnd, │ │ │ │ - "loaderror": tile.onLoadError, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: removeTileMonitoringHooks │ │ │ │ - * This function takes a tile as input and removes the tile hooks │ │ │ │ - * that were added in addTileMonitoringHooks() │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * tile - {} │ │ │ │ - */ │ │ │ │ - removeTileMonitoringHooks: function(tile) { │ │ │ │ - tile.unload(); │ │ │ │ - tile.events.un({ │ │ │ │ - "loadstart": tile.onLoadStart, │ │ │ │ - "loadend": tile.onLoadEnd, │ │ │ │ - "unload": tile.onLoadEnd, │ │ │ │ - "loaderror": tile.onLoadError, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: moveGriddedTiles │ │ │ │ - */ │ │ │ │ - moveGriddedTiles: function() { │ │ │ │ - var buffer = this.buffer + 1; │ │ │ │ - while (true) { │ │ │ │ - var tlTile = this.grid[0][0]; │ │ │ │ - var tlViewPort = { │ │ │ │ - x: tlTile.position.x + │ │ │ │ - this.map.layerContainerOriginPx.x, │ │ │ │ - y: tlTile.position.y + │ │ │ │ - this.map.layerContainerOriginPx.y │ │ │ │ - }; │ │ │ │ - var ratio = this.getServerResolution() / this.map.getResolution(); │ │ │ │ - var tileSize = { │ │ │ │ - w: Math.round(this.tileSize.w * ratio), │ │ │ │ - h: Math.round(this.tileSize.h * ratio) │ │ │ │ - }; │ │ │ │ - if (tlViewPort.x > -tileSize.w * (buffer - 1)) { │ │ │ │ - this.shiftColumn(true, tileSize); │ │ │ │ - } else if (tlViewPort.x < -tileSize.w * buffer) { │ │ │ │ - this.shiftColumn(false, tileSize); │ │ │ │ - } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) { │ │ │ │ - this.shiftRow(true, tileSize); │ │ │ │ - } else if (tlViewPort.y < -tileSize.h * buffer) { │ │ │ │ - this.shiftRow(false, tileSize); │ │ │ │ - } else { │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: shiftRow │ │ │ │ - * Shifty grid work │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * prepend - {Boolean} if true, prepend to beginning. │ │ │ │ - * if false, then append to end │ │ │ │ - * tileSize - {Object} rendered tile size; object with w and h properties │ │ │ │ - */ │ │ │ │ - shiftRow: function(prepend, tileSize) { │ │ │ │ - var grid = this.grid; │ │ │ │ - var rowIndex = prepend ? 0 : (grid.length - 1); │ │ │ │ - var sign = prepend ? -1 : 1; │ │ │ │ - var rowSign = this.rowSign; │ │ │ │ - var tileLayout = this.gridLayout; │ │ │ │ - tileLayout.startrow += sign * rowSign; │ │ │ │ - │ │ │ │ - var modelRow = grid[rowIndex]; │ │ │ │ - var row = grid[prepend ? 'pop' : 'shift'](); │ │ │ │ - for (var i = 0, len = row.length; i < len; i++) { │ │ │ │ - var tile = row[i]; │ │ │ │ - var position = modelRow[i].position.clone(); │ │ │ │ - position.y += tileSize.h * sign; │ │ │ │ - tile.moveTo(this.getTileBoundsForGridIndex(rowIndex, i), position); │ │ │ │ - } │ │ │ │ - grid[prepend ? 'unshift' : 'push'](row); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: shiftColumn │ │ │ │ - * Shift grid work in the other dimension │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * prepend - {Boolean} if true, prepend to beginning. │ │ │ │ - * if false, then append to end │ │ │ │ - * tileSize - {Object} rendered tile size; object with w and h properties │ │ │ │ - */ │ │ │ │ - shiftColumn: function(prepend, tileSize) { │ │ │ │ - var grid = this.grid; │ │ │ │ - var colIndex = prepend ? 0 : (grid[0].length - 1); │ │ │ │ - var sign = prepend ? -1 : 1; │ │ │ │ - var tileLayout = this.gridLayout; │ │ │ │ - tileLayout.startcol += sign; │ │ │ │ - │ │ │ │ - for (var i = 0, len = grid.length; i < len; i++) { │ │ │ │ - var row = grid[i]; │ │ │ │ - var position = row[colIndex].position.clone(); │ │ │ │ - var tile = row[prepend ? 'pop' : 'shift'](); │ │ │ │ - position.x += tileSize.w * sign; │ │ │ │ - tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position); │ │ │ │ - row[prepend ? 'unshift' : 'push'](tile); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: removeExcessTiles │ │ │ │ - * When the size of the map or the buffer changes, we may need to │ │ │ │ - * remove some excess rows and columns. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * rows - {Integer} Maximum number of rows we want our grid to have. │ │ │ │ - * columns - {Integer} Maximum number of columns we want our grid to have. │ │ │ │ - */ │ │ │ │ - removeExcessTiles: function(rows, columns) { │ │ │ │ - var i, l; │ │ │ │ - │ │ │ │ - // remove extra rows │ │ │ │ - while (this.grid.length > rows) { │ │ │ │ - var row = this.grid.pop(); │ │ │ │ - for (i = 0, l = row.length; i < l; i++) { │ │ │ │ - var tile = row[i]; │ │ │ │ - this.destroyTile(tile); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - // remove extra columns │ │ │ │ - for (i = 0, l = this.grid.length; i < l; i++) { │ │ │ │ - while (this.grid[i].length > columns) { │ │ │ │ - var row = this.grid[i]; │ │ │ │ - var tile = row.pop(); │ │ │ │ - this.destroyTile(tile); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: onMapResize │ │ │ │ - * For singleTile layers, this will set a new tile size according to the │ │ │ │ - * dimensions of the map pane. │ │ │ │ - */ │ │ │ │ - onMapResize: function() { │ │ │ │ - if (this.singleTile) { │ │ │ │ - this.clearGrid(); │ │ │ │ - this.setTileSize(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getTileBounds │ │ │ │ - * Returns The tile bounds for a layer given a pixel location. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * viewPortPx - {} The location in the viewport. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {} Bounds of the tile at the given pixel location. │ │ │ │ - */ │ │ │ │ - getTileBounds: function(viewPortPx) { │ │ │ │ - var maxExtent = this.maxExtent; │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var tileMapWidth = resolution * this.tileSize.w; │ │ │ │ - var tileMapHeight = resolution * this.tileSize.h; │ │ │ │ - var mapPoint = this.getLonLatFromViewPortPx(viewPortPx); │ │ │ │ - var tileLeft = maxExtent.left + (tileMapWidth * │ │ │ │ - Math.floor((mapPoint.lon - │ │ │ │ - maxExtent.left) / │ │ │ │ - tileMapWidth)); │ │ │ │ - var tileBottom = maxExtent.bottom + (tileMapHeight * │ │ │ │ - Math.floor((mapPoint.lat - │ │ │ │ - maxExtent.bottom) / │ │ │ │ - tileMapHeight)); │ │ │ │ - return new OpenLayers.Bounds(tileLeft, tileBottom, │ │ │ │ - tileLeft + tileMapWidth, │ │ │ │ - tileBottom + tileMapHeight); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Grid" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/TileManager.js │ │ │ │ - ====================================================================== */ │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/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. │ │ │ │ + * Class: OpenLayers.Control │ │ │ │ + * Controls affect the display or behavior of the map. They allow everything │ │ │ │ + * from panning and zooming to displaying a scale indicator. Controls by │ │ │ │ + * default are added to the map they are contained within however it is │ │ │ │ + * possible to add a control to an external div by passing the div in the │ │ │ │ + * options parameter. │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * The following example shows how to add many of the common controls │ │ │ │ + * to a map. │ │ │ │ + * │ │ │ │ + * > var map = new OpenLayers.Map('map', { controls: [] }); │ │ │ │ + * > │ │ │ │ + * > map.addControl(new OpenLayers.Control.PanZoomBar()); │ │ │ │ + * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false})); │ │ │ │ + * > map.addControl(new OpenLayers.Control.Permalink()); │ │ │ │ + * > map.addControl(new OpenLayers.Control.Permalink('permalink')); │ │ │ │ + * > map.addControl(new OpenLayers.Control.MousePosition()); │ │ │ │ + * > map.addControl(new OpenLayers.Control.OverviewMap()); │ │ │ │ + * > map.addControl(new OpenLayers.Control.KeyboardDefaults()); │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * The next code fragment is a quick example of how to intercept │ │ │ │ + * shift-mouse click to display the extent of the bounding box │ │ │ │ + * dragged out by the user. Usually controls are not created │ │ │ │ + * in exactly this manner. See the source for a more complete │ │ │ │ + * example: │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * > var control = new OpenLayers.Control(); │ │ │ │ + * > OpenLayers.Util.extend(control, { │ │ │ │ + * > draw: function () { │ │ │ │ + * > // this Handler.Box will intercept the shift-mousedown │ │ │ │ + * > // before Control.MouseDefault gets to see it │ │ │ │ + * > this.box = new OpenLayers.Handler.Box( control, │ │ │ │ + * > {"done": this.notice}, │ │ │ │ + * > {keyMask: OpenLayers.Handler.MOD_SHIFT}); │ │ │ │ + * > this.box.activate(); │ │ │ │ + * > }, │ │ │ │ + * > │ │ │ │ + * > notice: function (bounds) { │ │ │ │ + * > OpenLayers.Console.userError(bounds); │ │ │ │ + * > } │ │ │ │ + * > }); │ │ │ │ + * > map.addControl(control); │ │ │ │ + * │ │ │ │ */ │ │ │ │ -OpenLayers.TileManager = OpenLayers.Class({ │ │ │ │ +OpenLayers.Control = OpenLayers.Class({ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: cacheSize │ │ │ │ - * {Number} Number of image elements to keep referenced in this instance's │ │ │ │ - * cache for fast reuse. Default is 256. │ │ │ │ + /** │ │ │ │ + * Property: id │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - cacheSize: 256, │ │ │ │ + id: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: tilesPerFrame │ │ │ │ - * {Number} Number of queued tiles to load per frame (see ). │ │ │ │ - * Default is 2. │ │ │ │ + /** │ │ │ │ + * Property: map │ │ │ │ + * {} this gets set in the addControl() function in │ │ │ │ + * OpenLayers.Map │ │ │ │ */ │ │ │ │ - tilesPerFrame: 2, │ │ │ │ + map: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: frameDelay │ │ │ │ - * {Number} Delay between tile loading frames (see ) in │ │ │ │ - * milliseconds. Default is 16. │ │ │ │ + /** │ │ │ │ + * APIProperty: div │ │ │ │ + * {DOMElement} The element that contains the control, if not present the │ │ │ │ + * control is placed inside the map. │ │ │ │ */ │ │ │ │ - frameDelay: 16, │ │ │ │ + div: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: moveDelay │ │ │ │ - * {Number} Delay in milliseconds after a map's move event before loading │ │ │ │ - * tiles. Default is 100. │ │ │ │ + /** │ │ │ │ + * APIProperty: type │ │ │ │ + * {Number} Controls can have a 'type'. The type determines the type of │ │ │ │ + * interactions which are possible with them when they are placed in an │ │ │ │ + * . │ │ │ │ */ │ │ │ │ - moveDelay: 100, │ │ │ │ + type: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomDelay │ │ │ │ - * {Number} Delay in milliseconds after a map's zoomend event before loading │ │ │ │ - * tiles. Default is 200. │ │ │ │ + /** │ │ │ │ + * Property: allowSelection │ │ │ │ + * {Boolean} By default, controls do not allow selection, because │ │ │ │ + * it may interfere with map dragging. If this is true, OpenLayers │ │ │ │ + * will not prevent selection of the control. │ │ │ │ + * Default is false. │ │ │ │ */ │ │ │ │ - zoomDelay: 200, │ │ │ │ + allowSelection: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: maps │ │ │ │ - * {Array()} The maps to manage tiles on. │ │ │ │ + /** │ │ │ │ + * Property: displayClass │ │ │ │ + * {string} This property is used for CSS related to the drawing of the │ │ │ │ + * Control. │ │ │ │ */ │ │ │ │ - maps: null, │ │ │ │ + displayClass: "", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: tileQueueId │ │ │ │ - * {Object} The ids of the loop, keyed by map id. │ │ │ │ + * APIProperty: title │ │ │ │ + * {string} This property is used for showing a tooltip over the │ │ │ │ + * Control. │ │ │ │ */ │ │ │ │ - tileQueueId: null, │ │ │ │ + title: "", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: tileQueue │ │ │ │ - * {Object(Array())} Tiles queued for drawing, keyed by │ │ │ │ - * map id. │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * false. │ │ │ │ */ │ │ │ │ - tileQueue: null, │ │ │ │ + autoActivate: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: tileCache │ │ │ │ - * {Object} Cached image elements, keyed by URL. │ │ │ │ + /** │ │ │ │ + * APIProperty: active │ │ │ │ + * {Boolean} The control is active (read-only). Use and │ │ │ │ + * to change control state. │ │ │ │ */ │ │ │ │ - tileCache: null, │ │ │ │ + active: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: tileCacheIndex │ │ │ │ - * {Array(String)} URLs of cached tiles. First entry is the least recently │ │ │ │ - * used. │ │ │ │ + * Property: handlerOptions │ │ │ │ + * {Object} Used to set non-default properties on the control's handler │ │ │ │ */ │ │ │ │ - tileCacheIndex: null, │ │ │ │ + handlerOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.TileManager │ │ │ │ - * Constructor for a new instance. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Configuration for this instance. │ │ │ │ + * Property: handler │ │ │ │ + * {} null │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.maps = []; │ │ │ │ - this.tileQueueId = {}; │ │ │ │ - this.tileQueue = {}; │ │ │ │ - this.tileCache = {}; │ │ │ │ - this.tileCacheIndex = []; │ │ │ │ - }, │ │ │ │ + handler: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: addMap │ │ │ │ - * Binds this instance to a map │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {} │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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 │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ + eventListeners: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: removeMap │ │ │ │ - * Unbinds this instance from a map │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * map - {} │ │ │ │ - */ │ │ │ │ - removeMap: function(map) { │ │ │ │ - if (this._destroyed || !OpenLayers.Layer.Grid) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - window.clearTimeout(this.tileQueueId[map.id]); │ │ │ │ - if (map.layers) { │ │ │ │ - for (var i = 0, ii = map.layers.length; i < ii; ++i) { │ │ │ │ - this.removeLayer({ │ │ │ │ - layer: map.layers[i] │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (map.events) { │ │ │ │ - map.events.un({ │ │ │ │ - move: this.move, │ │ │ │ - zoomend: this.zoomEnd, │ │ │ │ - changelayer: this.changeLayer, │ │ │ │ - addlayer: this.addLayer, │ │ │ │ - preremovelayer: this.removeLayer, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - delete this.tileQueue[map.id]; │ │ │ │ - delete this.tileQueueId[map.id]; │ │ │ │ - OpenLayers.Util.removeItem(this.maps, map); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: move │ │ │ │ - * Handles the map's move event │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} Listener argument │ │ │ │ - */ │ │ │ │ - move: function(evt) { │ │ │ │ - this.updateTimeout(evt.object, this.moveDelay, true); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: zoomEnd │ │ │ │ - * Handles the map's zoomEnd event │ │ │ │ + * Listeners will be called with a reference to an event object. The │ │ │ │ + * properties of this event depends on exactly what happened. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} Listener argument │ │ │ │ - */ │ │ │ │ - zoomEnd: function(evt) { │ │ │ │ - this.updateTimeout(evt.object, this.zoomDelay); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: changeLayer │ │ │ │ - * Handles the map's changeLayer event │ │ │ │ + * All event objects have at least the following properties: │ │ │ │ + * object - {Object} A reference to control.events.object (a reference │ │ │ │ + * to the control). │ │ │ │ + * element - {DOMElement} A reference to control.events.element (which │ │ │ │ + * will be null unless documented otherwise). │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} Listener argument │ │ │ │ + * Supported map event types: │ │ │ │ + * activate - Triggered when activated. │ │ │ │ + * deactivate - Triggered when deactivated. │ │ │ │ */ │ │ │ │ - changeLayer: function(evt) { │ │ │ │ - if (evt.property === 'visibility' || evt.property === 'params') { │ │ │ │ - this.updateTimeout(evt.object, 0); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + events: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: addLayer │ │ │ │ - * Handles the map's addlayer event │ │ │ │ + * Constructor: OpenLayers.Control │ │ │ │ + * Create an OpenLayers Control. The options passed as a parameter │ │ │ │ + * directly extend the control. For example passing the following: │ │ │ │ + * │ │ │ │ + * > var control = new OpenLayers.Control({div: myDiv}); │ │ │ │ * │ │ │ │ + * Overrides the default div attribute value of null. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} The listener argument │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - 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 │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + initialize: function(options) { │ │ │ │ + // We do this before the extend so that instances can override │ │ │ │ + // className in options. │ │ │ │ + this.displayClass = │ │ │ │ + this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, ""); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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 │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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. │ │ │ │ - */ │ │ │ │ - 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 │ │ │ │ - ); │ │ │ │ + this.events = new OpenLayers.Events(this); │ │ │ │ + if (this.eventListeners instanceof Object) { │ │ │ │ + this.events.on(this.eventListeners); │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * 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 │ │ │ │ - }); │ │ │ │ + if (this.id == null) { │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: unloadTile │ │ │ │ - * Listener for the tile's unload event │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} The listener argument │ │ │ │ - */ │ │ │ │ - unloadTile: function(evt) { │ │ │ │ - var tile = evt.object; │ │ │ │ - tile.events.un({ │ │ │ │ - beforedraw: this.queueTileDraw, │ │ │ │ - beforeload: this.manageTileCache, │ │ │ │ - loadend: this.addToCache, │ │ │ │ - unload: this.unloadTile, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - OpenLayers.Util.removeItem(this.tileQueue[tile.layer.map.id], tile); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: queueTileDraw │ │ │ │ - * Adds a tile to the queue that will draw it. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} Listener argument of the tile's beforedraw event │ │ │ │ + * Method: destroy │ │ │ │ + * The destroy method is used to perform any clean up before the control │ │ │ │ + * is dereferenced. Typically this is where event listeners are removed │ │ │ │ + * to prevent memory leaks. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + destroy: function() { │ │ │ │ + if (this.events) { │ │ │ │ + if (this.eventListeners) { │ │ │ │ + this.events.un(this.eventListeners); │ │ │ │ } │ │ │ │ - queued = true; │ │ │ │ - } │ │ │ │ - return !queued; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: drawTilesFromQueue │ │ │ │ - * Draws tiles from the tileQueue, and unqueues the tiles │ │ │ │ - */ │ │ │ │ - drawTilesFromQueue: function(map) { │ │ │ │ - var tileQueue = this.tileQueue[map.id]; │ │ │ │ - var limit = this.tilesPerFrame; │ │ │ │ - var animating = map.zoomTween && map.zoomTween.playing; │ │ │ │ - while (!animating && tileQueue.length && limit) { │ │ │ │ - tileQueue.shift().draw(true); │ │ │ │ - --limit; │ │ │ │ + this.events.destroy(); │ │ │ │ + this.events = null; │ │ │ │ } │ │ │ │ - }, │ │ │ │ + this.eventListeners = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: manageTileCache │ │ │ │ - * Adds, updates, removes and fetches cache entries. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} Listener argument of the tile's beforeload event │ │ │ │ - */ │ │ │ │ - manageTileCache: function(evt) { │ │ │ │ - var tile = evt.object; │ │ │ │ - var img = this.tileCache[tile.url]; │ │ │ │ - if (img) { │ │ │ │ - // if image is on its layer's backbuffer, remove it from backbuffer │ │ │ │ - if (img.parentNode && │ │ │ │ - OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) { │ │ │ │ - img.parentNode.removeChild(img); │ │ │ │ - img.id = null; │ │ │ │ - } │ │ │ │ - // only use image from cache if it is not on a layer already │ │ │ │ - if (!img.parentNode) { │ │ │ │ - img.style.visibility = 'hidden'; │ │ │ │ - img.style.opacity = 0; │ │ │ │ - tile.setImage(img); │ │ │ │ - // LRU - move tile to the end of the array to mark it as the most │ │ │ │ - // recently used │ │ │ │ - OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url); │ │ │ │ - this.tileCacheIndex.push(tile.url); │ │ │ │ - } │ │ │ │ + // eliminate circular references │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.destroy(); │ │ │ │ + this.handler = null; │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: addToCache │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} Listener argument for the tile's loadend event │ │ │ │ - */ │ │ │ │ - addToCache: function(evt) { │ │ │ │ - var tile = evt.object; │ │ │ │ - if (!this.tileCache[tile.url]) { │ │ │ │ - if (!OpenLayers.Element.hasClass(tile.imgDiv, 'olImageLoadError')) { │ │ │ │ - if (this.tileCacheIndex.length >= this.cacheSize) { │ │ │ │ - delete this.tileCache[this.tileCacheIndex[0]]; │ │ │ │ - this.tileCacheIndex.shift(); │ │ │ │ + if (this.handlers) { │ │ │ │ + for (var key in this.handlers) { │ │ │ │ + if (this.handlers.hasOwnProperty(key) && │ │ │ │ + typeof this.handlers[key].destroy == "function") { │ │ │ │ + this.handlers[key].destroy(); │ │ │ │ } │ │ │ │ - this.tileCache[tile.url] = tile.imgDiv; │ │ │ │ - this.tileCacheIndex.push(tile.url); │ │ │ │ } │ │ │ │ + this.handlers = null; │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * 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); │ │ │ │ - } │ │ │ │ + if (this.map) { │ │ │ │ + this.map.removeControl(this); │ │ │ │ + this.map = null; │ │ │ │ } │ │ │ │ + this.div = null; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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/Strategy.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Strategy │ │ │ │ - * Abstract vector layer strategy class. Not to be instantiated directly. Use │ │ │ │ - * one of the strategy subclasses instead. │ │ │ │ - */ │ │ │ │ -OpenLayers.Strategy = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: layer │ │ │ │ - * {} The layer this strategy belongs to. │ │ │ │ - */ │ │ │ │ - layer: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: options │ │ │ │ - * {Object} Any options sent to the constructor. │ │ │ │ - */ │ │ │ │ - options: null, │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Property: active │ │ │ │ - * {Boolean} The control is active. │ │ │ │ - */ │ │ │ │ - active: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: autoActivate │ │ │ │ - * {Boolean} The creator of the strategy can set autoActivate to false │ │ │ │ - * to fully control when the protocol is activated and deactivated. │ │ │ │ - * Defaults to true. │ │ │ │ - */ │ │ │ │ - autoActivate: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: autoDestroy │ │ │ │ - * {Boolean} The creator of the strategy can set autoDestroy to false │ │ │ │ - * to fully control when the strategy is destroyed. Defaults to │ │ │ │ - * true. │ │ │ │ - */ │ │ │ │ - autoDestroy: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Strategy │ │ │ │ - * Abstract class for vector strategies. Create instances of a subclass. │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the control. This is done through an accessor │ │ │ │ + * so that subclasses can override this and take special action once │ │ │ │ + * they have their map variable set. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.options = options; │ │ │ │ - // set the active property here, so that user cannot override it │ │ │ │ - this.active = false; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Clean up the strategy. │ │ │ │ + * map - {} │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - this.layer = null; │ │ │ │ - this.options = null; │ │ │ │ + setMap: function(map) { │ │ │ │ + this.map = map; │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.setMap(map); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setLayer │ │ │ │ - * Called to set the property. │ │ │ │ + * Method: draw │ │ │ │ + * The draw method is called when the control is ready to be displayed │ │ │ │ + * on the page. If a div has not been created one is created. Controls │ │ │ │ + * with a visual component will almost always want to override this method │ │ │ │ + * to customize the look of control. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * layer - {} │ │ │ │ - */ │ │ │ │ - setLayer: function(layer) { │ │ │ │ - this.layer = layer; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: activate │ │ │ │ - * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ + * px - {} The top-left pixel position of the control │ │ │ │ + * or null. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} True if the strategy was successfully activated or false if │ │ │ │ - * the strategy was already active. │ │ │ │ + * {DOMElement} A reference to the DIV DOMElement containing the control │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - if (!this.active) { │ │ │ │ - this.active = true; │ │ │ │ - return true; │ │ │ │ + draw: function(px) { │ │ │ │ + if (this.div == null) { │ │ │ │ + this.div = OpenLayers.Util.createDiv(this.id); │ │ │ │ + this.div.className = this.displayClass; │ │ │ │ + if (!this.allowSelection) { │ │ │ │ + this.div.className += " olControlNoSelect"; │ │ │ │ + this.div.setAttribute("unselectable", "on", 0); │ │ │ │ + this.div.onselectstart = OpenLayers.Function.False; │ │ │ │ + } │ │ │ │ + if (this.title != "") { │ │ │ │ + this.div.title = this.title; │ │ │ │ + } │ │ │ │ } │ │ │ │ - return false; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ - * tear-down. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} True if the strategy was successfully deactivated or false if │ │ │ │ - * the strategy was already inactive. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.active = false; │ │ │ │ - return true; │ │ │ │ + if (px != null) { │ │ │ │ + this.position = px.clone(); │ │ │ │ } │ │ │ │ - return false; │ │ │ │ + this.moveTo(this.position); │ │ │ │ + return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Handler.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Handler │ │ │ │ - * Base class to construct a higher-level handler for event sequences. All │ │ │ │ - * handlers have activate and deactivate methods. In addition, they have │ │ │ │ - * methods named like browser events. When a handler is activated, any │ │ │ │ - * additional methods named like a browser event is registered as a │ │ │ │ - * listener for the corresponding event. When a handler is deactivated, │ │ │ │ - * those same methods are unregistered as event listeners. │ │ │ │ - * │ │ │ │ - * Handlers also typically have a callbacks object with keys named like │ │ │ │ - * the abstracted events or event sequences that they are in charge of │ │ │ │ - * handling. The controls that wrap handlers define the methods that │ │ │ │ - * correspond to these abstract events - so instead of listening for │ │ │ │ - * individual browser events, they only listen for the abstract events │ │ │ │ - * defined by the handler. │ │ │ │ - * │ │ │ │ - * Handlers are created by controls, which ultimately have the responsibility │ │ │ │ - * of making changes to the the state of the application. Handlers │ │ │ │ - * themselves may make temporary changes, but in general are expected to │ │ │ │ - * return the application in the same state that they found it. │ │ │ │ - */ │ │ │ │ -OpenLayers.Handler = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: id │ │ │ │ - * {String} │ │ │ │ - */ │ │ │ │ - id: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: control │ │ │ │ - * {}. The control that initialized this handler. The │ │ │ │ - * control is assumed to have a valid map property - that map is used │ │ │ │ - * in the handler's own setMap method. │ │ │ │ - */ │ │ │ │ - control: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: map │ │ │ │ - * {} │ │ │ │ - */ │ │ │ │ - map: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: keyMask │ │ │ │ - * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler │ │ │ │ - * constants to construct a keyMask. The keyMask is used by │ │ │ │ - * . If the keyMask matches the combination of keys │ │ │ │ - * down on an event, checkModifiers returns true. │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * // handler only responds if the Shift key is down │ │ │ │ - * handler.keyMask = OpenLayers.Handler.MOD_SHIFT; │ │ │ │ - * │ │ │ │ - * // handler only responds if Ctrl-Shift is down │ │ │ │ - * handler.keyMask = OpenLayers.Handler.MOD_SHIFT | │ │ │ │ - * OpenLayers.Handler.MOD_CTRL; │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ - keyMask: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: active │ │ │ │ - * {Boolean} │ │ │ │ - */ │ │ │ │ - active: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: evt │ │ │ │ - * {Event} This property references the last event handled by the handler. │ │ │ │ - * Note that this property is not part of the stable API. Use of the │ │ │ │ - * evt property should be restricted to controls in the library │ │ │ │ - * or other applications that are willing to update with changes to │ │ │ │ - * the OpenLayers code. │ │ │ │ - */ │ │ │ │ - evt: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: touch │ │ │ │ - * {Boolean} Indicates the support of touch events. When touch events are │ │ │ │ - * started touch will be true and all mouse related listeners will do │ │ │ │ - * nothing. │ │ │ │ - */ │ │ │ │ - touch: false, │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Handler │ │ │ │ - * Construct a handler. │ │ │ │ + * Method: moveTo │ │ │ │ + * Sets the left and top style attributes to the passed in pixel │ │ │ │ + * coordinates. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * control - {} The control that initialized this │ │ │ │ - * handler. The control is assumed to have a valid map property; that │ │ │ │ - * map is used in the handler's own setMap method. If a map property │ │ │ │ - * is present in the options argument it will be used instead. │ │ │ │ - * callbacks - {Object} An object whose properties correspond to abstracted │ │ │ │ - * events or sequences of browser events. The values for these │ │ │ │ - * properties are functions defined by the control that get called by │ │ │ │ - * the handler. │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * the handler. │ │ │ │ - */ │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.control = control; │ │ │ │ - this.callbacks = callbacks; │ │ │ │ - │ │ │ │ - var map = this.map || control.map; │ │ │ │ - if (map) { │ │ │ │ - this.setMap(map); │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - */ │ │ │ │ - setMap: function(map) { │ │ │ │ - this.map = map; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: checkModifiers │ │ │ │ - * Check the keyMask on the handler. If no is set, this always │ │ │ │ - * returns true. If a is set and it matches the combination │ │ │ │ - * of keys down on an event, this returns true. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The keyMask matches the keys down on an event. │ │ │ │ + * px - {} │ │ │ │ */ │ │ │ │ - checkModifiers: function(evt) { │ │ │ │ - if (this.keyMask == null) { │ │ │ │ - return true; │ │ │ │ + moveTo: function(px) { │ │ │ │ + if ((px != null) && (this.div != null)) { │ │ │ │ + this.div.style.left = px.x + "px"; │ │ │ │ + this.div.style.top = px.y + "px"; │ │ │ │ } │ │ │ │ - /* calculate the keyboard modifier mask for this event */ │ │ │ │ - var keyModifiers = │ │ │ │ - (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) | │ │ │ │ - (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) | │ │ │ │ - (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) | │ │ │ │ - (evt.metaKey ? OpenLayers.Handler.MOD_META : 0); │ │ │ │ - │ │ │ │ - /* if it differs from the handler object's key mask, │ │ │ │ - bail out of the event handler */ │ │ │ │ - return (keyModifiers == this.keyMask); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ * APIMethod: activate │ │ │ │ - * Turn on the handler. Returns false if the handler was already active. │ │ │ │ + * Explicitly activates a control and it's associated │ │ │ │ + * handler if one has been set. Controls can be │ │ │ │ + * deactivated by calling the deactivate() method. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The handler was activated. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} True if the control was successfully activated or │ │ │ │ + * false if the control was already active. │ │ │ │ */ │ │ │ │ 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]]); │ │ │ │ - } │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.activate(); │ │ │ │ } │ │ │ │ this.active = true; │ │ │ │ + if (this.map) { │ │ │ │ + OpenLayers.Element.addClass( │ │ │ │ + this.map.viewPortDiv, │ │ │ │ + this.displayClass.replace(/ /g, "") + "Active" │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("activate"); │ │ │ │ return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ * APIMethod: deactivate │ │ │ │ - * Turn off the handler. Returns false if the handler was already inactive. │ │ │ │ + * Deactivates a control and it's associated handler if any. The exact │ │ │ │ + * effect of this depends on the control itself. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The handler was deactivated. │ │ │ │ + * {Boolean} True if the control was effectively deactivated or false │ │ │ │ + * if the control was already inactive. │ │ │ │ */ │ │ │ │ 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]]); │ │ │ │ + if (this.active) { │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.deactivate(); │ │ │ │ } │ │ │ │ - } │ │ │ │ - this.touch = false; │ │ │ │ - this.active = false; │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: startTouch │ │ │ │ - * Start touch events, this method must be called by subclasses in │ │ │ │ - * "touchstart" method. When touch events are started will be │ │ │ │ - * true and all mouse related listeners will do nothing. │ │ │ │ - */ │ │ │ │ - startTouch: function() { │ │ │ │ - if (!this.touch) { │ │ │ │ - this.touch = true; │ │ │ │ - var events = [ │ │ │ │ - "mousedown", "mouseup", "mousemove", "click", "dblclick", │ │ │ │ - "mouseout" │ │ │ │ - ]; │ │ │ │ - for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ - if (this[events[i]]) { │ │ │ │ - this.unregister(events[i], this[events[i]]); │ │ │ │ - } │ │ │ │ + this.active = false; │ │ │ │ + if (this.map) { │ │ │ │ + OpenLayers.Element.removeClass( │ │ │ │ + this.map.viewPortDiv, │ │ │ │ + this.displayClass.replace(/ /g, "") + "Active" │ │ │ │ + ); │ │ │ │ } │ │ │ │ + this.events.triggerEvent("deactivate"); │ │ │ │ + return true; │ │ │ │ } │ │ │ │ + return false; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: callback │ │ │ │ - * Trigger the control's named callback with the given arguments │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} The key for the callback that is one of the properties │ │ │ │ - * of the handler's callbacks object. │ │ │ │ - * args - {Array(*)} An array of arguments (any type) with which to call │ │ │ │ - * the callback (defined by the control). │ │ │ │ - */ │ │ │ │ - callback: function(name, args) { │ │ │ │ - if (name && this.callbacks[name]) { │ │ │ │ - this.callbacks[name].apply(this.control, args); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: register │ │ │ │ - * register an event on the map │ │ │ │ - */ │ │ │ │ - register: function(name, method) { │ │ │ │ - // TODO: deal with registerPriority in 3.0 │ │ │ │ - this.map.events.registerPriority(name, this, method); │ │ │ │ - this.map.events.registerPriority(name, this, this.setEvent); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: unregister │ │ │ │ - * unregister an event from the map │ │ │ │ - */ │ │ │ │ - unregister: function(name, method) { │ │ │ │ - this.map.events.unregister(name, this, method); │ │ │ │ - this.map.events.unregister(name, this, this.setEvent); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setEvent │ │ │ │ - * With each registered browser event, the handler sets its own evt │ │ │ │ - * property. This property can be accessed by controls if needed │ │ │ │ - * to get more information about the event that the handler is │ │ │ │ - * processing. │ │ │ │ - * │ │ │ │ - * This allows modifier keys on the event to be checked (alt, shift, ctrl, │ │ │ │ - * and meta cannot be checked with the keyboard handler). For a │ │ │ │ - * control to determine which modifier keys are associated with the │ │ │ │ - * event that a handler is currently processing, it should access │ │ │ │ - * (code)handler.evt.altKey || handler.evt.shiftKey || │ │ │ │ - * handler.evt.ctrlKey || handler.evt.metaKey(end). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} The browser event. │ │ │ │ - */ │ │ │ │ - setEvent: function(evt) { │ │ │ │ - this.evt = evt; │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * Deconstruct the handler. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - // unregister event listeners │ │ │ │ - this.deactivate(); │ │ │ │ - // eliminate circular references │ │ │ │ - this.control = this.map = null; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Handler" │ │ │ │ + CLASS_NAME: "OpenLayers.Control" │ │ │ │ }); │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Constant: OpenLayers.Control.TYPE_BUTTON │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.MOD_CTRL = 2; │ │ │ │ +OpenLayers.Control.TYPE_BUTTON = 1; │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: OpenLayers.Handler.MOD_ALT │ │ │ │ - * If set as the , returns false if Alt is down. │ │ │ │ + * Constant: OpenLayers.Control.TYPE_TOGGLE │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.MOD_ALT = 4; │ │ │ │ +OpenLayers.Control.TYPE_TOGGLE = 2; │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: OpenLayers.Handler.MOD_META │ │ │ │ - * If set as the , returns false if Cmd is down. │ │ │ │ + * Constant: OpenLayers.Control.TYPE_TOOL │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.MOD_META = 8; │ │ │ │ - │ │ │ │ - │ │ │ │ +OpenLayers.Control.TYPE_TOOL = 3; │ │ │ │ /* ====================================================================== │ │ │ │ OpenLayers/Geometry.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ @@ -16780,768 +16747,14 @@ │ │ │ │ distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2), │ │ │ │ x: x, │ │ │ │ y: y, │ │ │ │ along: along │ │ │ │ }; │ │ │ │ }; │ │ │ │ /* ====================================================================== │ │ │ │ - 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/Control.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control │ │ │ │ - * Controls affect the display or behavior of the map. They allow everything │ │ │ │ - * from panning and zooming to displaying a scale indicator. Controls by │ │ │ │ - * default are added to the map they are contained within however it is │ │ │ │ - * possible to add a control to an external div by passing the div in the │ │ │ │ - * options parameter. │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * The following example shows how to add many of the common controls │ │ │ │ - * to a map. │ │ │ │ - * │ │ │ │ - * > var map = new OpenLayers.Map('map', { controls: [] }); │ │ │ │ - * > │ │ │ │ - * > map.addControl(new OpenLayers.Control.PanZoomBar()); │ │ │ │ - * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false})); │ │ │ │ - * > map.addControl(new OpenLayers.Control.Permalink()); │ │ │ │ - * > map.addControl(new OpenLayers.Control.Permalink('permalink')); │ │ │ │ - * > map.addControl(new OpenLayers.Control.MousePosition()); │ │ │ │ - * > map.addControl(new OpenLayers.Control.OverviewMap()); │ │ │ │ - * > map.addControl(new OpenLayers.Control.KeyboardDefaults()); │ │ │ │ - * │ │ │ │ - * The next code fragment is a quick example of how to intercept │ │ │ │ - * shift-mouse click to display the extent of the bounding box │ │ │ │ - * dragged out by the user. Usually controls are not created │ │ │ │ - * in exactly this manner. See the source for a more complete │ │ │ │ - * example: │ │ │ │ - * │ │ │ │ - * > var control = new OpenLayers.Control(); │ │ │ │ - * > OpenLayers.Util.extend(control, { │ │ │ │ - * > draw: function () { │ │ │ │ - * > // this Handler.Box will intercept the shift-mousedown │ │ │ │ - * > // before Control.MouseDefault gets to see it │ │ │ │ - * > this.box = new OpenLayers.Handler.Box( control, │ │ │ │ - * > {"done": this.notice}, │ │ │ │ - * > {keyMask: OpenLayers.Handler.MOD_SHIFT}); │ │ │ │ - * > this.box.activate(); │ │ │ │ - * > }, │ │ │ │ - * > │ │ │ │ - * > notice: function (bounds) { │ │ │ │ - * > OpenLayers.Console.userError(bounds); │ │ │ │ - * > } │ │ │ │ - * > }); │ │ │ │ - * > map.addControl(control); │ │ │ │ - * │ │ │ │ - */ │ │ │ │ -OpenLayers.Control = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: id │ │ │ │ - * {String} │ │ │ │ - */ │ │ │ │ - id: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: map │ │ │ │ - * {} this gets set in the addControl() function in │ │ │ │ - * OpenLayers.Map │ │ │ │ - */ │ │ │ │ - map: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: div │ │ │ │ - * {DOMElement} The element that contains the control, if not present the │ │ │ │ - * control is placed inside the map. │ │ │ │ - */ │ │ │ │ - div: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: type │ │ │ │ - * {Number} Controls can have a 'type'. The type determines the type of │ │ │ │ - * interactions which are possible with them when they are placed in an │ │ │ │ - * . │ │ │ │ - */ │ │ │ │ - type: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: allowSelection │ │ │ │ - * {Boolean} By default, controls do not allow selection, because │ │ │ │ - * it may interfere with map dragging. If this is true, OpenLayers │ │ │ │ - * will not prevent selection of the control. │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - allowSelection: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: displayClass │ │ │ │ - * {string} This property is used for CSS related to the drawing of the │ │ │ │ - * Control. │ │ │ │ - */ │ │ │ │ - displayClass: "", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: title │ │ │ │ - * {string} This property is used for showing a tooltip over the │ │ │ │ - * Control. │ │ │ │ - */ │ │ │ │ - title: "", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * false. │ │ │ │ - */ │ │ │ │ - autoActivate: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: active │ │ │ │ - * {Boolean} The control is active (read-only). Use and │ │ │ │ - * to change control state. │ │ │ │ - */ │ │ │ │ - active: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: handlerOptions │ │ │ │ - * {Object} Used to set non-default properties on the control's handler │ │ │ │ - */ │ │ │ │ - handlerOptions: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: handler │ │ │ │ - * {} null │ │ │ │ - */ │ │ │ │ - handler: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: eventListeners │ │ │ │ - * {Object} If set as an option at construction, the eventListeners │ │ │ │ - * object will be registered with . Object │ │ │ │ - * structure must be a listeners object as shown in the example for │ │ │ │ - * the events.on method. │ │ │ │ - */ │ │ │ │ - eventListeners: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Listeners will be called with a reference to an event object. The │ │ │ │ - * properties of this event depends on exactly what happened. │ │ │ │ - * │ │ │ │ - * All event objects have at least the following properties: │ │ │ │ - * object - {Object} A reference to control.events.object (a reference │ │ │ │ - * to the control). │ │ │ │ - * element - {DOMElement} A reference to control.events.element (which │ │ │ │ - * will be null unless documented otherwise). │ │ │ │ - * │ │ │ │ - * Supported map event types: │ │ │ │ - * activate - Triggered when activated. │ │ │ │ - * deactivate - Triggered when deactivated. │ │ │ │ - */ │ │ │ │ - events: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control │ │ │ │ - * Create an OpenLayers Control. The options passed as a parameter │ │ │ │ - * directly extend the control. For example passing the following: │ │ │ │ - * │ │ │ │ - * > var control = new OpenLayers.Control({div: myDiv}); │ │ │ │ - * │ │ │ │ - * Overrides the default div attribute value of null. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - // We do this before the extend so that instances can override │ │ │ │ - // className in options. │ │ │ │ - this.displayClass = │ │ │ │ - this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, ""); │ │ │ │ - │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - │ │ │ │ - this.events = new OpenLayers.Events(this); │ │ │ │ - if (this.eventListeners instanceof Object) { │ │ │ │ - this.events.on(this.eventListeners); │ │ │ │ - } │ │ │ │ - if (this.id == null) { │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * The destroy method is used to perform any clean up before the control │ │ │ │ - * is dereferenced. Typically this is where event listeners are removed │ │ │ │ - * to prevent memory leaks. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.events) { │ │ │ │ - if (this.eventListeners) { │ │ │ │ - this.events.un(this.eventListeners); │ │ │ │ - } │ │ │ │ - this.events.destroy(); │ │ │ │ - this.events = null; │ │ │ │ - } │ │ │ │ - this.eventListeners = null; │ │ │ │ - │ │ │ │ - // eliminate circular references │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.destroy(); │ │ │ │ - this.handler = null; │ │ │ │ - } │ │ │ │ - if (this.handlers) { │ │ │ │ - for (var key in this.handlers) { │ │ │ │ - if (this.handlers.hasOwnProperty(key) && │ │ │ │ - typeof this.handlers[key].destroy == "function") { │ │ │ │ - this.handlers[key].destroy(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.handlers = null; │ │ │ │ - } │ │ │ │ - if (this.map) { │ │ │ │ - this.map.removeControl(this); │ │ │ │ - this.map = null; │ │ │ │ - } │ │ │ │ - this.div = null; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the control. This is done through an accessor │ │ │ │ - * so that subclasses can override this and take special action once │ │ │ │ - * they have their map variable set. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {} │ │ │ │ - */ │ │ │ │ - setMap: function(map) { │ │ │ │ - this.map = map; │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.setMap(map); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ - * The draw method is called when the control is ready to be displayed │ │ │ │ - * on the page. If a div has not been created one is created. Controls │ │ │ │ - * with a visual component will almost always want to override this method │ │ │ │ - * to customize the look of control. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {} The top-left pixel position of the control │ │ │ │ - * or null. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A reference to the DIV DOMElement containing the control │ │ │ │ - */ │ │ │ │ - draw: function(px) { │ │ │ │ - if (this.div == null) { │ │ │ │ - this.div = OpenLayers.Util.createDiv(this.id); │ │ │ │ - this.div.className = this.displayClass; │ │ │ │ - if (!this.allowSelection) { │ │ │ │ - this.div.className += " olControlNoSelect"; │ │ │ │ - this.div.setAttribute("unselectable", "on", 0); │ │ │ │ - this.div.onselectstart = OpenLayers.Function.False; │ │ │ │ - } │ │ │ │ - if (this.title != "") { │ │ │ │ - this.div.title = this.title; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (px != null) { │ │ │ │ - this.position = px.clone(); │ │ │ │ - } │ │ │ │ - this.moveTo(this.position); │ │ │ │ - return this.div; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * Sets the left and top style attributes to the passed in pixel │ │ │ │ - * coordinates. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {} │ │ │ │ - */ │ │ │ │ - moveTo: function(px) { │ │ │ │ - if ((px != null) && (this.div != null)) { │ │ │ │ - this.div.style.left = px.x + "px"; │ │ │ │ - this.div.style.top = px.y + "px"; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Explicitly activates a control and it's associated │ │ │ │ - * handler if one has been set. Controls can be │ │ │ │ - * deactivated by calling the deactivate() method. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} True if the control was successfully activated or │ │ │ │ - * false if the control was already active. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.activate(); │ │ │ │ - } │ │ │ │ - this.active = true; │ │ │ │ - if (this.map) { │ │ │ │ - OpenLayers.Element.addClass( │ │ │ │ - this.map.viewPortDiv, │ │ │ │ - this.displayClass.replace(/ /g, "") + "Active" │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - this.events.triggerEvent("activate"); │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivates a control and it's associated handler if any. The exact │ │ │ │ - * effect of this depends on the control itself. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} True if the control was effectively deactivated or false │ │ │ │ - * if the control was already inactive. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.deactivate(); │ │ │ │ - } │ │ │ │ - this.active = false; │ │ │ │ - if (this.map) { │ │ │ │ - OpenLayers.Element.removeClass( │ │ │ │ - this.map.viewPortDiv, │ │ │ │ - this.displayClass.replace(/ /g, "") + "Active" │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - this.events.triggerEvent("deactivate"); │ │ │ │ - return true; │ │ │ │ - } │ │ │ │ - return false; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Control" │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Control.TYPE_BUTTON │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.TYPE_BUTTON = 1; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Control.TYPE_TOGGLE │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.TYPE_TOGGLE = 2; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Control.TYPE_TOOL │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.TYPE_TOOL = 3; │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format │ │ │ │ - * Base class for format reading/writing a variety of formats. Subclasses │ │ │ │ - * of OpenLayers.Format are expected to have read and write methods. │ │ │ │ - */ │ │ │ │ -OpenLayers.Format = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: options │ │ │ │ - * {Object} A reference to options passed to the constructor. │ │ │ │ - */ │ │ │ │ - options: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: externalProjection │ │ │ │ - * {} When passed a externalProjection and │ │ │ │ - * internalProjection, the format will reproject the geometries it │ │ │ │ - * reads or writes. The externalProjection is the projection used by │ │ │ │ - * the content which is passed into read or which comes out of write. │ │ │ │ - * In order to reproject, a projection transformation function for the │ │ │ │ - * specified projections must be available. This support may be │ │ │ │ - * provided via proj4js or via a custom transformation function. See │ │ │ │ - * {} for more information on │ │ │ │ - * custom transformations. │ │ │ │ - */ │ │ │ │ - externalProjection: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: internalProjection │ │ │ │ - * {} When passed a externalProjection and │ │ │ │ - * internalProjection, the format will reproject the geometries it │ │ │ │ - * reads or writes. The internalProjection is the projection used by │ │ │ │ - * the geometries which are returned by read or which are passed into │ │ │ │ - * write. In order to reproject, a projection transformation function │ │ │ │ - * for the specified projections must be available. This support may be │ │ │ │ - * provided via proj4js or via a custom transformation function. See │ │ │ │ - * {} for more information on │ │ │ │ - * custom transformations. │ │ │ │ - */ │ │ │ │ - internalProjection: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: data │ │ │ │ - * {Object} When is true, this is the parsed string sent to │ │ │ │ - * . │ │ │ │ - */ │ │ │ │ - data: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: keepData │ │ │ │ - * {Object} Maintain a reference () to the most recently read data. │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - keepData: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format │ │ │ │ - * Instances of this class are not useful. See one of the subclasses. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object with properties to set on the │ │ │ │ - * format │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * keepData - {Boolean} If true, upon , the data property will be │ │ │ │ - * set to the parsed object (e.g. the json or xml object). │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * An instance of OpenLayers.Format │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.options = options; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Clean up. │ │ │ │ - */ │ │ │ │ - destroy: function() {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: read │ │ │ │ - * Read data from a string, and return an object whose type depends on the │ │ │ │ - * subclass. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {string} Data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * Depends on the subclass │ │ │ │ - */ │ │ │ │ - read: function(data) { │ │ │ │ - throw new Error('Read not implemented.'); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: write │ │ │ │ - * Accept an object, and return a string. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * object - {Object} Object to be serialized │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A string representation of the object. │ │ │ │ - */ │ │ │ │ - write: function(object) { │ │ │ │ - throw new Error('Write not implemented.'); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ OpenLayers/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. */ │ │ │ │ @@ -23776,104 +22989,14 @@ │ │ │ │ * Constant: OpenLayers.Format.WFST.DEFAULTS │ │ │ │ * {Object} Default properties for the WFST format. │ │ │ │ */ │ │ │ │ OpenLayers.Format.WFST.DEFAULTS = { │ │ │ │ "version": "1.0.0" │ │ │ │ }; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Filter.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ - * @requires OpenLayers/Style.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Filter │ │ │ │ - * This class represents an OGC Filter. │ │ │ │ - */ │ │ │ │ -OpenLayers.Filter = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Filter │ │ │ │ - * This class represents a generic filter. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {} │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Remove reference to anything added. │ │ │ │ - */ │ │ │ │ - destroy: function() {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: evaluate │ │ │ │ - * Evaluates this filter in a specific context. Instances or subclasses │ │ │ │ - * are supposed to override this method. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * context - {Object} Context to use in evaluating the filter. If a vector │ │ │ │ - * feature is provided, the feature.attributes will be used as context. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The filter applies. │ │ │ │ - */ │ │ │ │ - evaluate: function(context) { │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Clones this filter. Should be implemented by subclasses. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {} Clone of this filter. │ │ │ │ - */ │ │ │ │ - clone: function() { │ │ │ │ - return null; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: toString │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} Include in your build to get a CQL │ │ │ │ - * representation of the filter returned. Otherwise "[Object object]" │ │ │ │ - * will be returned. │ │ │ │ - */ │ │ │ │ - toString: function() { │ │ │ │ - var string; │ │ │ │ - if (OpenLayers.Format && OpenLayers.Format.CQL) { │ │ │ │ - string = OpenLayers.Format.CQL.prototype.write(this); │ │ │ │ - } else { │ │ │ │ - string = Object.prototype.toString.call(this); │ │ │ │ - } │ │ │ │ - return string; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Filter" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ OpenLayers/Filter/Spatial.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ @@ -30102,2554 +29225,906 @@ │ │ │ │ OpenLayers.Util.extend(this, options); │ │ │ │ }, │ │ │ │ │ │ │ │ CLASS_NAME: "OpenLayers.WPSProcess.ChainLink" │ │ │ │ │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Spherical.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/SingleFile.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Namespace: Spherical │ │ │ │ - * The OpenLayers.Spherical namespace includes utility functions for │ │ │ │ - * calculations on the basis of a spherical earth (ignoring ellipsoidal │ │ │ │ - * effects), which is accurate enough for most purposes. │ │ │ │ + * Class: OpenLayers.Format.WPSDescribeProcess │ │ │ │ + * Read WPS DescribeProcess responses. │ │ │ │ * │ │ │ │ - * Relevant links: │ │ │ │ - * * http://www.movable-type.co.uk/scripts/latlong.html │ │ │ │ - * * http://code.google.com/apis/maps/documentation/javascript/reference.html#spherical │ │ │ │ + * Inherits from: │ │ │ │ + * - │ │ │ │ */ │ │ │ │ +OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ -OpenLayers.Spherical = OpenLayers.Spherical || {}; │ │ │ │ + /** │ │ │ │ + * Constant: VERSION │ │ │ │ + * {String} 1.0.0 │ │ │ │ + */ │ │ │ │ + VERSION: "1.0.0", │ │ │ │ │ │ │ │ -OpenLayers.Spherical.DEFAULT_RADIUS = 6378137; │ │ │ │ + /** │ │ │ │ + * 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" │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * 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)); │ │ │ │ -}; │ │ │ │ + /** │ │ │ │ + * 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", │ │ │ │ │ │ │ │ -/** │ │ │ │ - * 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; │ │ │ │ -}; │ │ │ │ + /** │ │ │ │ + * Property: regExes │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ + */ │ │ │ │ + regExes: { │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WPSDescribeProcess │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Parse a WPS DescribeProcess and return an object with its information. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ + } │ │ │ │ + var info = {}; │ │ │ │ + this.readNode(data, info); │ │ │ │ + return info; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wps": { │ │ │ │ + "ProcessDescriptions": function(node, obj) { │ │ │ │ + obj.processDescriptions = {}; │ │ │ │ + this.readChildNodes(node, obj.processDescriptions); │ │ │ │ + }, │ │ │ │ + "ProcessDescription": function(node, processDescriptions) { │ │ │ │ + var processVersion = this.getAttributeNS(node, this.namespaces.wps, "processVersion"); │ │ │ │ + var processDescription = { │ │ │ │ + processVersion: processVersion, │ │ │ │ + statusSupported: (node.getAttribute("statusSupported") === "true"), │ │ │ │ + storeSupported: (node.getAttribute("storeSupported") === "true") │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, processDescription); │ │ │ │ + processDescriptions[processDescription.identifier] = processDescription; │ │ │ │ + }, │ │ │ │ + "DataInputs": function(node, processDescription) { │ │ │ │ + processDescription.dataInputs = []; │ │ │ │ + this.readChildNodes(node, processDescription.dataInputs); │ │ │ │ + }, │ │ │ │ + "ProcessOutputs": function(node, processDescription) { │ │ │ │ + processDescription.processOutputs = []; │ │ │ │ + this.readChildNodes(node, processDescription.processOutputs); │ │ │ │ + }, │ │ │ │ + "Output": function(node, processOutputs) { │ │ │ │ + var output = {}; │ │ │ │ + this.readChildNodes(node, output); │ │ │ │ + processOutputs.push(output); │ │ │ │ + }, │ │ │ │ + "ComplexOutput": function(node, output) { │ │ │ │ + output.complexOutput = {}; │ │ │ │ + this.readChildNodes(node, output.complexOutput); │ │ │ │ + }, │ │ │ │ + "LiteralOutput": function(node, output) { │ │ │ │ + output.literalOutput = {}; │ │ │ │ + this.readChildNodes(node, output.literalOutput); │ │ │ │ + }, │ │ │ │ + "Input": function(node, dataInputs) { │ │ │ │ + var input = { │ │ │ │ + maxOccurs: parseInt(node.getAttribute("maxOccurs")), │ │ │ │ + minOccurs: parseInt(node.getAttribute("minOccurs")) │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, input); │ │ │ │ + dataInputs.push(input); │ │ │ │ + }, │ │ │ │ + "BoundingBoxData": function(node, input) { │ │ │ │ + input.boundingBoxData = {}; │ │ │ │ + this.readChildNodes(node, input.boundingBoxData); │ │ │ │ + }, │ │ │ │ + "CRS": function(node, obj) { │ │ │ │ + if (!obj.CRSs) { │ │ │ │ + obj.CRSs = {}; │ │ │ │ + } │ │ │ │ + obj.CRSs[this.getChildValue(node)] = true; │ │ │ │ + }, │ │ │ │ + "LiteralData": function(node, input) { │ │ │ │ + input.literalData = {}; │ │ │ │ + this.readChildNodes(node, input.literalData); │ │ │ │ + }, │ │ │ │ + "ComplexData": function(node, input) { │ │ │ │ + input.complexData = {}; │ │ │ │ + this.readChildNodes(node, input.complexData); │ │ │ │ + }, │ │ │ │ + "Default": function(node, complexData) { │ │ │ │ + complexData["default"] = {}; │ │ │ │ + this.readChildNodes(node, complexData["default"]); │ │ │ │ + }, │ │ │ │ + "Supported": function(node, complexData) { │ │ │ │ + complexData["supported"] = {}; │ │ │ │ + this.readChildNodes(node, complexData["supported"]); │ │ │ │ + }, │ │ │ │ + "Format": function(node, obj) { │ │ │ │ + var format = {}; │ │ │ │ + this.readChildNodes(node, format); │ │ │ │ + if (!obj.formats) { │ │ │ │ + obj.formats = {}; │ │ │ │ + } │ │ │ │ + obj.formats[format.mimeType] = true; │ │ │ │ + }, │ │ │ │ + "MimeType": function(node, format) { │ │ │ │ + format.mimeType = this.getChildValue(node); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WPSDescribeProcess" │ │ │ │ + │ │ │ │ + }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Protocol.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/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/SingleFile.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Protocol │ │ │ │ - * Abstract vector layer protocol class. Not to be instantiated directly. Use │ │ │ │ - * one of the protocol subclasses instead. │ │ │ │ + * @requires OpenLayers/Events.js │ │ │ │ + * @requires OpenLayers/WPSProcess.js │ │ │ │ + * @requires OpenLayers/Format/WPSDescribeProcess.js │ │ │ │ + * @requires OpenLayers/Request.js │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: format │ │ │ │ - * {} The format used by this protocol. │ │ │ │ - */ │ │ │ │ - format: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: options │ │ │ │ - * {Object} Any options sent to the constructor. │ │ │ │ - */ │ │ │ │ - options: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: autoDestroy │ │ │ │ - * {Boolean} The creator of the protocol can set autoDestroy to false │ │ │ │ - * to fully control when the protocol is destroyed. Defaults to │ │ │ │ - * true. │ │ │ │ - */ │ │ │ │ - autoDestroy: true, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: defaultFilter │ │ │ │ - * {} Optional default filter to read requests │ │ │ │ - */ │ │ │ │ - defaultFilter: null, │ │ │ │ +/** │ │ │ │ + * 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({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Protocol │ │ │ │ - * Abstract class for vector protocols. Create instances of a subclass. │ │ │ │ + * Property: servers │ │ │ │ + * {Object} Service metadata, keyed by a local identifier. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.options = options; │ │ │ │ - }, │ │ │ │ + servers: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: mergeWithDefaultFilter │ │ │ │ - * Merge filter passed to the read method with the default one │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * filter - {} │ │ │ │ + * Property: version │ │ │ │ + * {String} The default WPS version to use if none is configured. Default │ │ │ │ + * is '1.0.0'. │ │ │ │ */ │ │ │ │ - mergeWithDefaultFilter: function(filter) { │ │ │ │ - var merged; │ │ │ │ - if (filter && this.defaultFilter) { │ │ │ │ - merged = new OpenLayers.Filter.Logical({ │ │ │ │ - type: OpenLayers.Filter.Logical.AND, │ │ │ │ - filters: [this.defaultFilter, filter] │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - merged = filter || this.defaultFilter || undefined; │ │ │ │ - } │ │ │ │ - return merged; │ │ │ │ - }, │ │ │ │ + version: '1.0.0', │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Clean up the protocol. │ │ │ │ + * Property: lazy │ │ │ │ + * {Boolean} Should the DescribeProcess be deferred until a process is │ │ │ │ + * fully configured? Default is false. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.options = null; │ │ │ │ - this.format = null; │ │ │ │ - }, │ │ │ │ + lazy: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Construct a request for reading new features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ + * Property: events │ │ │ │ + * {} │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {} An │ │ │ │ - * object, the same object will be passed to the callback function passed │ │ │ │ - * if one exists in the options object. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - read: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - options.filter = this.mergeWithDefaultFilter(options.filter); │ │ │ │ - }, │ │ │ │ - │ │ │ │ + events: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: create │ │ │ │ - * Construct a request for writing newly created features. │ │ │ │ + * Constructor: OpenLayers.WPSClient │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array({})} or │ │ │ │ - * {} │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ + * options - {Object} Object whose properties will be set on the instance. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {} An │ │ │ │ - * object, the same object will be passed to the callback function passed │ │ │ │ - * if one exists in the options object. │ │ │ │ - */ │ │ │ │ - create: function() {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: update │ │ │ │ - * Construct a request updating modified features. │ │ │ │ + * 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: │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array({})} or │ │ │ │ - * {} │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ + * (code) │ │ │ │ + * servers: { │ │ │ │ + * local: '/geoserver/wps' │ │ │ │ + * }, { │ │ │ │ + * opengeo: { │ │ │ │ + * url: 'http://demo.opengeo.org/geoserver/wps', │ │ │ │ + * version: '1.0.0' │ │ │ │ + * } │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {} An │ │ │ │ - * object, the same object will be passed to the callback function passed │ │ │ │ - * if one exists in the options object. │ │ │ │ + * lazy - {Boolean} Optional. Set to true if DescribeProcess should not be │ │ │ │ + * requested until a process is fully configured. Default is false. │ │ │ │ */ │ │ │ │ - update: function() {}, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.events = new OpenLayers.Events(this); │ │ │ │ + this.servers = {}; │ │ │ │ + for (var s in options.servers) { │ │ │ │ + this.servers[s] = typeof options.servers[s] == 'string' ? { │ │ │ │ + url: options.servers[s], │ │ │ │ + version: this.version, │ │ │ │ + processDescription: {} │ │ │ │ + } : options.servers[s]; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: delete │ │ │ │ - * Construct a request deleting a removed feature. │ │ │ │ + * 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: │ │ │ │ - * feature - {} │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ + * options - {Object} Options for the execute operation. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {} An │ │ │ │ - * object, the same object will be passed to the callback function passed │ │ │ │ - * if one exists in the options object. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - "delete": function() {}, │ │ │ │ + execute: function(options) { │ │ │ │ + var process = this.getProcess(options.server, options.process); │ │ │ │ + process.execute({ │ │ │ │ + inputs: options.inputs, │ │ │ │ + success: options.success, │ │ │ │ + scope: options.scope │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: commit │ │ │ │ - * Go over the features and for each take action │ │ │ │ - * based on the feature state. Possible actions are create, │ │ │ │ - * update and delete. │ │ │ │ + * APIMethod: getProcess │ │ │ │ + * Creates an . │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array({})} │ │ │ │ - * options - {Object} Object whose possible keys are "create", "update", │ │ │ │ - * "delete", "callback" and "scope", the values referenced by the │ │ │ │ - * first three are objects as passed to the "create", "update", and │ │ │ │ - * "delete" methods, the value referenced by the "callback" key is │ │ │ │ - * a function which is called when the commit operation is complete │ │ │ │ - * using the scope referenced by the "scope" key. │ │ │ │ + * serverID - {String} Local identifier from the servers that this instance │ │ │ │ + * was constructed with. │ │ │ │ + * processID - {String} Process identifier known to the server. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array({})} An array of │ │ │ │ - * objects. │ │ │ │ + * {} │ │ │ │ */ │ │ │ │ - commit: function() {}, │ │ │ │ + getProcess: function(serverID, processID) { │ │ │ │ + var process = new OpenLayers.WPSProcess({ │ │ │ │ + client: this, │ │ │ │ + server: serverID, │ │ │ │ + identifier: processID │ │ │ │ + }); │ │ │ │ + if (!this.lazy) { │ │ │ │ + process.describe(); │ │ │ │ + } │ │ │ │ + return process; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: abort │ │ │ │ - * Abort an ongoing request. │ │ │ │ + * Method: describeProcess │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * response - {} │ │ │ │ + * 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 │ │ │ │ */ │ │ │ │ - abort: function(response) {}, │ │ │ │ + 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: createCallback │ │ │ │ - * Returns a function that applies the given public method with resp and │ │ │ │ - * options arguments. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * method - {Function} The method to be applied by the callback. │ │ │ │ - * response - {} The protocol response object. │ │ │ │ - * options - {Object} Options sent to the protocol method │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - createCallback: function(method, response, options) { │ │ │ │ - return OpenLayers.Function.bind(function() { │ │ │ │ - method.apply(this, [response, options]); │ │ │ │ - }, this); │ │ │ │ + destroy: function() { │ │ │ │ + this.events.destroy(); │ │ │ │ + this.events = null; │ │ │ │ + this.servers = null; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol" │ │ │ │ + CLASS_NAME: 'OpenLayers.WPSClient' │ │ │ │ + │ │ │ │ }); │ │ │ │ +/* ====================================================================== │ │ │ │ + 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. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Protocol.Response │ │ │ │ - * Protocols return Response objects to their users. │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Animation.js │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol.Response = OpenLayers.Class({ │ │ │ │ + │ │ │ │ +OpenLayers.Kinetic = OpenLayers.Class({ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Property: code │ │ │ │ - * {Number} - OpenLayers.Protocol.Response.SUCCESS or │ │ │ │ - * OpenLayers.Protocol.Response.FAILURE │ │ │ │ + * Property: threshold │ │ │ │ + * In most cases changing the threshold isn't needed. │ │ │ │ + * In px/ms, default to 0. │ │ │ │ */ │ │ │ │ - code: null, │ │ │ │ + threshold: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: requestType │ │ │ │ - * {String} The type of request this response corresponds to. Either │ │ │ │ - * "create", "read", "update" or "delete". │ │ │ │ + * Property: deceleration │ │ │ │ + * {Float} the deseleration in px/ms², default to 0.0035. │ │ │ │ */ │ │ │ │ - requestType: null, │ │ │ │ + deceleration: 0.0035, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: last │ │ │ │ - * {Boolean} - true if this is the last response expected in a commit, │ │ │ │ - * false otherwise, defaults to true. │ │ │ │ + * Property: nbPoints │ │ │ │ + * {Integer} the number of points we use to calculate the kinetic │ │ │ │ + * initial values. │ │ │ │ */ │ │ │ │ - last: true, │ │ │ │ + nbPoints: 100, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: features │ │ │ │ - * {Array({})} or {} │ │ │ │ - * The features returned in the response by the server. Depending on the │ │ │ │ - * protocol's read payload, either features or data will be populated. │ │ │ │ + * Property: delay │ │ │ │ + * {Float} time to consider to calculate the kinetic initial values. │ │ │ │ + * In ms, default to 200. │ │ │ │ */ │ │ │ │ - features: null, │ │ │ │ + delay: 200, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: data │ │ │ │ - * {Object} │ │ │ │ - * The data returned in the response by the server. Depending on the │ │ │ │ - * protocol's read payload, either features or data will be populated. │ │ │ │ + * Property: points │ │ │ │ + * List of points use to calculate the kinetic initial values. │ │ │ │ */ │ │ │ │ - data: null, │ │ │ │ + points: undefined, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: reqFeatures │ │ │ │ - * {Array({})} or {} │ │ │ │ - * The features provided by the user and placed in the request by the │ │ │ │ - * protocol. │ │ │ │ + * Property: timerId │ │ │ │ + * ID of the timer. │ │ │ │ */ │ │ │ │ - reqFeatures: null, │ │ │ │ + timerId: undefined, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: priv │ │ │ │ + * Constructor: OpenLayers.Kinetic │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - priv: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: error │ │ │ │ - * {Object} The error object in case a service exception was encountered. │ │ │ │ + * Method: begin │ │ │ │ + * Begins the dragging. │ │ │ │ */ │ │ │ │ - error: null, │ │ │ │ + begin: function() { │ │ │ │ + OpenLayers.Animation.stop(this.timerId); │ │ │ │ + this.timerId = undefined; │ │ │ │ + this.points = []; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Protocol.Response │ │ │ │ + * Method: update │ │ │ │ + * Updates during the dragging. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * xy - {} The new position. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ + update: function(xy) { │ │ │ │ + this.points.unshift({ │ │ │ │ + xy: xy, │ │ │ │ + tick: new Date().getTime() │ │ │ │ + }); │ │ │ │ + if (this.points.length > this.nbPoints) { │ │ │ │ + this.points.pop(); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: success │ │ │ │ + * Method: end │ │ │ │ + * Ends the dragging, start the kinetic. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * xy - {} The last position. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} - true on success, false otherwise │ │ │ │ + * {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. │ │ │ │ */ │ │ │ │ - success: function() { │ │ │ │ - return this.code > 0; │ │ │ │ + end: function(xy) { │ │ │ │ + var last, now = new Date().getTime(); │ │ │ │ + for (var i = 0, l = this.points.length, point; i < l; i++) { │ │ │ │ + point = this.points[i]; │ │ │ │ + if (now - point.tick > this.delay) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + last = point; │ │ │ │ + } │ │ │ │ + if (!last) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + var time = new Date().getTime() - last.tick; │ │ │ │ + var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) + │ │ │ │ + Math.pow(xy.y - last.xy.y, 2)); │ │ │ │ + var speed = dist / time; │ │ │ │ + if (speed == 0 || speed < this.threshold) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + var theta = Math.asin((xy.y - last.xy.y) / dist); │ │ │ │ + if (last.xy.x <= xy.x) { │ │ │ │ + theta = Math.PI - theta; │ │ │ │ + } │ │ │ │ + return { │ │ │ │ + speed: speed, │ │ │ │ + theta: theta │ │ │ │ + }; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.Response" │ │ │ │ -}); │ │ │ │ + /** │ │ │ │ + * Method: move │ │ │ │ + * Launch the kinetic move pan. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * info - {Object} An object with two properties, "speed", and "theta". │ │ │ │ + * These values are those returned from the "end" call. │ │ │ │ + * callback - {Function} Function called on every step of the animation, │ │ │ │ + * receives x, y (values to pan), end (is the last point). │ │ │ │ + */ │ │ │ │ + move: function(info, callback) { │ │ │ │ + var v0 = info.speed; │ │ │ │ + var fx = Math.cos(info.theta); │ │ │ │ + var fy = -Math.sin(info.theta); │ │ │ │ │ │ │ │ -OpenLayers.Protocol.Response.SUCCESS = 1; │ │ │ │ -OpenLayers.Protocol.Response.FAILURE = 0; │ │ │ │ + var initialTime = new Date().getTime(); │ │ │ │ + │ │ │ │ + var lastX = 0; │ │ │ │ + var lastY = 0; │ │ │ │ + │ │ │ │ + var timerCallback = function() { │ │ │ │ + if (this.timerId == null) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + │ │ │ │ + var t = new Date().getTime() - initialTime; │ │ │ │ + │ │ │ │ + var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t; │ │ │ │ + var x = p * fx; │ │ │ │ + var y = p * fy; │ │ │ │ + │ │ │ │ + var args = {}; │ │ │ │ + args.end = false; │ │ │ │ + var v = -this.deceleration * t + v0; │ │ │ │ + │ │ │ │ + if (v <= 0) { │ │ │ │ + OpenLayers.Animation.stop(this.timerId); │ │ │ │ + this.timerId = null; │ │ │ │ + args.end = true; │ │ │ │ + } │ │ │ │ + │ │ │ │ + args.x = x - lastX; │ │ │ │ + args.y = y - lastY; │ │ │ │ + lastX = x; │ │ │ │ + lastY = y; │ │ │ │ + callback(args.x, args.y, args.end); │ │ │ │ + }; │ │ │ │ + │ │ │ │ + this.timerId = OpenLayers.Animation.start( │ │ │ │ + OpenLayers.Function.bind(timerCallback, this) │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Kinetic" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Renderer.js │ │ │ │ + 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.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.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.Renderer = OpenLayers.Class({ │ │ │ │ +OpenLayers.Popup = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: container │ │ │ │ - * {DOMElement} │ │ │ │ + * Property: events │ │ │ │ + * {} custom event manager │ │ │ │ */ │ │ │ │ - container: null, │ │ │ │ + events: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: root │ │ │ │ - * {DOMElement} │ │ │ │ + /** Property: id │ │ │ │ + * {String} the unique identifier assigned to this popup. │ │ │ │ */ │ │ │ │ - root: null, │ │ │ │ + id: "", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: extent │ │ │ │ - * {} │ │ │ │ + * Property: lonlat │ │ │ │ + * {} the position of this popup on the map │ │ │ │ */ │ │ │ │ - extent: null, │ │ │ │ + lonlat: 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: div │ │ │ │ + * {DOMElement} the div that contains this popup. │ │ │ │ */ │ │ │ │ - locked: false, │ │ │ │ + div: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: size │ │ │ │ - * {} │ │ │ │ + * Property: contentSize │ │ │ │ + * {} the width and height of the content. │ │ │ │ */ │ │ │ │ - size: null, │ │ │ │ + contentSize: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: resolution │ │ │ │ - * {Float} cache of current map resolution │ │ │ │ + /** │ │ │ │ + * Property: size │ │ │ │ + * {} the width and height of the popup. │ │ │ │ */ │ │ │ │ - resolution: null, │ │ │ │ + size: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: map │ │ │ │ - * {} Reference to the map -- this is set in Vector's setMap() │ │ │ │ + /** │ │ │ │ + * Property: contentHTML │ │ │ │ + * {String} An HTML string for this popup to display. │ │ │ │ */ │ │ │ │ - map: null, │ │ │ │ + contentHTML: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: featureDx │ │ │ │ - * {Number} Feature offset in x direction. Will be calculated for and │ │ │ │ - * applied to the current feature while rendering (see │ │ │ │ - * ). │ │ │ │ + /** │ │ │ │ + * Property: backgroundColor │ │ │ │ + * {String} the background color used by the popup. │ │ │ │ */ │ │ │ │ - featureDx: 0, │ │ │ │ + backgroundColor: "", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Renderer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * containerID - {} │ │ │ │ - * options - {Object} options for this renderer. See sublcasses for │ │ │ │ - * supported options. │ │ │ │ + /** │ │ │ │ + * Property: opacity │ │ │ │ + * {float} the opacity of this popup (between 0.0 and 1.0) │ │ │ │ */ │ │ │ │ - initialize: function(containerID, options) { │ │ │ │ - this.container = OpenLayers.Util.getElement(containerID); │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - }, │ │ │ │ + opacity: "", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ + /** │ │ │ │ + * Property: border │ │ │ │ + * {String} the border size of the popup. (eg 2px) │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.container = null; │ │ │ │ - this.extent = null; │ │ │ │ - this.size = null; │ │ │ │ - this.resolution = null; │ │ │ │ - this.map = null; │ │ │ │ - }, │ │ │ │ + border: "", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: supported │ │ │ │ - * This should be overridden by specific subclasses │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Whether or not the browser supports the renderer class │ │ │ │ + /** │ │ │ │ + * Property: contentDiv │ │ │ │ + * {DOMElement} a reference to the element that holds the content of │ │ │ │ + * the div. │ │ │ │ */ │ │ │ │ - supported: function() { │ │ │ │ - return false; │ │ │ │ - }, │ │ │ │ + contentDiv: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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. │ │ │ │ + /** │ │ │ │ + * Property: groupDiv │ │ │ │ + * {DOMElement} First and only child of 'div'. The group Div contains the │ │ │ │ + * 'contentDiv' and the 'closeDiv'. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - }, │ │ │ │ + groupDiv: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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 - {} │ │ │ │ + /** │ │ │ │ + * Property: closeDiv │ │ │ │ + * {DOMElement} the optional closer image │ │ │ │ */ │ │ │ │ - setSize: function(size) { │ │ │ │ - this.size = size.clone(); │ │ │ │ - this.resolution = null; │ │ │ │ - }, │ │ │ │ + closeDiv: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getResolution │ │ │ │ - * Uses cached copy of resolution if available to minimize computing │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Float} The current map's resolution │ │ │ │ + * APIProperty: autoSize │ │ │ │ + * {Boolean} Resize the popup to auto-fit the contents. │ │ │ │ + * Default is false. │ │ │ │ */ │ │ │ │ - getResolution: function() { │ │ │ │ - this.resolution = this.resolution || this.map.getResolution(); │ │ │ │ - return this.resolution; │ │ │ │ - }, │ │ │ │ + autoSize: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawFeature │ │ │ │ - * Draw the feature. The optional style argument can be used │ │ │ │ - * to override the feature's own style. This method should only │ │ │ │ - * be called from layer.drawFeature(). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {} │ │ │ │ - * style - {} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} true if the feature has been drawn completely, false if not, │ │ │ │ - * undefined if the feature had no geometry │ │ │ │ + * APIProperty: minSize │ │ │ │ + * {} Minimum size allowed for the popup's contents. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + minSize: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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 │ │ │ │ + * APIProperty: maxSize │ │ │ │ + * {} Maximum size allowed for the popup's contents. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + maxSize: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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 - {} │ │ │ │ + * Property: displayClass │ │ │ │ + * {String} The CSS class of the popup. │ │ │ │ */ │ │ │ │ - drawGeometry: function(geometry, style, featureId) {}, │ │ │ │ + displayClass: "olPopup", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawText │ │ │ │ - * Function for drawing text labels. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * featureId - {String} │ │ │ │ - * style - │ │ │ │ - * location - {} │ │ │ │ + /** │ │ │ │ + * Property: contentDisplayClass │ │ │ │ + * {String} The CSS class of the popup content div. │ │ │ │ */ │ │ │ │ - drawText: function(featureId, style, location) {}, │ │ │ │ + contentDisplayClass: "olPopupContent", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: removeText │ │ │ │ - * Function for removing text labels. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * featureId - {String} │ │ │ │ - */ │ │ │ │ - removeText: function(featureId) {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: clear │ │ │ │ - * Clear all vectors from the renderer. │ │ │ │ - * virtual function. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - clear: function() {}, │ │ │ │ + padding: 0, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getFeatureIdFromEvent │ │ │ │ - * Returns a feature id from an event on the renderer. │ │ │ │ - * How this happens is specific to the renderer. This should be │ │ │ │ - * called from layer.getFeatureFromEvent(). │ │ │ │ - * Virtual function. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A feature id or undefined. │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - getFeatureIdFromEvent: function(evt) {}, │ │ │ │ + disableFirefoxOverflowHack: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: eraseFeatures │ │ │ │ - * This is called by the layer to erase features │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array()} │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + fixPadding: function() { │ │ │ │ + if (typeof this.padding == "number") { │ │ │ │ + this.padding = new OpenLayers.Bounds( │ │ │ │ + this.padding, this.padding, this.padding, this.padding │ │ │ │ + ); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: eraseGeometry │ │ │ │ - * Remove a geometry from the renderer (by id). │ │ │ │ - * virtual function. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {} │ │ │ │ - * featureId - {String} │ │ │ │ + * APIProperty: panMapIfOutOfView │ │ │ │ + * {Boolean} When drawn, pan map such that the entire popup is visible in │ │ │ │ + * the current viewport (if necessary). │ │ │ │ + * Default is false. │ │ │ │ */ │ │ │ │ - eraseGeometry: function(geometry, featureId) {}, │ │ │ │ + panMapIfOutOfView: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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 │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - moveRoot: function(renderer) {}, │ │ │ │ + keepInMap: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIProperty: closeOnMove │ │ │ │ + * {Boolean} When map pans, close the popup. │ │ │ │ + * Default is false. │ │ │ │ */ │ │ │ │ - getRenderLayerId: function() { │ │ │ │ - return this.container.id; │ │ │ │ - }, │ │ │ │ + closeOnMove: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: applyDefaultSymbolizer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * symbolizer - {Object} │ │ │ │ + /** │ │ │ │ + * Property: map │ │ │ │ + * {} this gets set in Map.js when the popup is added to the map │ │ │ │ + */ │ │ │ │ + map: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Popup │ │ │ │ + * Create a popup. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Object} │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) { │ │ │ │ + if (id == null) { │ │ │ │ + id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ } │ │ │ │ - OpenLayers.Util.extend(result, symbolizer); │ │ │ │ - return result; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer" │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Renderer.defaultSymbolizer │ │ │ │ - * {Object} Properties from this symbolizer will be applied to symbolizers │ │ │ │ - * with missing properties. This can also be used to set a global │ │ │ │ - * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the │ │ │ │ - * following code before rendering any vector features: │ │ │ │ - * (code) │ │ │ │ - * OpenLayers.Renderer.defaultSymbolizer = { │ │ │ │ - * fillColor: "#808080", │ │ │ │ - * fillOpacity: 1, │ │ │ │ - * strokeColor: "#000000", │ │ │ │ - * strokeOpacity: 1, │ │ │ │ - * strokeWidth: 1, │ │ │ │ - * pointRadius: 3, │ │ │ │ - * graphicName: "square" │ │ │ │ - * }; │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.defaultSymbolizer = { │ │ │ │ - fillColor: "#000000", │ │ │ │ - strokeColor: "#000000", │ │ │ │ - strokeWidth: 2, │ │ │ │ - fillOpacity: 1, │ │ │ │ - strokeOpacity: 1, │ │ │ │ - pointRadius: 0, │ │ │ │ - labelAlign: 'cm' │ │ │ │ -}; │ │ │ │ - │ │ │ │ │ │ │ │ + this.id = id; │ │ │ │ + this.lonlat = lonlat; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * 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.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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.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; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - */ │ │ │ │ + this.div = OpenLayers.Util.createDiv(this.id, null, null, │ │ │ │ + null, null, null, "hidden"); │ │ │ │ + this.div.className = this.displayClass; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Symbolizer │ │ │ │ - * Base class representing a symbolizer used for feature rendering. │ │ │ │ - */ │ │ │ │ -OpenLayers.Symbolizer = OpenLayers.Class({ │ │ │ │ + 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); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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, │ │ │ │ + if (closeBox) { │ │ │ │ + this.addCloseBox(closeBoxCallback); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Symbolizer │ │ │ │ - * Instances of this class are not useful. See one of the subclasses. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * config - {Object} An object containing properties to be set on the │ │ │ │ - * symbolizer. Any documented symbolizer property can be set at │ │ │ │ - * construction. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * A new symbolizer. │ │ │ │ - */ │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Util.extend(this, config); │ │ │ │ + this.registerEvents(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Create a copy of this symbolizer. │ │ │ │ - * │ │ │ │ - * Returns a symbolizer of the same type with the same properties. │ │ │ │ + * Method: destroy │ │ │ │ + * nullify references to prevent circular references and memory leaks │ │ │ │ */ │ │ │ │ - clone: function() { │ │ │ │ - var Type = eval(this.CLASS_NAME); │ │ │ │ - return new Type(OpenLayers.Util.extend({}, this)); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer" │ │ │ │ + destroy: function() { │ │ │ │ │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/StyleMap.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Style.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.StyleMap │ │ │ │ - */ │ │ │ │ -OpenLayers.StyleMap = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: styles │ │ │ │ - * {Object} Hash of {}, keyed by names of well known │ │ │ │ - * rendering intents (e.g. "default", "temporary", "select", "delete"). │ │ │ │ - */ │ │ │ │ - styles: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: extendDefault │ │ │ │ - * {Boolean} if true, every render intent will extend the symbolizers │ │ │ │ - * specified for the "default" intent at rendering time. Otherwise, every │ │ │ │ - * rendering intent will be treated as a completely independent style. │ │ │ │ - */ │ │ │ │ - extendDefault: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.StyleMap │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * style - {Object} Optional. Either a style hash, or a style object, or │ │ │ │ - * a hash of style objects (style hashes) keyed by rendering │ │ │ │ - * intent. If just one style hash or style object is passed, │ │ │ │ - * this will be used for all known render intents (default, │ │ │ │ - * select, temporary) │ │ │ │ - * options - {Object} optional hash of additional options for this │ │ │ │ - * instance │ │ │ │ - */ │ │ │ │ - initialize: function(style, options) { │ │ │ │ - this.styles = { │ │ │ │ - "default": new OpenLayers.Style( │ │ │ │ - OpenLayers.Feature.Vector.style["default"]), │ │ │ │ - "select": new OpenLayers.Style( │ │ │ │ - OpenLayers.Feature.Vector.style["select"]), │ │ │ │ - "temporary": new OpenLayers.Style( │ │ │ │ - OpenLayers.Feature.Vector.style["temporary"]), │ │ │ │ - "delete": new OpenLayers.Style( │ │ │ │ - OpenLayers.Feature.Vector.style["delete"]) │ │ │ │ - }; │ │ │ │ - │ │ │ │ - // take whatever the user passed as style parameter and convert it │ │ │ │ - // into parts of stylemap. │ │ │ │ - if (style instanceof OpenLayers.Style) { │ │ │ │ - // user passed a style object │ │ │ │ - this.styles["default"] = style; │ │ │ │ - this.styles["select"] = style; │ │ │ │ - this.styles["temporary"] = style; │ │ │ │ - this.styles["delete"] = style; │ │ │ │ - } else if (typeof style == "object") { │ │ │ │ - for (var key in style) { │ │ │ │ - if (style[key] instanceof OpenLayers.Style) { │ │ │ │ - // user passed a hash of style objects │ │ │ │ - this.styles[key] = style[key]; │ │ │ │ - } else if (typeof style[key] == "object") { │ │ │ │ - // user passsed a hash of style hashes │ │ │ │ - this.styles[key] = new OpenLayers.Style(style[key]); │ │ │ │ - } else { │ │ │ │ - // user passed a style hash (i.e. symbolizer) │ │ │ │ - this.styles["default"] = new OpenLayers.Style(style); │ │ │ │ - this.styles["select"] = new OpenLayers.Style(style); │ │ │ │ - this.styles["temporary"] = new OpenLayers.Style(style); │ │ │ │ - this.styles["delete"] = new OpenLayers.Style(style); │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - for (var key in this.styles) { │ │ │ │ - this.styles[key].destroy(); │ │ │ │ - } │ │ │ │ - this.styles = null; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: createSymbolizer │ │ │ │ - * Creates the symbolizer for a feature for a render intent. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {} The feature to evaluate the rules │ │ │ │ - * of the intended style against. │ │ │ │ - * intent - {String} The intent determines the symbolizer that will be │ │ │ │ - * used to draw the feature. Well known intents are "default" │ │ │ │ - * (for just drawing the features), "select" (for selected │ │ │ │ - * features) and "temporary" (for drawing features). │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} symbolizer hash │ │ │ │ - */ │ │ │ │ - createSymbolizer: function(feature, intent) { │ │ │ │ - if (!feature) { │ │ │ │ - feature = new OpenLayers.Feature.Vector(); │ │ │ │ - } │ │ │ │ - if (!this.styles[intent]) { │ │ │ │ - intent = "default"; │ │ │ │ - } │ │ │ │ - feature.renderIntent = intent; │ │ │ │ - var defaultSymbolizer = {}; │ │ │ │ - if (this.extendDefault && intent != "default") { │ │ │ │ - defaultSymbolizer = this.styles["default"].createSymbolizer(feature); │ │ │ │ - } │ │ │ │ - return OpenLayers.Util.extend(defaultSymbolizer, │ │ │ │ - this.styles[intent].createSymbolizer(feature)); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: addUniqueValueRules │ │ │ │ - * Convenience method to create comparison rules for unique values of a │ │ │ │ - * property. The rules will be added to the style object for a specified │ │ │ │ - * rendering intent. This method is a shortcut for creating something like │ │ │ │ - * the "unique value legends" familiar from well known desktop GIS systems │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * renderIntent - {String} rendering intent to add the rules to │ │ │ │ - * property - {String} values of feature attributes to create the │ │ │ │ - * rules for │ │ │ │ - * symbolizers - {Object} Hash of symbolizers, keyed by the desired │ │ │ │ - * property values │ │ │ │ - * context - {Object} An optional object with properties that │ │ │ │ - * symbolizers' property values should be evaluated │ │ │ │ - * against. If no context is specified, feature.attributes │ │ │ │ - * will be used │ │ │ │ - */ │ │ │ │ - addUniqueValueRules: function(renderIntent, property, symbolizers, context) { │ │ │ │ - var rules = []; │ │ │ │ - for (var value in symbolizers) { │ │ │ │ - rules.push(new OpenLayers.Rule({ │ │ │ │ - symbolizer: symbolizers[value], │ │ │ │ - context: context, │ │ │ │ - filter: new OpenLayers.Filter.Comparison({ │ │ │ │ - type: OpenLayers.Filter.Comparison.EQUAL_TO, │ │ │ │ - property: property, │ │ │ │ - value: value │ │ │ │ - }) │ │ │ │ - })); │ │ │ │ - } │ │ │ │ - this.styles[renderIntent].addRules(rules); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.StyleMap" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/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: │ │ │ │ - * lonlat - {} the position of this marker │ │ │ │ - * icon - {} the icon for this marker │ │ │ │ - */ │ │ │ │ - initialize: function(lonlat, icon) { │ │ │ │ - this.lonlat = lonlat; │ │ │ │ - │ │ │ │ - var newIcon = (icon) ? icon : OpenLayers.Marker.defaultIcon(); │ │ │ │ - if (this.icon == null) { │ │ │ │ - this.icon = newIcon; │ │ │ │ - } else { │ │ │ │ - this.icon.url = newIcon.url; │ │ │ │ - this.icon.size = newIcon.size; │ │ │ │ - this.icon.offset = newIcon.offset; │ │ │ │ - this.icon.calculateOffset = newIcon.calculateOffset; │ │ │ │ - } │ │ │ │ - this.events = new OpenLayers.Events(this, this.icon.imageDiv); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Destroy the marker. You must first remove the marker from any │ │ │ │ - * layer which it has been added to, or you will get buggy behavior. │ │ │ │ - * (This can not be done within the marker since the marker does not │ │ │ │ - * know which layer it is attached to.) │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - // erase any drawn features │ │ │ │ - this.erase(); │ │ │ │ - │ │ │ │ - this.map = null; │ │ │ │ - │ │ │ │ - this.events.destroy(); │ │ │ │ - this.events = null; │ │ │ │ - │ │ │ │ - if (this.icon != null) { │ │ │ │ - this.icon.destroy(); │ │ │ │ - this.icon = null; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ - * Calls draw on the icon, and returns that output. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A new DOM Image with this marker's icon set at the │ │ │ │ - * location passed-in │ │ │ │ - */ │ │ │ │ - draw: function(px) { │ │ │ │ - return this.icon.draw(px); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: erase │ │ │ │ - * Erases any drawn elements for this marker. │ │ │ │ - */ │ │ │ │ - erase: function() { │ │ │ │ - if (this.icon != null) { │ │ │ │ - this.icon.erase(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * Move the marker to the new location. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {|Object} the pixel position to move to. │ │ │ │ - * An OpenLayers.Pixel or an object with a 'x' and 'y' properties. │ │ │ │ - */ │ │ │ │ - moveTo: function(px) { │ │ │ │ - if ((px != null) && (this.icon != null)) { │ │ │ │ - this.icon.moveTo(px); │ │ │ │ - } │ │ │ │ - this.lonlat = this.map.getLonLatFromLayerPx(px); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: isDrawn │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Whether or not the marker is drawn. │ │ │ │ - */ │ │ │ │ - isDrawn: function() { │ │ │ │ - var isDrawn = (this.icon && this.icon.isDrawn()); │ │ │ │ - return isDrawn; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: onScreen │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Whether or not the marker is currently visible on screen. │ │ │ │ - */ │ │ │ │ - onScreen: function() { │ │ │ │ - │ │ │ │ - var onScreen = false; │ │ │ │ - if (this.map) { │ │ │ │ - var screenBounds = this.map.getExtent(); │ │ │ │ - onScreen = screenBounds.containsLonLat(this.lonlat); │ │ │ │ - } │ │ │ │ - return onScreen; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: inflate │ │ │ │ - * Englarges the markers icon by the specified ratio. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * inflate - {float} the ratio to enlarge the marker by (passing 2 │ │ │ │ - * will double the size). │ │ │ │ - */ │ │ │ │ - inflate: function(inflate) { │ │ │ │ - if (this.icon) { │ │ │ │ - this.icon.setSize({ │ │ │ │ - w: this.icon.size.w * inflate, │ │ │ │ - h: this.icon.size.h * inflate │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setOpacity │ │ │ │ - * Change the opacity of the marker by changin the opacity of │ │ │ │ - * its icon │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * opacity - {float} Specified as fraction (0.4, etc) │ │ │ │ - */ │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ - this.icon.setOpacity(opacity); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setUrl │ │ │ │ - * Change URL of the Icon Image. │ │ │ │ - * │ │ │ │ - * url - {String} │ │ │ │ - */ │ │ │ │ - setUrl: function(url) { │ │ │ │ - this.icon.setUrl(url); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: display │ │ │ │ - * Hide or show the icon │ │ │ │ - * │ │ │ │ - * display - {Boolean} │ │ │ │ - */ │ │ │ │ - display: function(display) { │ │ │ │ - this.icon.display(display); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Marker" │ │ │ │ -}); │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Function: defaultIcon │ │ │ │ - * Creates a default . │ │ │ │ - * │ │ │ │ - * 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 │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Symbolizer.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Symbolizer.Point │ │ │ │ - * A symbolizer used to render point features. │ │ │ │ - */ │ │ │ │ -OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: strokeColor │ │ │ │ - * {String} Color for line stroke. This is a RGB hex value (e.g. "#ff0000" │ │ │ │ - * for red). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: strokeOpacity │ │ │ │ - * {Number} Stroke opacity (0-1). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: strokeWidth │ │ │ │ - * {Number} Pixel stroke width. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: strokeLinecap │ │ │ │ - * {String} Stroke cap type ("butt", "round", or "square"). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: strokeDashstyle │ │ │ │ - * {String} Stroke dash style according to the SLD spec. Note that the │ │ │ │ - * OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot", │ │ │ │ - * "longdash", "longdashdot", or "solid") will not work in SLD, but │ │ │ │ - * most SLD patterns will render correctly in OpenLayers. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: fillColor │ │ │ │ - * {String} RGB hex fill color (e.g. "#ff0000" for red). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: fillOpacity │ │ │ │ - * {Number} Fill opacity (0-1). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: pointRadius │ │ │ │ - * {Number} Pixel point radius. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: externalGraphic │ │ │ │ - * {String} Url to an external graphic that will be used for rendering │ │ │ │ - * points. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: graphicWidth │ │ │ │ - * {Number} Pixel width for sizing an external graphic. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: graphicHeight │ │ │ │ - * {Number} Pixel height for sizing an external graphic. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: graphicOpacity │ │ │ │ - * {Number} Opacity (0-1) for an external graphic. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: graphicXOffset │ │ │ │ - * {Number} Pixel offset along the positive x axis for displacing an │ │ │ │ - * external graphic. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: graphicYOffset │ │ │ │ - * {Number} Pixel offset along the positive y axis for displacing an │ │ │ │ - * external graphic. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: rotation │ │ │ │ - * {Number} The rotation of a graphic in the clockwise direction about its │ │ │ │ - * center point (or any point off center as specified by │ │ │ │ - * 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: │ │ │ │ - * config - {Object} An object containing properties to be set on the │ │ │ │ - * symbolizer. Any documented symbolizer property can be set at │ │ │ │ - * construction. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * A new point symbolizer. │ │ │ │ - */ │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Point" │ │ │ │ - │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Symbolizer/Line.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Symbolizer.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Symbolizer.Line │ │ │ │ - * A symbolizer used to render line features. │ │ │ │ - */ │ │ │ │ -OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: strokeColor │ │ │ │ - * {String} Color for line stroke. This is a RGB hex value (e.g. "#ff0000" │ │ │ │ - * for red). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: strokeOpacity │ │ │ │ - * {Number} Stroke opacity (0-1). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: strokeWidth │ │ │ │ - * {Number} Pixel stroke width. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: strokeLinecap │ │ │ │ - * {String} Stroke cap type ("butt", "round", or "square"). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: strokeDashstyle │ │ │ │ - * {String} Stroke dash style according to the SLD spec. Note that the │ │ │ │ - * OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot", │ │ │ │ - * "longdash", "longdashdot", or "solid") will not work in SLD, but │ │ │ │ - * most SLD patterns will render correctly in OpenLayers. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Symbolizer.Line │ │ │ │ - * Create a symbolizer for rendering lines. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * config - {Object} An object containing properties to be set on the │ │ │ │ - * symbolizer. Any documented symbolizer property can be set at │ │ │ │ - * construction. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * A new line symbolizer. │ │ │ │ - */ │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Line" │ │ │ │ - │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Symbolizer/Polygon.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Symbolizer.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Symbolizer.Polygon │ │ │ │ - * A symbolizer used to render line features. │ │ │ │ - */ │ │ │ │ -OpenLayers.Symbolizer.Polygon = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: strokeColor │ │ │ │ - * {String} Color for line stroke. This is a RGB hex value (e.g. "#ff0000" │ │ │ │ - * for red). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: strokeOpacity │ │ │ │ - * {Number} Stroke opacity (0-1). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: strokeWidth │ │ │ │ - * {Number} Pixel stroke width. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: strokeLinecap │ │ │ │ - * {String} Stroke cap type ("butt", "round", or "square"). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: strokeDashstyle │ │ │ │ - * {String} Stroke dash style according to the SLD spec. Note that the │ │ │ │ - * OpenLayers values for strokeDashstyle ("dot", "dash", "dashdot", │ │ │ │ - * "longdash", "longdashdot", or "solid") will not work in SLD, but │ │ │ │ - * most SLD patterns will render correctly in OpenLayers. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: fillColor │ │ │ │ - * {String} RGB hex fill color (e.g. "#ff0000" for red). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: fillOpacity │ │ │ │ - * {Number} Fill opacity (0-1). │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Symbolizer.Polygon │ │ │ │ - * Create a symbolizer for rendering polygons. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * config - {Object} An object containing properties to be set on the │ │ │ │ - * symbolizer. Any documented symbolizer property can be set at │ │ │ │ - * construction. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * A new polygon symbolizer. │ │ │ │ - */ │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Polygon" │ │ │ │ - │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Symbolizer/Text.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Symbolizer.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Symbolizer.Text │ │ │ │ - * A symbolizer used to render text labels for features. │ │ │ │ - */ │ │ │ │ -OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: label │ │ │ │ - * {String} The text for the label. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: fontFamily │ │ │ │ - * {String} The font family for the label. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: fontSize │ │ │ │ - * {String} The font size for the label. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: fontWeight │ │ │ │ - * {String} The font weight for the label. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: fontStyle │ │ │ │ - * {String} The font style for the label. │ │ │ │ - * │ │ │ │ - * No default set here. Use OpenLayers.Renderer.defaultRenderer for defaults. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Symbolizer.Text │ │ │ │ - * Create a symbolizer for rendering text labels. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * config - {Object} An object containing properties to be set on the │ │ │ │ - * symbolizer. Any documented symbolizer property can be set at │ │ │ │ - * construction. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * A new text symbolizer. │ │ │ │ - */ │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Text" │ │ │ │ - │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Symbolizer/Raster.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Symbolizer.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Symbolizer.Raster │ │ │ │ - * A symbolizer used to render raster images. │ │ │ │ - */ │ │ │ │ -OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Symbolizer.Raster │ │ │ │ - * Create a symbolizer for rendering rasters. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * config - {Object} An object containing properties to be set on the │ │ │ │ - * symbolizer. Any documented symbolizer property can be set at │ │ │ │ - * construction. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * A new raster symbolizer. │ │ │ │ - */ │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Raster" │ │ │ │ - │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Style2.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Rule.js │ │ │ │ - * @requires OpenLayers/Symbolizer/Point.js │ │ │ │ - * @requires OpenLayers/Symbolizer/Line.js │ │ │ │ - * @requires OpenLayers/Symbolizer/Polygon.js │ │ │ │ - * @requires OpenLayers/Symbolizer/Text.js │ │ │ │ - * @requires OpenLayers/Symbolizer/Raster.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Style2 │ │ │ │ - * This class represents a collection of rules for rendering features. │ │ │ │ - */ │ │ │ │ -OpenLayers.Style2 = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: id │ │ │ │ - * {String} A unique id for this session. │ │ │ │ - */ │ │ │ │ - id: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: name │ │ │ │ - * {String} Style identifier. │ │ │ │ - */ │ │ │ │ - name: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: title │ │ │ │ - * {String} Title of this style. │ │ │ │ - */ │ │ │ │ - title: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: description │ │ │ │ - * {String} Description of this style. │ │ │ │ - */ │ │ │ │ - description: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: layerName │ │ │ │ - * {} 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: │ │ │ │ - * {} A new style object. │ │ │ │ - */ │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Util.extend(this, config); │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * nullify references to prevent circular references and memory leaks │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - for (var i = 0, len = this.rules.length; i < len; i++) { │ │ │ │ - this.rules[i].destroy(); │ │ │ │ - } │ │ │ │ - delete this.rules; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Clones this style. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {} Clone of this style. │ │ │ │ - */ │ │ │ │ - clone: function() { │ │ │ │ - var config = OpenLayers.Util.extend({}, this); │ │ │ │ - // clone rules │ │ │ │ - if (this.rules) { │ │ │ │ - config.rules = []; │ │ │ │ - for (var i = 0, len = this.rules.length; i < len; ++i) { │ │ │ │ - config.rules.push(this.rules[i].clone()); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return new OpenLayers.Style2(config); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Style2" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WPSDescribeProcess.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WPSDescribeProcess │ │ │ │ - * Read WPS DescribeProcess responses. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.XML, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: VERSION │ │ │ │ - * {String} 1.0.0 │ │ │ │ - */ │ │ │ │ - VERSION: "1.0.0", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ - */ │ │ │ │ - namespaces: { │ │ │ │ - wps: "http://www.opengis.net/wps/1.0.0", │ │ │ │ - ows: "http://www.opengis.net/ows/1.1", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} Schema location │ │ │ │ - */ │ │ │ │ - schemaLocation: "http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ - */ │ │ │ │ - defaultPrefix: "wps", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: regExes │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ - */ │ │ │ │ - regExes: { │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WPSDescribeProcess │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Parse a WPS DescribeProcess and return an object with its information. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} │ │ │ │ - */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - } │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ - data = data.documentElement; │ │ │ │ - } │ │ │ │ - var info = {}; │ │ │ │ - this.readNode(data, info); │ │ │ │ - return info; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wps": { │ │ │ │ - "ProcessDescriptions": function(node, obj) { │ │ │ │ - obj.processDescriptions = {}; │ │ │ │ - this.readChildNodes(node, obj.processDescriptions); │ │ │ │ - }, │ │ │ │ - "ProcessDescription": function(node, processDescriptions) { │ │ │ │ - var processVersion = this.getAttributeNS(node, this.namespaces.wps, "processVersion"); │ │ │ │ - var processDescription = { │ │ │ │ - processVersion: processVersion, │ │ │ │ - statusSupported: (node.getAttribute("statusSupported") === "true"), │ │ │ │ - storeSupported: (node.getAttribute("storeSupported") === "true") │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, processDescription); │ │ │ │ - processDescriptions[processDescription.identifier] = processDescription; │ │ │ │ - }, │ │ │ │ - "DataInputs": function(node, processDescription) { │ │ │ │ - processDescription.dataInputs = []; │ │ │ │ - this.readChildNodes(node, processDescription.dataInputs); │ │ │ │ - }, │ │ │ │ - "ProcessOutputs": function(node, processDescription) { │ │ │ │ - processDescription.processOutputs = []; │ │ │ │ - this.readChildNodes(node, processDescription.processOutputs); │ │ │ │ - }, │ │ │ │ - "Output": function(node, processOutputs) { │ │ │ │ - var output = {}; │ │ │ │ - this.readChildNodes(node, output); │ │ │ │ - processOutputs.push(output); │ │ │ │ - }, │ │ │ │ - "ComplexOutput": function(node, output) { │ │ │ │ - output.complexOutput = {}; │ │ │ │ - this.readChildNodes(node, output.complexOutput); │ │ │ │ - }, │ │ │ │ - "LiteralOutput": function(node, output) { │ │ │ │ - output.literalOutput = {}; │ │ │ │ - this.readChildNodes(node, output.literalOutput); │ │ │ │ - }, │ │ │ │ - "Input": function(node, dataInputs) { │ │ │ │ - var input = { │ │ │ │ - maxOccurs: parseInt(node.getAttribute("maxOccurs")), │ │ │ │ - minOccurs: parseInt(node.getAttribute("minOccurs")) │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, input); │ │ │ │ - dataInputs.push(input); │ │ │ │ - }, │ │ │ │ - "BoundingBoxData": function(node, input) { │ │ │ │ - input.boundingBoxData = {}; │ │ │ │ - this.readChildNodes(node, input.boundingBoxData); │ │ │ │ - }, │ │ │ │ - "CRS": function(node, obj) { │ │ │ │ - if (!obj.CRSs) { │ │ │ │ - obj.CRSs = {}; │ │ │ │ - } │ │ │ │ - obj.CRSs[this.getChildValue(node)] = true; │ │ │ │ - }, │ │ │ │ - "LiteralData": function(node, input) { │ │ │ │ - input.literalData = {}; │ │ │ │ - this.readChildNodes(node, input.literalData); │ │ │ │ - }, │ │ │ │ - "ComplexData": function(node, input) { │ │ │ │ - input.complexData = {}; │ │ │ │ - this.readChildNodes(node, input.complexData); │ │ │ │ - }, │ │ │ │ - "Default": function(node, complexData) { │ │ │ │ - complexData["default"] = {}; │ │ │ │ - this.readChildNodes(node, complexData["default"]); │ │ │ │ - }, │ │ │ │ - "Supported": function(node, complexData) { │ │ │ │ - complexData["supported"] = {}; │ │ │ │ - this.readChildNodes(node, complexData["supported"]); │ │ │ │ - }, │ │ │ │ - "Format": function(node, obj) { │ │ │ │ - var format = {}; │ │ │ │ - this.readChildNodes(node, format); │ │ │ │ - if (!obj.formats) { │ │ │ │ - obj.formats = {}; │ │ │ │ - } │ │ │ │ - obj.formats[format.mimeType] = true; │ │ │ │ - }, │ │ │ │ - "MimeType": function(node, format) { │ │ │ │ - format.mimeType = this.getChildValue(node); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WPSDescribeProcess" │ │ │ │ - │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/WPSClient.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/SingleFile.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ - * @requires OpenLayers/WPSProcess.js │ │ │ │ - * @requires OpenLayers/Format/WPSDescribeProcess.js │ │ │ │ - * @requires OpenLayers/Request.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.WPSClient │ │ │ │ - * High level API for interaction with Web Processing Services (WPS). │ │ │ │ - * An 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: servers │ │ │ │ - * {Object} Service metadata, keyed by a local identifier. │ │ │ │ - * │ │ │ │ - * Properties: │ │ │ │ - * url - {String} the url of the server │ │ │ │ - * version - {String} WPS version of the server │ │ │ │ - * processDescription - {Object} Cache of raw DescribeProcess │ │ │ │ - * responses, keyed by process identifier. │ │ │ │ - */ │ │ │ │ - servers: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: version │ │ │ │ - * {String} The default WPS version to use if none is configured. Default │ │ │ │ - * is '1.0.0'. │ │ │ │ - */ │ │ │ │ - version: '1.0.0', │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: lazy │ │ │ │ - * {Boolean} Should the DescribeProcess be deferred until a process is │ │ │ │ - * fully configured? Default is false. │ │ │ │ - */ │ │ │ │ - lazy: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: events │ │ │ │ - * {} │ │ │ │ - * │ │ │ │ - * Supported event types: │ │ │ │ - * describeprocess - Fires when the process description is available. │ │ │ │ - * Listeners receive an object with a 'raw' property holding the raw │ │ │ │ - * DescribeProcess response, and an 'identifier' property holding the │ │ │ │ - * process identifier of the described process. │ │ │ │ - */ │ │ │ │ - events: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.WPSClient │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Object whose properties will be set on the instance. │ │ │ │ - * │ │ │ │ - * Avaliable options: │ │ │ │ - * servers - {Object} Mandatory. Service metadata, keyed by a local │ │ │ │ - * identifier. Can either be a string with the service url or an │ │ │ │ - * object literal with additional metadata: │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * servers: { │ │ │ │ - * local: '/geoserver/wps' │ │ │ │ - * }, { │ │ │ │ - * opengeo: { │ │ │ │ - * url: 'http://demo.opengeo.org/geoserver/wps', │ │ │ │ - * version: '1.0.0' │ │ │ │ - * } │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * lazy - {Boolean} Optional. Set to true if DescribeProcess should not be │ │ │ │ - * requested until a process is fully configured. Default is false. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.events = new OpenLayers.Events(this); │ │ │ │ - this.servers = {}; │ │ │ │ - for (var s in options.servers) { │ │ │ │ - this.servers[s] = typeof options.servers[s] == 'string' ? { │ │ │ │ - url: options.servers[s], │ │ │ │ - version: this.version, │ │ │ │ - processDescription: {} │ │ │ │ - } : options.servers[s]; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: execute │ │ │ │ - * Shortcut to execute a process with a single function call. This is │ │ │ │ - * equivalent to using 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 │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getProcess │ │ │ │ - * Creates an . │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * serverID - {String} Local identifier from the servers that this instance │ │ │ │ - * was constructed with. │ │ │ │ - * processID - {String} Process identifier known to the server. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {} │ │ │ │ - */ │ │ │ │ - getProcess: function(serverID, processID) { │ │ │ │ - var process = new OpenLayers.WPSProcess({ │ │ │ │ - client: this, │ │ │ │ - server: serverID, │ │ │ │ - identifier: processID │ │ │ │ - }); │ │ │ │ - if (!this.lazy) { │ │ │ │ - process.describe(); │ │ │ │ - } │ │ │ │ - return process; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: describeProcess │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * serverID - {String} Identifier of the server │ │ │ │ - * processID - {String} Identifier of the requested process │ │ │ │ - * callback - {Function} Callback to call when the description is available │ │ │ │ - * scope - {Object} Optional execution scope for the callback function │ │ │ │ - */ │ │ │ │ - describeProcess: function(serverID, processID, callback, scope) { │ │ │ │ - var server = this.servers[serverID]; │ │ │ │ - if (!server.processDescription[processID]) { │ │ │ │ - if (!(processID in server.processDescription)) { │ │ │ │ - // set to null so we know a describeFeature request is pending │ │ │ │ - server.processDescription[processID] = null; │ │ │ │ - OpenLayers.Request.GET({ │ │ │ │ - url: server.url, │ │ │ │ - params: { │ │ │ │ - SERVICE: 'WPS', │ │ │ │ - VERSION: server.version, │ │ │ │ - REQUEST: 'DescribeProcess', │ │ │ │ - IDENTIFIER: processID │ │ │ │ - }, │ │ │ │ - success: function(response) { │ │ │ │ - server.processDescription[processID] = response.responseText; │ │ │ │ - this.events.triggerEvent('describeprocess', { │ │ │ │ - identifier: processID, │ │ │ │ - raw: response.responseText │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - // pending request │ │ │ │ - this.events.register('describeprocess', this, function describe(evt) { │ │ │ │ - if (evt.identifier === processID) { │ │ │ │ - this.events.unregister('describeprocess', this, describe); │ │ │ │ - callback.call(scope, evt.raw); │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - window.setTimeout(function() { │ │ │ │ - callback.call(scope, server.processDescription[processID]); │ │ │ │ - }, 0); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - this.events.destroy(); │ │ │ │ - this.events = null; │ │ │ │ - this.servers = null; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: 'OpenLayers.WPSClient' │ │ │ │ - │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/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, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: groupDiv │ │ │ │ - * {DOMElement} First and only child of 'div'. The group Div contains the │ │ │ │ - * 'contentDiv' and the 'closeDiv'. │ │ │ │ - */ │ │ │ │ - groupDiv: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: closeDiv │ │ │ │ - * {DOMElement} the optional closer image │ │ │ │ - */ │ │ │ │ - closeDiv: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: autoSize │ │ │ │ - * {Boolean} Resize the popup to auto-fit the contents. │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - autoSize: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: minSize │ │ │ │ - * {} 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. │ │ │ │ - */ │ │ │ │ - disableFirefoxOverflowHack: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: fixPadding │ │ │ │ - * To be removed in 3.0, this function merely helps us to deal with the │ │ │ │ - * case where the user may have set an integer value for padding, │ │ │ │ - * instead of an object. │ │ │ │ - */ │ │ │ │ - fixPadding: function() { │ │ │ │ - if (typeof this.padding == "number") { │ │ │ │ - this.padding = new OpenLayers.Bounds( │ │ │ │ - this.padding, this.padding, this.padding, this.padding │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: panMapIfOutOfView │ │ │ │ - * {Boolean} When drawn, pan map such that the entire popup is visible in │ │ │ │ - * the current viewport (if necessary). │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - panMapIfOutOfView: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: keepInMap │ │ │ │ - * {Boolean} If panMapIfOutOfView is false, and this property is true, │ │ │ │ - * contrain the popup such that it always fits in the available map │ │ │ │ - * space. By default, this is not set on the base class. If you are │ │ │ │ - * creating popups that are near map edges and not allowing pannning, │ │ │ │ - * and especially if you have a popup which has a │ │ │ │ - * fixedRelativePosition, setting this to false may be a smart thing to │ │ │ │ - * do. Subclasses may want to override this setting. │ │ │ │ - * │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - keepInMap: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: closeOnMove │ │ │ │ - * {Boolean} When map pans, close the popup. │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - closeOnMove: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: map │ │ │ │ - * {} 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. │ │ │ │ - */ │ │ │ │ - initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) { │ │ │ │ - if (id == null) { │ │ │ │ - id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.id = id; │ │ │ │ - this.lonlat = lonlat; │ │ │ │ - │ │ │ │ - this.contentSize = (contentSize != null) ? contentSize : │ │ │ │ - new OpenLayers.Size( │ │ │ │ - OpenLayers.Popup.WIDTH, │ │ │ │ - OpenLayers.Popup.HEIGHT); │ │ │ │ - if (contentHTML != null) { │ │ │ │ - this.contentHTML = contentHTML; │ │ │ │ - } │ │ │ │ - this.backgroundColor = OpenLayers.Popup.COLOR; │ │ │ │ - this.opacity = OpenLayers.Popup.OPACITY; │ │ │ │ - this.border = OpenLayers.Popup.BORDER; │ │ │ │ - │ │ │ │ - this.div = OpenLayers.Util.createDiv(this.id, null, null, │ │ │ │ - null, null, null, "hidden"); │ │ │ │ - this.div.className = this.displayClass; │ │ │ │ - │ │ │ │ - var groupDivId = this.id + "_GroupDiv"; │ │ │ │ - this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null, │ │ │ │ - null, "relative", null, │ │ │ │ - "hidden"); │ │ │ │ - │ │ │ │ - var id = this.div.id + "_contentDiv"; │ │ │ │ - this.contentDiv = OpenLayers.Util.createDiv(id, null, this.contentSize.clone(), │ │ │ │ - null, "relative"); │ │ │ │ - this.contentDiv.className = this.contentDisplayClass; │ │ │ │ - this.groupDiv.appendChild(this.contentDiv); │ │ │ │ - this.div.appendChild(this.groupDiv); │ │ │ │ - │ │ │ │ - if (closeBox) { │ │ │ │ - this.addCloseBox(closeBoxCallback); │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.registerEvents(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * nullify references to prevent circular references and memory leaks │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - │ │ │ │ - this.id = null; │ │ │ │ - this.lonlat = null; │ │ │ │ - this.size = null; │ │ │ │ - this.contentHTML = null; │ │ │ │ + this.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); │ │ │ │ @@ -33427,5330 +30902,4805 @@ │ │ │ │ │ │ │ │ OpenLayers.Popup.WIDTH = 200; │ │ │ │ OpenLayers.Popup.HEIGHT = 200; │ │ │ │ OpenLayers.Popup.COLOR = "white"; │ │ │ │ OpenLayers.Popup.OPACITY = 1; │ │ │ │ OpenLayers.Popup.BORDER = "0px"; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Kinetic.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/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Animation.js │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ */ │ │ │ │ │ │ │ │ -OpenLayers.Kinetic = OpenLayers.Class({ │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.HTTPRequest │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: threshold │ │ │ │ - * In most cases changing the threshold isn't needed. │ │ │ │ - * In px/ms, default to 0. │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - threshold: 0, │ │ │ │ + URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: deceleration │ │ │ │ - * {Float} the deseleration in px/ms², default to 0.0035. │ │ │ │ + /** │ │ │ │ + * Property: url │ │ │ │ + * {Array(String) or String} This is either an array of url strings or │ │ │ │ + * a single url string. │ │ │ │ */ │ │ │ │ - deceleration: 0.0035, │ │ │ │ + url: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: nbPoints │ │ │ │ - * {Integer} the number of points we use to calculate the kinetic │ │ │ │ - * initial values. │ │ │ │ + /** │ │ │ │ + * Property: params │ │ │ │ + * {Object} Hashtable of key/value parameters │ │ │ │ */ │ │ │ │ - nbPoints: 100, │ │ │ │ + params: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: delay │ │ │ │ - * {Float} time to consider to calculate the kinetic initial values. │ │ │ │ - * In ms, default to 200. │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ + * │ │ │ │ */ │ │ │ │ - delay: 200, │ │ │ │ + reproject: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: points │ │ │ │ - * List of points use to calculate the kinetic initial values. │ │ │ │ + * 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 │ │ │ │ */ │ │ │ │ - points: undefined, │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: timerId │ │ │ │ - * ID of the timer. │ │ │ │ + * APIMethod: destroy │ │ │ │ */ │ │ │ │ - timerId: undefined, │ │ │ │ + destroy: function() { │ │ │ │ + this.url = null; │ │ │ │ + this.params = null; │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Kinetic │ │ │ │ - * │ │ │ │ + * APIMethod: clone │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} │ │ │ │ + * obj - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {} An exact clone of this │ │ │ │ + * │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ + 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; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: begin │ │ │ │ - * Begins the dragging. │ │ │ │ + /** │ │ │ │ + * APIMethod: setUrl │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * newUrl - {String} │ │ │ │ */ │ │ │ │ - begin: function() { │ │ │ │ - OpenLayers.Animation.stop(this.timerId); │ │ │ │ - this.timerId = undefined; │ │ │ │ - this.points = []; │ │ │ │ + setUrl: function(newUrl) { │ │ │ │ + this.url = newUrl; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: update │ │ │ │ - * Updates during the dragging. │ │ │ │ - * │ │ │ │ + * APIMethod: mergeNewParams │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * xy - {} The new position. │ │ │ │ + * newParams - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * redrawn: {Boolean} whether the layer was actually redrawn. │ │ │ │ */ │ │ │ │ - update: function(xy) { │ │ │ │ - this.points.unshift({ │ │ │ │ - xy: xy, │ │ │ │ - tick: new Date().getTime() │ │ │ │ - }); │ │ │ │ - if (this.points.length > this.nbPoints) { │ │ │ │ - this.points.pop(); │ │ │ │ + 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; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: end │ │ │ │ - * Ends the dragging, start the kinetic. │ │ │ │ + * APIMethod: redraw │ │ │ │ + * Redraws the layer. Returns true if the layer was redrawn, false if not. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * xy - {} The last position. │ │ │ │ + * force - {Boolean} Force redraw by adding random parameter. │ │ │ │ * │ │ │ │ * 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. │ │ │ │ + * {Boolean} The layer was redrawn. │ │ │ │ */ │ │ │ │ - end: function(xy) { │ │ │ │ - var last, now = new Date().getTime(); │ │ │ │ - for (var i = 0, l = this.points.length, point; i < l; i++) { │ │ │ │ - point = this.points[i]; │ │ │ │ - if (now - point.tick > this.delay) { │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - last = point; │ │ │ │ - } │ │ │ │ - if (!last) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - var time = new Date().getTime() - last.tick; │ │ │ │ - var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) + │ │ │ │ - Math.pow(xy.y - last.xy.y, 2)); │ │ │ │ - var speed = dist / time; │ │ │ │ - if (speed == 0 || speed < this.threshold) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - var theta = Math.asin((xy.y - last.xy.y) / dist); │ │ │ │ - if (last.xy.x <= xy.x) { │ │ │ │ - theta = Math.PI - theta; │ │ │ │ + redraw: function(force) { │ │ │ │ + if (force) { │ │ │ │ + return this.mergeNewParams({ │ │ │ │ + "_olSalt": Math.random() │ │ │ │ + }); │ │ │ │ + } else { │ │ │ │ + return OpenLayers.Layer.prototype.redraw.apply(this, []); │ │ │ │ } │ │ │ │ - return { │ │ │ │ - speed: speed, │ │ │ │ - theta: theta │ │ │ │ - }; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: move │ │ │ │ - * Launch the kinetic move pan. │ │ │ │ + * 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: │ │ │ │ - * 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). │ │ │ │ + * paramString - {String} │ │ │ │ + * urls - {Array(String)} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} An entry from the urls array, deterministically selected based │ │ │ │ + * on the paramString. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + 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)]; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var timerCallback = function() { │ │ │ │ - if (this.timerId == null) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: getFullRequestString │ │ │ │ + * Combine url with layer's params and these newParams. │ │ │ │ + * │ │ │ │ + * does checking on the serverPath variable, allowing for cases when it │ │ │ │ + * is supplied with trailing ? or &, as well as cases where not. │ │ │ │ + * │ │ │ │ + * return in formatted string like this: │ │ │ │ + * "server?key1=value1&key2=value2&key3=value3" │ │ │ │ + * │ │ │ │ + * WARNING: The altUrl parameter is deprecated and will be removed in 3.0. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * newParams - {Object} │ │ │ │ + * altUrl - {String} Use this as the url instead of the layer's url │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} │ │ │ │ + */ │ │ │ │ + getFullRequestString: function(newParams, altUrl) { │ │ │ │ │ │ │ │ - var t = new Date().getTime() - initialTime; │ │ │ │ + // if not altUrl passed in, use layer's url │ │ │ │ + var url = altUrl || this.url; │ │ │ │ │ │ │ │ - var p = (-this.deceleration * Math.pow(t, 2)) / 2.0 + v0 * t; │ │ │ │ - var x = p * fx; │ │ │ │ - var y = p * fy; │ │ │ │ + // 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); │ │ │ │ │ │ │ │ - var args = {}; │ │ │ │ - args.end = false; │ │ │ │ - var v = -this.deceleration * t + v0; │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ │ │ │ │ - if (v <= 0) { │ │ │ │ - OpenLayers.Animation.stop(this.timerId); │ │ │ │ - this.timerId = null; │ │ │ │ - args.end = true; │ │ │ │ + // 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); │ │ │ │ │ │ │ │ - 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) │ │ │ │ - ); │ │ │ │ + return OpenLayers.Util.urlAppend(url, paramsString); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Kinetic" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.HTTPRequest" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/Markers.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/Layer.js │ │ │ │ + * @requires OpenLayers/Tile.js │ │ │ │ + * @requires OpenLayers/Animation.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.Markers │ │ │ │ - * │ │ │ │ + * 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.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ +OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {} An events object that handles all │ │ │ │ + * events on the tile. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * tile.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to the events): │ │ │ │ + * beforeload - Triggered before an image is prepared for loading, when the │ │ │ │ + * url for the image is known already. Listeners may call on │ │ │ │ + * the tile instance. If they do so, that image will be used and no new │ │ │ │ + * one will be created. │ │ │ │ + */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} Markers layer is never a base layer. │ │ │ │ + * APIProperty: url │ │ │ │ + * {String} The URL of the image being requested. No default. Filled in by │ │ │ │ + * layer.getURL() function. May be modified by loadstart listeners. │ │ │ │ */ │ │ │ │ - isBaseLayer: false, │ │ │ │ + url: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: markers │ │ │ │ - * {Array()} internal marker list │ │ │ │ + * Property: imgDiv │ │ │ │ + * {HTMLImageElement} The image for this tile. │ │ │ │ */ │ │ │ │ - markers: null, │ │ │ │ + imgDiv: null, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Property: frame │ │ │ │ + * {DOMElement} The image element is appended to the frame. Any gutter on │ │ │ │ + * the image will be hidden behind the frame. If no gutter is set, │ │ │ │ + * this will be null. │ │ │ │ + */ │ │ │ │ + frame: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: drawn │ │ │ │ - * {Boolean} internal state of drawing. This is a workaround for the fact │ │ │ │ - * that the map does not call moveTo with a zoomChanged when the map is │ │ │ │ - * first starting up. This lets us catch the case where we have *never* │ │ │ │ - * drawn the layer, and draw it even if the zoom hasn't changed. │ │ │ │ + * Property: imageReloadAttempts │ │ │ │ + * {Integer} Attempts to load the image. │ │ │ │ */ │ │ │ │ - drawn: false, │ │ │ │ + imageReloadAttempts: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.Markers │ │ │ │ - * Create a Markers layer. │ │ │ │ + * Property: layerAlphaHack │ │ │ │ + * {Boolean} True if the png alpha hack needs to be applied on the layer's div. │ │ │ │ + */ │ │ │ │ + layerAlphaHack: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: asyncRequestId │ │ │ │ + * {Integer} ID of an request to see if request is still valid. This is a │ │ │ │ + * number which increments by 1 for each asynchronous request. │ │ │ │ + */ │ │ │ │ + asyncRequestId: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: maxGetUrlLength │ │ │ │ + * {Number} If set, requests that would result in GET urls with more │ │ │ │ + * characters than the number provided will be made using form-encoded │ │ │ │ + * HTTP POST. It is good practice to avoid urls that are longer than 2048 │ │ │ │ + * characters. │ │ │ │ * │ │ │ │ + * Caution: │ │ │ │ + * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most │ │ │ │ + * Opera versions do not fully support this option. On all browsers, │ │ │ │ + * transition effects are not supported if POST requests are used. │ │ │ │ + */ │ │ │ │ + maxGetUrlLength: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: canvasContext │ │ │ │ + * {CanvasRenderingContext2D} A canvas context associated with │ │ │ │ + * the tile image. │ │ │ │ + */ │ │ │ │ + canvasContext: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: crossOriginKeyword │ │ │ │ + * The value of the crossorigin keyword to use when loading images. This is │ │ │ │ + * only relevant when using for tiles from remote │ │ │ │ + * origins and should be set to either 'anonymous' or 'use-credentials' │ │ │ │ + * for servers that send Access-Control-Allow-Origin headers with their │ │ │ │ + * tiles. │ │ │ │ + */ │ │ │ │ + crossOriginKeyword: null, │ │ │ │ + │ │ │ │ + /** TBD 3.0 - reorder the parameters to the init function to remove │ │ │ │ + * URL. the getUrl() function on the layer gets called on │ │ │ │ + * each draw(), so no need to specify it here. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Tile.Image │ │ │ │ + * Constructor for a new instance. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * layer - {} layer that the tile will go in. │ │ │ │ + * position - {} │ │ │ │ + * bounds - {} │ │ │ │ + * url - {} Deprecated. Remove me in 3.0. │ │ │ │ + * size - {} │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - initialize: function(name, options) { │ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ - this.markers = []; │ │ │ │ + initialize: function(layer, position, bounds, url, size, options) { │ │ │ │ + OpenLayers.Tile.prototype.initialize.apply(this, arguments); │ │ │ │ + │ │ │ │ + this.url = url; //deprecated remove me │ │ │ │ + │ │ │ │ + this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack(); │ │ │ │ + │ │ │ │ + if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) { │ │ │ │ + // only create frame if it's needed │ │ │ │ + this.frame = document.createElement("div"); │ │ │ │ + this.frame.style.position = "absolute"; │ │ │ │ + this.frame.style.overflow = "hidden"; │ │ │ │ + } │ │ │ │ + if (this.maxGetUrlLength != null) { │ │ │ │ + OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * nullify references to prevent circular references and memory leaks │ │ │ │ */ │ │ │ │ destroy: function() { │ │ │ │ - this.clearMarkers(); │ │ │ │ - this.markers = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ + 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); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setOpacity │ │ │ │ - * Sets the opacity for all the markers. │ │ │ │ + * Method: draw │ │ │ │ + * Check that a tile should be drawn, and draw it. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * opacity - {Float} │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned │ │ │ │ + * false. │ │ │ │ */ │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ - if (opacity != this.opacity) { │ │ │ │ - this.opacity = opacity; │ │ │ │ - for (var i = 0, len = this.markers.length; i < len; i++) { │ │ │ │ - this.markers[i].setOpacity(this.opacity); │ │ │ │ + draw: function() { │ │ │ │ + var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments); │ │ │ │ + if (shouldDraw) { │ │ │ │ + // The layer's reproject option is deprecated. │ │ │ │ + if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { │ │ │ │ + // getBoundsFromBaseLayer is defined in deprecated.js. │ │ │ │ + this.bounds = this.getBoundsFromBaseLayer(this.position); │ │ │ │ + } │ │ │ │ + if (this.isLoading) { │ │ │ │ + //if we're already loading, send 'reload' instead of 'loadstart'. │ │ │ │ + this._loadEvent = "reload"; │ │ │ │ + } else { │ │ │ │ + this.isLoading = true; │ │ │ │ + this._loadEvent = "loadstart"; │ │ │ │ } │ │ │ │ + this.renderTile(); │ │ │ │ + this.positionTile(); │ │ │ │ + } else if (shouldDraw === false) { │ │ │ │ + this.unload(); │ │ │ │ } │ │ │ │ + return shouldDraw; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {} │ │ │ │ - * zoomChanged - {Boolean} │ │ │ │ - * dragging - {Boolean} │ │ │ │ + /** │ │ │ │ + * Method: renderTile │ │ │ │ + * Internal function to actually initialize the image tile, │ │ │ │ + * position it correctly, and set its url. │ │ │ │ */ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ + 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(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (zoomChanged || !this.drawn) { │ │ │ │ - for (var i = 0, len = this.markers.length; i < len; i++) { │ │ │ │ - this.drawMarker(this.markers[i]); │ │ │ │ + /** │ │ │ │ + * Method: positionTile │ │ │ │ + * Using the properties currenty set on the layer, position the tile correctly. │ │ │ │ + * This method is used both by the async and non-async versions of the Tile.Image │ │ │ │ + * code. │ │ │ │ + */ │ │ │ │ + positionTile: function() { │ │ │ │ + var style = this.getTile().style, │ │ │ │ + size = this.frame ? this.size : │ │ │ │ + this.layer.getImageSize(this.bounds), │ │ │ │ + ratio = 1; │ │ │ │ + if (this.layer instanceof OpenLayers.Layer.Grid) { │ │ │ │ + ratio = this.layer.getServerResolution() / this.layer.map.getResolution(); │ │ │ │ + } │ │ │ │ + style.left = this.position.x + "px"; │ │ │ │ + style.top = this.position.y + "px"; │ │ │ │ + style.width = Math.round(ratio * size.w) + "px"; │ │ │ │ + style.height = Math.round(ratio * size.h) + "px"; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: clear │ │ │ │ + * Remove the tile from the DOM, clear it of any image related data so that │ │ │ │ + * it can be reused in a new location. │ │ │ │ + */ │ │ │ │ + clear: function() { │ │ │ │ + OpenLayers.Tile.prototype.clear.apply(this, arguments); │ │ │ │ + var img = this.imgDiv; │ │ │ │ + if (img) { │ │ │ │ + var tile = this.getTile(); │ │ │ │ + if (tile.parentNode === this.layer.div) { │ │ │ │ + this.layer.div.removeChild(tile); │ │ │ │ } │ │ │ │ - this.drawn = true; │ │ │ │ + this.setImgSrc(); │ │ │ │ + if (this.layerAlphaHack === true) { │ │ │ │ + img.style.filter = ""; │ │ │ │ + } │ │ │ │ + OpenLayers.Element.removeClass(img, "olImageLoadError"); │ │ │ │ } │ │ │ │ + this.canvasContext = null; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: addMarker │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * marker - {} │ │ │ │ + * Method: getImage │ │ │ │ + * Returns or creates and returns the tile image. │ │ │ │ */ │ │ │ │ - addMarker: function(marker) { │ │ │ │ - this.markers.push(marker); │ │ │ │ + getImage: function() { │ │ │ │ + if (!this.imgDiv) { │ │ │ │ + this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false); │ │ │ │ │ │ │ │ - if (this.opacity < 1) { │ │ │ │ - marker.setOpacity(this.opacity); │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ } │ │ │ │ │ │ │ │ - if (this.map && this.map.getExtent()) { │ │ │ │ - marker.map = this.map; │ │ │ │ - this.drawMarker(marker); │ │ │ │ - } │ │ │ │ + return this.imgDiv; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: removeMarker │ │ │ │ + * APIMethod: setImage │ │ │ │ + * Sets the image element for this tile. This method should only be called │ │ │ │ + * from beforeload listeners. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * marker - {} │ │ │ │ + * Parameters │ │ │ │ + * img - {HTMLImageElement} The image to use for this tile. │ │ │ │ */ │ │ │ │ - removeMarker: function(marker) { │ │ │ │ - if (this.markers && this.markers.length) { │ │ │ │ - OpenLayers.Util.removeItem(this.markers, marker); │ │ │ │ - marker.erase(); │ │ │ │ - } │ │ │ │ + setImage: function(img) { │ │ │ │ + this.imgDiv = img; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clearMarkers │ │ │ │ - * This method removes all markers from a layer. The markers are not │ │ │ │ - * destroyed by this function, but are removed from the list of markers. │ │ │ │ + * Method: initImage │ │ │ │ + * Creates the content for the frame on the tile. │ │ │ │ */ │ │ │ │ - clearMarkers: function() { │ │ │ │ - if (this.markers != null) { │ │ │ │ - while (this.markers.length > 0) { │ │ │ │ - this.removeMarker(this.markers[0]); │ │ │ │ + initImage: function() { │ │ │ │ + if (!this.url && !this.imgDiv) { │ │ │ │ + // fast path out - if there is no tile url and no previous image │ │ │ │ + this.isLoading = false; │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + this.events.triggerEvent('beforeload'); │ │ │ │ + this.layer.div.appendChild(this.getTile()); │ │ │ │ + this.events.triggerEvent(this._loadEvent); │ │ │ │ + var img = this.getImage(); │ │ │ │ + var src = img.getAttribute('src') || ''; │ │ │ │ + if (this.url && OpenLayers.Util.isEquivalentUrl(src, this.url)) { │ │ │ │ + this._loadTimeout = window.setTimeout( │ │ │ │ + OpenLayers.Function.bind(this.onImageLoad, this), 0 │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + this.stopLoading(); │ │ │ │ + if (this.crossOriginKeyword) { │ │ │ │ + img.removeAttribute("crossorigin"); │ │ │ │ } │ │ │ │ + OpenLayers.Event.observe(img, "load", │ │ │ │ + OpenLayers.Function.bind(this.onImageLoad, this) │ │ │ │ + ); │ │ │ │ + OpenLayers.Event.observe(img, "error", │ │ │ │ + OpenLayers.Function.bind(this.onImageError, this) │ │ │ │ + ); │ │ │ │ + this.imageReloadAttempts = 0; │ │ │ │ + this.setImgSrc(this.url); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawMarker │ │ │ │ - * Calculate the pixel location for the marker, create it, and │ │ │ │ - * add it to the layer's div │ │ │ │ + /** │ │ │ │ + * Method: setImgSrc │ │ │ │ + * Sets the source for the tile image │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * marker - {} │ │ │ │ + * url - {String} or undefined to hide the image │ │ │ │ */ │ │ │ │ - drawMarker: function(marker) { │ │ │ │ - var px = this.map.getLayerPxFromLonLat(marker.lonlat); │ │ │ │ - if (px == null) { │ │ │ │ - marker.display(false); │ │ │ │ + setImgSrc: function(url) { │ │ │ │ + var img = this.imgDiv; │ │ │ │ + if (url) { │ │ │ │ + img.style.visibility = 'hidden'; │ │ │ │ + img.style.opacity = 0; │ │ │ │ + // don't set crossOrigin if the url is a data URL │ │ │ │ + if (this.crossOriginKeyword) { │ │ │ │ + if (url.substr(0, 5) !== 'data:') { │ │ │ │ + img.setAttribute("crossorigin", this.crossOriginKeyword); │ │ │ │ + } else { │ │ │ │ + img.removeAttribute("crossorigin"); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + img.src = url; │ │ │ │ } else { │ │ │ │ - if (!marker.isDrawn()) { │ │ │ │ - var markerImg = marker.draw(px); │ │ │ │ - this.div.appendChild(markerImg); │ │ │ │ - } else if (marker.icon) { │ │ │ │ - marker.icon.moveTo(px); │ │ │ │ + // 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); │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getDataExtent │ │ │ │ - * Calculates the max extent which includes all of the markers. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: getTile │ │ │ │ + * Get the tile's markup. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {} │ │ │ │ + * {DOMElement} The tile's markup │ │ │ │ */ │ │ │ │ - getDataExtent: function() { │ │ │ │ - var maxExtent = null; │ │ │ │ + getTile: function() { │ │ │ │ + return this.frame ? this.frame : this.getImage(); │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.markers && (this.markers.length > 0)) { │ │ │ │ - var maxExtent = new OpenLayers.Bounds(); │ │ │ │ - for (var i = 0, len = this.markers.length; i < len; i++) { │ │ │ │ - var marker = this.markers[i]; │ │ │ │ - maxExtent.extend(marker.lonlat); │ │ │ │ + /** │ │ │ │ + * 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(); │ │ │ │ } │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - return maxExtent; │ │ │ │ + /** │ │ │ │ + * 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; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Markers" │ │ │ │ + /** │ │ │ │ + * APIMethod: getCanvasContext │ │ │ │ + * Returns a canvas context associated with the tile image (with │ │ │ │ + * the image drawn on it). │ │ │ │ + * Returns undefined if the browser does not support canvas, if │ │ │ │ + * the tile has no image or if it's currently loading. │ │ │ │ + * │ │ │ │ + * The function returns a canvas context instance but the │ │ │ │ + * underlying canvas is still available in the 'canvas' property: │ │ │ │ + * (code) │ │ │ │ + * var context = tile.getCanvasContext(); │ │ │ │ + * if (context) { │ │ │ │ + * var data = context.canvas.toDataURL('image/jpeg'); │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} │ │ │ │ + */ │ │ │ │ + getCanvasContext: function() { │ │ │ │ + if (OpenLayers.CANVAS_SUPPORTED && this.imgDiv && !this.isLoading) { │ │ │ │ + if (!this.canvasContext) { │ │ │ │ + var canvas = document.createElement("canvas"); │ │ │ │ + canvas.width = this.size.w; │ │ │ │ + canvas.height = this.size.h; │ │ │ │ + this.canvasContext = canvas.getContext("2d"); │ │ │ │ + this.canvasContext.drawImage(this.imgDiv, 0, 0); │ │ │ │ + } │ │ │ │ + return this.canvasContext; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Tile.Image" │ │ │ │ + │ │ │ │ }); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Tile.Image.IMAGE │ │ │ │ + * {HTMLImageElement} The image for a tile. │ │ │ │ + */ │ │ │ │ +OpenLayers.Tile.Image.IMAGE = (function() { │ │ │ │ + var img = new Image(); │ │ │ │ + img.className = "olTileImage"; │ │ │ │ + // avoid image gallery menu in IE6 │ │ │ │ + img.galleryImg = "no"; │ │ │ │ + return img; │ │ │ │ +}()); │ │ │ │ + │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/GeoRSS.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/Layer/Markers.js │ │ │ │ - * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ + * @requires OpenLayers/Layer/HTTPRequest.js │ │ │ │ + * @requires OpenLayers/Tile/Image.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.GeoRSS │ │ │ │ - * Add GeoRSS Point features to your map. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Layer.Grid │ │ │ │ + * Base class for layers that use a lattice of tiles. Create a new grid │ │ │ │ + * layer with the constructor. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - │ │ │ │ + * - │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, { │ │ │ │ +OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: location │ │ │ │ - * {String} store url of text file │ │ │ │ + /** │ │ │ │ + * APIProperty: tileSize │ │ │ │ + * {} │ │ │ │ */ │ │ │ │ - location: null, │ │ │ │ + tileSize: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: features │ │ │ │ - * {Array()} │ │ │ │ + /** │ │ │ │ + * 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". │ │ │ │ */ │ │ │ │ - features: null, │ │ │ │ + tileOriginCorner: "bl", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: formatOptions │ │ │ │ - * {Object} Hash of options which should be passed to the format when it is │ │ │ │ - * created. Must be passed in the constructor. │ │ │ │ + * 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``. │ │ │ │ */ │ │ │ │ - formatOptions: null, │ │ │ │ + tileOrigin: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: selectedFeature │ │ │ │ - * {} │ │ │ │ + /** APIProperty: tileOptions │ │ │ │ + * {Object} optional configuration options for instances │ │ │ │ + * created by this Layer, if supported by the tile class. │ │ │ │ */ │ │ │ │ - selectedFeature: null, │ │ │ │ + tileOptions: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: icon │ │ │ │ - * {}. This determines the Icon to be used on the map │ │ │ │ - * for this GeoRSS layer. │ │ │ │ + /** │ │ │ │ + * APIProperty: tileClass │ │ │ │ + * {} The tile class to use for this layer. │ │ │ │ + * Defaults is OpenLayers.Tile.Image. │ │ │ │ */ │ │ │ │ - icon: null, │ │ │ │ + tileClass: OpenLayers.Tile.Image, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: popupSize │ │ │ │ - * {} This determines the size of GeoRSS popups. If │ │ │ │ - * not provided, defaults to 250px by 120px. │ │ │ │ + * Property: grid │ │ │ │ + * {Array(Array())} This is an array of rows, each row is │ │ │ │ + * an array of tiles. │ │ │ │ */ │ │ │ │ - popupSize: null, │ │ │ │ + grid: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: useFeedTitle │ │ │ │ - * {Boolean} Set layer.name to the first element in the feed. Default is true. │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - useFeedTitle: true, │ │ │ │ + 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, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.GeoRSS │ │ │ │ - * Create a GeoRSS Layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * location - {String} │ │ │ │ - * options - {Object} │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - initialize: function(name, location, options) { │ │ │ │ - OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]); │ │ │ │ - this.location = location; │ │ │ │ - this.features = []; │ │ │ │ - }, │ │ │ │ + buffer: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - // Warning: Layer.Markers.destroy() must be called prior to calling │ │ │ │ - // clearFeatures() here, otherwise we leak memory. Indeed, if │ │ │ │ - // Layer.Markers.destroy() is called after clearFeatures(), it won't be │ │ │ │ - // able to remove the marker image elements from the layer's div since │ │ │ │ - // the markers will have been destroyed by clearFeatures(). │ │ │ │ - OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments); │ │ │ │ - this.clearFeatures(); │ │ │ │ - this.features = null; │ │ │ │ - }, │ │ │ │ + transitionEffect: "resize", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: loadRSS │ │ │ │ - * Start the load of the RSS data. Don't do this when we first add the layer, │ │ │ │ - * since we may not be visible at any point, and it would therefore be a waste. │ │ │ │ + * APIProperty: numLoadingTiles │ │ │ │ + * {Integer} How many tiles are still loading? │ │ │ │ */ │ │ │ │ - loadRSS: function() { │ │ │ │ - if (!this.loaded) { │ │ │ │ - this.events.triggerEvent("loadstart"); │ │ │ │ - OpenLayers.Request.GET({ │ │ │ │ - url: this.location, │ │ │ │ - success: this.parseData, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.loaded = true; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + numLoadingTiles: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveTo │ │ │ │ - * If layer is visible and RSS has not been loaded, load RSS. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {Object} │ │ │ │ - * zoomChanged - {Object} │ │ │ │ - * minor - {Object} │ │ │ │ + * Property: serverResolutions │ │ │ │ + * {Array(Number}} This property is documented in subclasses as │ │ │ │ + * an API property. │ │ │ │ */ │ │ │ │ - moveTo: function(bounds, zoomChanged, minor) { │ │ │ │ - OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments); │ │ │ │ - if (this.visibility && !this.loaded) { │ │ │ │ - this.loadRSS(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + serverResolutions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseData │ │ │ │ - * Parse the data returned from the Events call. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>} │ │ │ │ + * Property: loading │ │ │ │ + * {Boolean} Indicates if tiles are being loaded. │ │ │ │ */ │ │ │ │ - parseData: function(ajaxRequest) { │ │ │ │ - var doc = ajaxRequest.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText); │ │ │ │ - } │ │ │ │ + loading: false, │ │ │ │ │ │ │ │ - if (this.useFeedTitle) { │ │ │ │ - var name = null; │ │ │ │ - try { │ │ │ │ - name = doc.getElementsByTagNameNS('*', 'title')[0].firstChild.nodeValue; │ │ │ │ - } catch (e) { │ │ │ │ - try { │ │ │ │ - name = doc.getElementsByTagName('title')[0].firstChild.nodeValue; │ │ │ │ - } catch (e) {} │ │ │ │ - } │ │ │ │ - if (name) { │ │ │ │ - this.setName(name); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: backBuffer │ │ │ │ + * {DOMElement} The back buffer. │ │ │ │ + */ │ │ │ │ + backBuffer: null, │ │ │ │ │ │ │ │ - var options = {}; │ │ │ │ + /** │ │ │ │ + * Property: gridResolution │ │ │ │ + * {Number} The resolution of the current grid. Used for backbuffer and │ │ │ │ + * client zoom. This property is updated every time the grid is │ │ │ │ + * initialized. │ │ │ │ + */ │ │ │ │ + gridResolution: null, │ │ │ │ │ │ │ │ - OpenLayers.Util.extend(options, this.formatOptions); │ │ │ │ + /** │ │ │ │ + * Property: backBufferResolution │ │ │ │ + * {Number} The resolution of the current back buffer. This property is │ │ │ │ + * updated each time a back buffer is created. │ │ │ │ + */ │ │ │ │ + backBufferResolution: null, │ │ │ │ │ │ │ │ - if (this.map && !this.projection.equals(this.map.getProjectionObject())) { │ │ │ │ - options.externalProjection = this.projection; │ │ │ │ - options.internalProjection = this.map.getProjectionObject(); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: backBufferLonLat │ │ │ │ + * {Object} The top-left corner of the current back buffer. Includes lon │ │ │ │ + * and lat properties. This object is updated each time a back buffer │ │ │ │ + * is created. │ │ │ │ + */ │ │ │ │ + backBufferLonLat: null, │ │ │ │ │ │ │ │ - var format = new OpenLayers.Format.GeoRSS(options); │ │ │ │ - var features = format.read(doc); │ │ │ │ + /** │ │ │ │ + * Property: backBufferTimerId │ │ │ │ + * {Number} The id of the back buffer timer. This timer is used to │ │ │ │ + * delay the removal of the back buffer, thereby preventing │ │ │ │ + * flash effects caused by tile animation. │ │ │ │ + */ │ │ │ │ + backBufferTimerId: null, │ │ │ │ │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - var data = {}; │ │ │ │ - var feature = features[i]; │ │ │ │ + /** │ │ │ │ + * APIProperty: removeBackBufferDelay │ │ │ │ + * {Number} Delay for removing the backbuffer when all tiles have finished │ │ │ │ + * loading. Can be set to 0 when no css opacity transitions for the │ │ │ │ + * olTileImage class are used. Default is 0 for <singleTile> layers, │ │ │ │ + * 2500 for tiled layers. See <className> for more information on │ │ │ │ + * tile animation. │ │ │ │ + */ │ │ │ │ + removeBackBufferDelay: null, │ │ │ │ │ │ │ │ - // we don't support features with no geometry in the GeoRSS │ │ │ │ - // layer at this time. │ │ │ │ - if (!feature.geometry) { │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: className │ │ │ │ + * {String} Name of the class added to the layer div. If not set in the │ │ │ │ + * options passed to the constructor then className defaults to │ │ │ │ + * "olLayerGridSingleTile" for single tile layers (see <singleTile>), │ │ │ │ + * and "olLayerGrid" for non single tile layers. │ │ │ │ + * │ │ │ │ + * Note: │ │ │ │ + * │ │ │ │ + * The displaying of tiles is not animated by default for single tile │ │ │ │ + * layers - OpenLayers' default theme (style.css) includes this: │ │ │ │ + * (code) │ │ │ │ + * .olLayerGrid .olTileImage { │ │ │ │ + * -webkit-transition: opacity 0.2s linear; │ │ │ │ + * -moz-transition: opacity 0.2s linear; │ │ │ │ + * -o-transition: opacity 0.2s linear; │ │ │ │ + * transition: opacity 0.2s linear; │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ + * To animate tile displaying for any grid layer the following │ │ │ │ + * CSS rule can be used: │ │ │ │ + * (code) │ │ │ │ + * .olTileImage { │ │ │ │ + * -webkit-transition: opacity 0.2s linear; │ │ │ │ + * -moz-transition: opacity 0.2s linear; │ │ │ │ + * -o-transition: opacity 0.2s linear; │ │ │ │ + * transition: opacity 0.2s linear; │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ + * In that case, to avoid flash effects, <removeBackBufferDelay> │ │ │ │ + * should not be zero. │ │ │ │ + */ │ │ │ │ + className: null, │ │ │ │ │ │ │ │ - var title = feature.attributes.title ? │ │ │ │ - feature.attributes.title : "Untitled"; │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ + */ │ │ │ │ │ │ │ │ - var description = feature.attributes.description ? │ │ │ │ - feature.attributes.description : "No description."; │ │ │ │ + /** │ │ │ │ + * Property: gridLayout │ │ │ │ + * {Object} Object containing properties tilelon, tilelat, startcol, │ │ │ │ + * startrow │ │ │ │ + */ │ │ │ │ + gridLayout: null, │ │ │ │ │ │ │ │ - var link = feature.attributes.link ? feature.attributes.link : ""; │ │ │ │ + /** │ │ │ │ + * 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, │ │ │ │ │ │ │ │ - var location = feature.geometry.getBounds().getCenterLonLat(); │ │ │ │ + /** │ │ │ │ + * Property: transitionendEvents │ │ │ │ + * {Array} Event names for transitionend │ │ │ │ + */ │ │ │ │ + transitionendEvents: [ │ │ │ │ + 'transitionend', 'webkitTransitionEnd', 'otransitionend', │ │ │ │ + 'oTransitionEnd' │ │ │ │ + ], │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Grid │ │ │ │ + * Create a new grid layer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} │ │ │ │ + * url - {String} │ │ │ │ + * params - {Object} │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + */ │ │ │ │ + initialize: function(name, url, params, options) { │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, │ │ │ │ + arguments); │ │ │ │ + this.grid = []; │ │ │ │ + this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this); │ │ │ │ │ │ │ │ - data.icon = this.icon == null ? │ │ │ │ - OpenLayers.Marker.defaultIcon() : │ │ │ │ - this.icon.clone(); │ │ │ │ + this.initProperties(); │ │ │ │ │ │ │ │ - data.popupSize = this.popupSize ? │ │ │ │ - this.popupSize.clone() : │ │ │ │ - new OpenLayers.Size(250, 120); │ │ │ │ + this.rowSign = this.tileOriginCorner.substr(0, 1) === "t" ? 1 : -1; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (title || description) { │ │ │ │ - // we have supplemental data, store them. │ │ │ │ - data.title = title; │ │ │ │ - data.description = description; │ │ │ │ + /** │ │ │ │ + * Method: initProperties │ │ │ │ + * Set any properties that depend on the value of singleTile. │ │ │ │ + * Currently sets removeBackBufferDelay and className │ │ │ │ + */ │ │ │ │ + initProperties: function() { │ │ │ │ + if (this.options.removeBackBufferDelay === undefined) { │ │ │ │ + this.removeBackBufferDelay = this.singleTile ? 0 : 2500; │ │ │ │ + } │ │ │ │ │ │ │ │ - var contentHTML = '<div class="olLayerGeoRSSClose">[x]</div>'; │ │ │ │ - contentHTML += '<div class="olLayerGeoRSSTitle">'; │ │ │ │ - if (link) { │ │ │ │ - contentHTML += '<a class="link" href="' + link + '" target="_blank">'; │ │ │ │ - } │ │ │ │ - contentHTML += title; │ │ │ │ - if (link) { │ │ │ │ - contentHTML += '</a>'; │ │ │ │ - } │ │ │ │ - contentHTML += '</div>'; │ │ │ │ - contentHTML += '<div style="" class="olLayerGeoRSSDescription">'; │ │ │ │ - contentHTML += description; │ │ │ │ - contentHTML += '</div>'; │ │ │ │ - data['popupContentHTML'] = contentHTML; │ │ │ │ - } │ │ │ │ - var feature = new OpenLayers.Feature(this, location, data); │ │ │ │ - this.features.push(feature); │ │ │ │ - var marker = feature.createMarker(); │ │ │ │ - marker.events.register('click', feature, this.markerClick); │ │ │ │ - this.addMarker(marker); │ │ │ │ + if (this.options.className === undefined) { │ │ │ │ + this.className = this.singleTile ? 'olLayerGridSingleTile' : │ │ │ │ + 'olLayerGrid'; │ │ │ │ } │ │ │ │ - this.events.triggerEvent("loadend"); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: markerClick │ │ │ │ + * Method: setMap │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * map - {<OpenLayers.Map>} The map. │ │ │ │ */ │ │ │ │ - markerClick: function(evt) { │ │ │ │ - var sameMarkerClicked = (this == this.layer.selectedFeature); │ │ │ │ - this.layer.selectedFeature = (!sameMarkerClicked) ? this : null; │ │ │ │ - for (var i = 0, len = this.layer.map.popups.length; i < len; i++) { │ │ │ │ - this.layer.map.removePopup(this.layer.map.popups[i]); │ │ │ │ - } │ │ │ │ - if (!sameMarkerClicked) { │ │ │ │ - var popup = this.createPopup(); │ │ │ │ - OpenLayers.Event.observe(popup.div, "click", │ │ │ │ - OpenLayers.Function.bind(function() { │ │ │ │ - for (var i = 0, len = this.layer.map.popups.length; i < len; i++) { │ │ │ │ - this.layer.map.removePopup(this.layer.map.popups[i]); │ │ │ │ - } │ │ │ │ - }, this) │ │ │ │ - ); │ │ │ │ - this.layer.map.addPopup(popup); │ │ │ │ - } │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map); │ │ │ │ + OpenLayers.Element.addClass(this.div, this.className); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clearFeatures │ │ │ │ - * Destroy all features in this layer. │ │ │ │ + * Method: removeMap │ │ │ │ + * Called when the layer is removed from the map. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} The map. │ │ │ │ */ │ │ │ │ - clearFeatures: function() { │ │ │ │ - if (this.features != null) { │ │ │ │ - while (this.features.length > 0) { │ │ │ │ - var feature = this.features[0]; │ │ │ │ - OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ - feature.destroy(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + removeMap: function(map) { │ │ │ │ + this.removeBackBuffer(); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.GeoRSS" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/XYZ.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/Grid.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.XYZ │ │ │ │ - * The XYZ class is designed to make it easier for people who have tiles │ │ │ │ - * arranged by a standard XYZ grid. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * Default is true, as this is designed to be a base tile source. │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Deconstruct the layer and clear the grid. │ │ │ │ */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + destroy: function() { │ │ │ │ + this.removeBackBuffer(); │ │ │ │ + this.clearGrid(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: sphericalMercator │ │ │ │ - * Whether the tile extents should be set to the defaults for │ │ │ │ - * spherical mercator. Useful for things like OpenStreetMap. │ │ │ │ - * Default is false, except for the OSM subclass. │ │ │ │ - */ │ │ │ │ - sphericalMercator: false, │ │ │ │ + this.grid = null; │ │ │ │ + this.tileSize = null; │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: zoomOffset │ │ │ │ - * {Number} If your cache has more zoom levels than you want to provide │ │ │ │ - * access to with this layer, supply a zoomOffset. This zoom offset │ │ │ │ - * is added to the current map zoom level to determine the level │ │ │ │ - * for a requested tile. For example, if you supply a zoomOffset │ │ │ │ - * of 3, when the map is at the zoom 0, tiles will be requested from │ │ │ │ - * level 3 of your cache. Default is 0 (assumes cache level and map │ │ │ │ - * zoom are equivalent). Using <zoomOffset> is an alternative to │ │ │ │ - * setting <serverResolutions> if you only want to expose a subset │ │ │ │ - * of the server resolutions. │ │ │ │ + * APIMethod: mergeNewParams │ │ │ │ + * Refetches tiles with new params merged, keeping a backbuffer. Each │ │ │ │ + * loading new tile will have a css class of '.olTileReplacing'. If a │ │ │ │ + * stylesheet applies a 'display: none' style to that class, any fade-in │ │ │ │ + * transition will not apply, and backbuffers for each tile will be removed │ │ │ │ + * as soon as the tile is loaded. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * newParams - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * redrawn: {Boolean} whether the layer was actually redrawn. │ │ │ │ */ │ │ │ │ - zoomOffset: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: serverResolutions │ │ │ │ - * {Array} A list of all resolutions available on the server. Only set this │ │ │ │ - * property if the map resolutions differ from the server. This │ │ │ │ - * property serves two purposes. (a) <serverResolutions> can include │ │ │ │ - * resolutions that the server supports and that you don't want to │ │ │ │ - * provide with this layer; you can also look at <zoomOffset>, which is │ │ │ │ - * an alternative to <serverResolutions> for that specific purpose. │ │ │ │ - * (b) The map can work with resolutions that aren't supported by │ │ │ │ - * the server, i.e. that aren't in <serverResolutions>. When the │ │ │ │ - * map is displayed in such a resolution data for the closest │ │ │ │ - * server-supported resolution is loaded and the layer div is │ │ │ │ - * stretched as necessary. │ │ │ │ + * Method: clearGrid │ │ │ │ + * Go through and remove all tiles from the grid, calling │ │ │ │ + * destroy() on each of them to kill circular references │ │ │ │ */ │ │ │ │ - serverResolutions: null, │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.XYZ │ │ │ │ - * │ │ │ │ + * APIMethod: addOptions │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * url - {String} │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - if (options && options.sphericalMercator || this.sphericalMercator) { │ │ │ │ - options = OpenLayers.Util.extend({ │ │ │ │ - projection: "EPSG:900913", │ │ │ │ - numZoomLevels: 19 │ │ │ │ - }, options); │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, [ │ │ │ │ - name || this.name, url || this.url, {}, │ │ │ │ - options │ │ │ │ - ]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ * APIMethod: clone │ │ │ │ * Create a clone of this layer │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ * obj - {Object} Is this ever used? │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ │ │ │ │ + * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid │ │ │ │ */ │ │ │ │ clone: function(obj) { │ │ │ │ │ │ │ │ if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.XYZ(this.name, │ │ │ │ + obj = new OpenLayers.Layer.Grid(this.name, │ │ │ │ this.url, │ │ │ │ + this.params, │ │ │ │ this.getOptions()); │ │ │ │ } │ │ │ │ │ │ │ │ //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + 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: getURL │ │ │ │ + * Method: moveTo │ │ │ │ + * This function is called whenever the map is moved. All the moving │ │ │ │ + * of actual 'tiles' is done by the map, but moveTo's role is to accept │ │ │ │ + * a bounds and make sure the data that that bounds requires is pre-loaded. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * zoomChanged - {Boolean} │ │ │ │ + * dragging - {Boolean} │ │ │ │ + */ │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments); │ │ │ │ + │ │ │ │ + bounds = bounds || this.map.getExtent(); │ │ │ │ + │ │ │ │ + if (bounds != null) { │ │ │ │ + │ │ │ │ + // if grid is empty or zoom has changed, we *must* re-tile │ │ │ │ + var forceReTile = !this.grid.length || zoomChanged; │ │ │ │ + │ │ │ │ + // total bounds of the tiles │ │ │ │ + var tilesBounds = this.getTilesBounds(); │ │ │ │ + │ │ │ │ + // the new map resolution │ │ │ │ + var resolution = this.map.getResolution(); │ │ │ │ + │ │ │ │ + // the server-supported resolution for the new map resolution │ │ │ │ + var serverResolution = this.getServerResolution(resolution); │ │ │ │ + │ │ │ │ + if (this.singleTile) { │ │ │ │ + │ │ │ │ + // We want to redraw whenever even the slightest part of the │ │ │ │ + // current bounds is not contained by our tile. │ │ │ │ + // (thus, we do not specify partial -- its default is false) │ │ │ │ + │ │ │ │ + if (forceReTile || │ │ │ │ + (!dragging && !tilesBounds.containsBounds(bounds))) { │ │ │ │ + │ │ │ │ + // In single tile mode with no transition effect, we insert │ │ │ │ + // a non-scaled backbuffer when the layer is moved. But if │ │ │ │ + // a zoom occurs right after a move, i.e. before the new │ │ │ │ + // image is received, we need to remove the backbuffer, or │ │ │ │ + // an ill-positioned image will be visible during the zoom │ │ │ │ + // transition. │ │ │ │ + │ │ │ │ + if (zoomChanged && this.transitionEffect !== 'resize') { │ │ │ │ + this.removeBackBuffer(); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (!zoomChanged || this.transitionEffect === 'resize') { │ │ │ │ + this.applyBackBuffer(resolution); │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.initSingleTile(bounds); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + │ │ │ │ + // if the bounds have changed such that they are not even │ │ │ │ + // *partially* contained by our tiles (e.g. when user has │ │ │ │ + // programmatically panned to the other side of the earth on │ │ │ │ + // zoom level 18), then moveGriddedTiles could potentially have │ │ │ │ + // to run through thousands of cycles, so we want to reTile │ │ │ │ + // instead (thus, partial true). │ │ │ │ + forceReTile = forceReTile || │ │ │ │ + !tilesBounds.intersectsBounds(bounds, { │ │ │ │ + worldBounds: this.map.baseLayer.wrapDateLine && │ │ │ │ + this.map.getMaxExtent() │ │ │ │ + }); │ │ │ │ + │ │ │ │ + if (forceReTile) { │ │ │ │ + if (zoomChanged && (this.transitionEffect === 'resize' || │ │ │ │ + this.gridResolution === resolution)) { │ │ │ │ + this.applyBackBuffer(resolution); │ │ │ │ + } │ │ │ │ + this.initGriddedTiles(bounds); │ │ │ │ + } else { │ │ │ │ + this.moveGriddedTiles(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getTileData │ │ │ │ + * Given a map location, retrieve a tile and the pixel offset within that │ │ │ │ + * tile corresponding to the location. If there is not an existing │ │ │ │ + * tile in the grid that covers the given location, null will be │ │ │ │ + * returned. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * loc - {<OpenLayers.LonLat>} map location │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A string with the layer's url and parameters and also the │ │ │ │ - * passed-in bounds and appropriate tile size specified as │ │ │ │ - * parameters │ │ │ │ + * {Object} Object with the following properties: tile ({<OpenLayers.Tile>}), │ │ │ │ + * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel │ │ │ │ + * offset from top left). │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - var xyz = this.getXYZ(bounds); │ │ │ │ - var url = this.url; │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - var s = '' + xyz.x + xyz.y + xyz.z; │ │ │ │ - url = this.selectUrl(s, url); │ │ │ │ + getTileData: function(loc) { │ │ │ │ + var data = null, │ │ │ │ + x = loc.lon, │ │ │ │ + y = loc.lat, │ │ │ │ + numRows = this.grid.length; │ │ │ │ + │ │ │ │ + if (this.map && numRows) { │ │ │ │ + var res = this.map.getResolution(), │ │ │ │ + tileWidth = this.tileSize.w, │ │ │ │ + tileHeight = this.tileSize.h, │ │ │ │ + bounds = this.grid[0][0].bounds, │ │ │ │ + left = bounds.left, │ │ │ │ + top = bounds.top; │ │ │ │ + │ │ │ │ + if (x < left) { │ │ │ │ + // deal with multiple worlds │ │ │ │ + if (this.map.baseLayer.wrapDateLine) { │ │ │ │ + var worldWidth = this.map.getMaxExtent().getWidth(); │ │ │ │ + var worldsAway = Math.ceil((left - x) / worldWidth); │ │ │ │ + x += worldWidth * worldsAway; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + // tile distance to location (fractional number of tiles); │ │ │ │ + var dtx = (x - left) / (res * tileWidth); │ │ │ │ + var dty = (top - y) / (res * tileHeight); │ │ │ │ + // index of tile in grid │ │ │ │ + var col = Math.floor(dtx); │ │ │ │ + var row = Math.floor(dty); │ │ │ │ + if (row >= 0 && row < numRows) { │ │ │ │ + var tile = this.grid[row][col]; │ │ │ │ + if (tile) { │ │ │ │ + data = { │ │ │ │ + tile: tile, │ │ │ │ + // pixel index within tile │ │ │ │ + i: Math.floor((dtx - col) * tileWidth), │ │ │ │ + j: Math.floor((dty - row) * tileHeight) │ │ │ │ + }; │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + return data; │ │ │ │ + }, │ │ │ │ │ │ │ │ - return OpenLayers.String.format(url, xyz); │ │ │ │ + /** │ │ │ │ + * Method: destroyTile │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * tile - {<OpenLayers.Tile>} │ │ │ │ + */ │ │ │ │ + destroyTile: function(tile) { │ │ │ │ + this.removeTileMonitoringHooks(tile); │ │ │ │ + tile.destroy(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getXYZ │ │ │ │ - * Calculates x, y and z for the given bounds. │ │ │ │ + * Method: getServerResolution │ │ │ │ + * Return the closest server-supported resolution. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * resolution - {Number} The base resolution. If undefined the │ │ │ │ + * map resolution is used. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} - an object with x, y and z properties. │ │ │ │ + * {Number} The closest server resolution value. │ │ │ │ */ │ │ │ │ - getXYZ: function(bounds) { │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var x = Math.round((bounds.left - this.maxExtent.left) / │ │ │ │ - (res * this.tileSize.w)); │ │ │ │ - var y = Math.round((this.maxExtent.top - bounds.top) / │ │ │ │ - (res * this.tileSize.h)); │ │ │ │ - var z = this.getServerZoom(); │ │ │ │ - │ │ │ │ - if (this.wrapDateLine) { │ │ │ │ - var limit = Math.pow(2, z); │ │ │ │ - x = ((x % limit) + limit) % limit; │ │ │ │ + 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; │ │ │ │ + }, │ │ │ │ │ │ │ │ - return { │ │ │ │ - 'x': x, │ │ │ │ - 'y': y, │ │ │ │ - 'z': z │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Method: getServerZoom │ │ │ │ + * Return the zoom value corresponding to the best matching server │ │ │ │ + * resolution, taking into account <serverResolutions> and <zoomOffset>. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Number} The closest server supported zoom. This is not the map zoom │ │ │ │ + * level, but an index of the server's resolutions array. │ │ │ │ + */ │ │ │ │ + getServerZoom: function() { │ │ │ │ + var resolution = this.getServerResolution(); │ │ │ │ + return this.serverResolutions ? │ │ │ │ + OpenLayers.Util.indexOf(this.serverResolutions, resolution) : │ │ │ │ + this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0); │ │ │ │ }, │ │ │ │ │ │ │ │ - /* APIMethod: setMap │ │ │ │ - * When the layer is added to a map, then we can fetch our origin │ │ │ │ - * (if we don't have one.) │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: applyBackBuffer │ │ │ │ + * Create, insert, scale and position a back buffer for the layer. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * resolution - {Number} The resolution to transition to. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ - if (!this.tileOrigin) { │ │ │ │ - this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, │ │ │ │ - this.maxExtent.bottom); │ │ │ │ + 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; │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.XYZ" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/ArcGISCache.js │ │ │ │ - ====================================================================== */ │ │ │ │ + // 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; │ │ │ │ + } │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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 ratio = this.backBufferResolution / resolution; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/XYZ.js │ │ │ │ - */ │ │ │ │ + // 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'; │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.ArcGISCache │ │ │ │ - * Layer for accessing cached map tiles from an ArcGIS Server style mapcache. │ │ │ │ - * Tile must already be cached for this layer to access it. This does not require │ │ │ │ - * ArcGIS Server itself. │ │ │ │ - * │ │ │ │ - * A few attempts have been made at this kind of layer before. See │ │ │ │ - * http://trac.osgeo.org/openlayers/ticket/1967 │ │ │ │ - * and │ │ │ │ - * http://trac.osgeo.org/openlayers/browser/sandbox/tschaub/arcgiscache/lib/OpenLayers/Layer/ArcGISCache.js │ │ │ │ - * │ │ │ │ - * Typically the problem encountered is that the tiles seem to "jump around". │ │ │ │ - * This is due to the fact that the actual max extent for the tiles on AGS layers │ │ │ │ - * changes at each zoom level due to the way these caches are constructed. │ │ │ │ - * We have attempted to use the resolutions, tile size, and tile origin │ │ │ │ - * from the cache meta data to make the appropriate changes to the max extent │ │ │ │ - * of the tile to compensate for this behavior. This must be done as zoom levels change │ │ │ │ - * and before tiles are requested, which is why methods from base classes are overridden. │ │ │ │ - * │ │ │ │ - * For reference, you can access mapcache meta data in two ways. For accessing a │ │ │ │ - * mapcache through ArcGIS Server, you can simply go to the landing page for the │ │ │ │ - * layer. (ie. http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer) │ │ │ │ - * For accessing it directly through HTTP, there should always be a conf.xml file │ │ │ │ - * in the root directory. │ │ │ │ - * (ie. http://serverx.esri.com/arcgiscache/DG_County_roads_yesA_backgroundDark/Layers/conf.xml) │ │ │ │ - * │ │ │ │ - *Inherits from: │ │ │ │ - * - <OpenLayers.Layer.XYZ> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.ArcGISCache = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ + // 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'; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: url │ │ │ │ - * {String | Array} The base URL for the layer cache. You can also │ │ │ │ - * provide a list of URL strings for the layer if your cache is │ │ │ │ - * available from multiple origins. This must be set before the layer │ │ │ │ - * is drawn. │ │ │ │ + * Method: createBackBuffer │ │ │ │ + * Create a back buffer. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The DOM element for the back buffer, undefined if the │ │ │ │ + * grid isn't initialized yet. │ │ │ │ */ │ │ │ │ - url: null, │ │ │ │ + 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; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: tileOrigin │ │ │ │ - * {<OpenLayers.LonLat>} The location of the tile origin for the cache. │ │ │ │ - * An ArcGIS cache has it's origin at the upper-left (lowest x value │ │ │ │ - * and highest y value of the coordinate system). The units for the │ │ │ │ - * tile origin should be the same as the units for the cached data. │ │ │ │ + * Method: removeBackBuffer │ │ │ │ + * Remove back buffer from DOM. │ │ │ │ */ │ │ │ │ - tileOrigin: null, │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: tileSize │ │ │ │ - * {<OpenLayers.Size>} This size of each tile. Defaults to 256 by 256 pixels. │ │ │ │ + * Method: moveByPx │ │ │ │ + * Move the layer based on pixel vector. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * dx - {Number} │ │ │ │ + * dy - {Number} │ │ │ │ */ │ │ │ │ - tileSize: new OpenLayers.Size(256, 256), │ │ │ │ + moveByPx: function(dx, dy) { │ │ │ │ + if (!this.singleTile) { │ │ │ │ + this.moveGriddedTiles(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: useAGS │ │ │ │ - * {Boolean} Indicates if we are going to be accessing the ArcGIS Server (AGS) │ │ │ │ - * cache via an AGS MapServer or directly through HTTP. When accessing via │ │ │ │ - * AGS the path structure uses a standard z/y/x structure. But AGS actually │ │ │ │ - * stores the tile images on disk using a hex based folder structure that looks │ │ │ │ - * like "http://example.com/mylayer/L00/R00000000/C00000000.png". Learn more │ │ │ │ - * about this here: │ │ │ │ - * http://blogs.esri.com/Support/blogs/mappingcenter/archive/2010/08/20/Checking-Your-Local-Cache-Folders.aspx │ │ │ │ - * Defaults to true; │ │ │ │ + * APIMethod: setTileSize │ │ │ │ + * Check if we are in singleTile mode and if so, set the size as a ratio │ │ │ │ + * of the map size (as specified by the layer's 'ratio' property). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * size - {<OpenLayers.Size>} │ │ │ │ */ │ │ │ │ - useArcGISServer: true, │ │ │ │ + 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]); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: type │ │ │ │ - * {String} Image type for the layer. This becomes the filename extension │ │ │ │ - * in tile requests. Default is "png" (generating a url like │ │ │ │ - * "http://example.com/mylayer/L00/R00000000/C00000000.png"). │ │ │ │ + * APIMethod: getTilesBounds │ │ │ │ + * Return the bounds of the tile grid. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the │ │ │ │ + * currently loaded tiles (including those partially or not at all seen │ │ │ │ + * onscreen). │ │ │ │ */ │ │ │ │ - type: 'png', │ │ │ │ + getTilesBounds: function() { │ │ │ │ + var bounds = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: useScales │ │ │ │ - * {Boolean} Optional override to indicate that the layer should use 'scale' information │ │ │ │ - * returned from the server capabilities object instead of 'resolution' information. │ │ │ │ - * This can be important if your tile server uses an unusual DPI for the tiles. │ │ │ │ - */ │ │ │ │ - useScales: false, │ │ │ │ + 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(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: overrideDPI │ │ │ │ - * {Boolean} Optional override to change the OpenLayers.DOTS_PER_INCH setting based │ │ │ │ - * on the tile information in the server capabilities object. This can be useful │ │ │ │ - * if your server has a non-standard DPI setting on its tiles, and you're only using │ │ │ │ - * tiles with that DPI. This value is used while OpenLayers is calculating resolution │ │ │ │ - * using scales, and is not necessary if you have resolution information. (This is │ │ │ │ - * typically the case) Regardless, this setting can be useful, but is dangerous │ │ │ │ - * because it will impact other layers while calculating resolution. Only use this │ │ │ │ - * if you know what you are doing. (See OpenLayers.Util.getResolutionFromScale) │ │ │ │ - */ │ │ │ │ - overrideDPI: false, │ │ │ │ + bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, │ │ │ │ + bottomLeftTileBounds.bottom, │ │ │ │ + bottomLeftTileBounds.left + width, │ │ │ │ + bottomLeftTileBounds.bottom + height); │ │ │ │ + } │ │ │ │ + return bounds; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.ArcGISCache │ │ │ │ - * Creates a new instance of this class │ │ │ │ + * Method: initSingleTile │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * url - {String} │ │ │ │ - * options - {Object} extra layer options │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); │ │ │ │ + initSingleTile: function(bounds) { │ │ │ │ + this.events.triggerEvent("retile"); │ │ │ │ │ │ │ │ - if (this.resolutions) { │ │ │ │ - this.serverResolutions = this.resolutions; │ │ │ │ - this.maxExtent = this.getMaxExtentForResolution(this.resolutions[0]); │ │ │ │ - } │ │ │ │ + //determine new tile bounds │ │ │ │ + var center = bounds.getCenterLonLat(); │ │ │ │ + var tileWidth = bounds.getWidth() * this.ratio; │ │ │ │ + var tileHeight = bounds.getHeight() * this.ratio; │ │ │ │ │ │ │ │ - // this block steps through translating the values from the server layer JSON │ │ │ │ - // capabilities object into values that we can use. This is also a helpful │ │ │ │ - // reference when configuring this layer directly. │ │ │ │ - if (this.layerInfo) { │ │ │ │ - // alias the object │ │ │ │ - var info = this.layerInfo; │ │ │ │ + var tileBounds = │ │ │ │ + new OpenLayers.Bounds(center.lon - (tileWidth / 2), │ │ │ │ + center.lat - (tileHeight / 2), │ │ │ │ + center.lon + (tileWidth / 2), │ │ │ │ + center.lat + (tileHeight / 2)); │ │ │ │ │ │ │ │ - // build our extents │ │ │ │ - var startingTileExtent = new OpenLayers.Bounds( │ │ │ │ - info.fullExtent.xmin, │ │ │ │ - info.fullExtent.ymin, │ │ │ │ - info.fullExtent.xmax, │ │ │ │ - info.fullExtent.ymax │ │ │ │ - ); │ │ │ │ + var px = this.map.getLayerPxFromLonLat({ │ │ │ │ + lon: tileBounds.left, │ │ │ │ + lat: tileBounds.top │ │ │ │ + }); │ │ │ │ │ │ │ │ - // set our projection based on the given spatial reference. │ │ │ │ - // esri uses slightly different IDs, so this may not be comprehensive │ │ │ │ - this.projection = 'EPSG:' + info.spatialReference.wkid; │ │ │ │ - this.sphericalMercator = (info.spatialReference.wkid == 102100); │ │ │ │ + if (!this.grid.length) { │ │ │ │ + this.grid[0] = []; │ │ │ │ + } │ │ │ │ │ │ │ │ - // convert esri units into openlayers units (basic feet or meters only) │ │ │ │ - this.units = (info.units == "esriFeet") ? 'ft' : 'm'; │ │ │ │ + var tile = this.grid[0][0]; │ │ │ │ + if (!tile) { │ │ │ │ + tile = this.addTile(tileBounds, px); │ │ │ │ │ │ │ │ - // optional extended section based on whether or not the server returned │ │ │ │ - // specific tile information │ │ │ │ - if (!!info.tileInfo) { │ │ │ │ - // either set the tiles based on rows/columns, or specific width/height │ │ │ │ - this.tileSize = new OpenLayers.Size( │ │ │ │ - info.tileInfo.width || info.tileInfo.cols, │ │ │ │ - info.tileInfo.height || info.tileInfo.rows │ │ │ │ - ); │ │ │ │ + this.addTileMonitoringHooks(tile); │ │ │ │ + tile.draw(); │ │ │ │ + this.grid[0][0] = tile; │ │ │ │ + } else { │ │ │ │ + tile.moveTo(tileBounds, px); │ │ │ │ + } │ │ │ │ │ │ │ │ - // this must be set when manually configuring this layer │ │ │ │ - this.tileOrigin = new OpenLayers.LonLat( │ │ │ │ - info.tileInfo.origin.x, │ │ │ │ - info.tileInfo.origin.y │ │ │ │ - ); │ │ │ │ + //remove all but our single tile │ │ │ │ + this.removeExcessTiles(1, 1); │ │ │ │ │ │ │ │ - var upperLeft = new OpenLayers.Geometry.Point( │ │ │ │ - startingTileExtent.left, │ │ │ │ - startingTileExtent.top │ │ │ │ - ); │ │ │ │ + // store the resolution of the grid │ │ │ │ + this.gridResolution = this.getServerResolution(); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var bottomRight = new OpenLayers.Geometry.Point( │ │ │ │ - startingTileExtent.right, │ │ │ │ - startingTileExtent.bottom │ │ │ │ - ); │ │ │ │ + /** │ │ │ │ + * Method: calculateGridLayout │ │ │ │ + * Generate parameters for the grid layout. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bound>|Object} OpenLayers.Bounds or an │ │ │ │ + * object with a 'left' and 'top' properties. │ │ │ │ + * origin - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an │ │ │ │ + * object with a 'lon' and 'lat' properties. │ │ │ │ + * resolution - {Number} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} Object containing properties tilelon, tilelat, startcol, │ │ │ │ + * startrow │ │ │ │ + */ │ │ │ │ + calculateGridLayout: function(bounds, origin, resolution) { │ │ │ │ + var tilelon = resolution * this.tileSize.w; │ │ │ │ + var tilelat = resolution * this.tileSize.h; │ │ │ │ │ │ │ │ - if (this.useScales) { │ │ │ │ - this.scales = []; │ │ │ │ - } else { │ │ │ │ - this.resolutions = []; │ │ │ │ - } │ │ │ │ + var offsetlon = bounds.left - origin.lon; │ │ │ │ + var tilecol = Math.floor(offsetlon / tilelon) - this.buffer; │ │ │ │ │ │ │ │ - this.lods = []; │ │ │ │ - for (var key in info.tileInfo.lods) { │ │ │ │ - if (info.tileInfo.lods.hasOwnProperty(key)) { │ │ │ │ - var lod = info.tileInfo.lods[key]; │ │ │ │ - if (this.useScales) { │ │ │ │ - this.scales.push(lod.scale); │ │ │ │ - } else { │ │ │ │ - this.resolutions.push(lod.resolution); │ │ │ │ - } │ │ │ │ + var rowSign = this.rowSign; │ │ │ │ │ │ │ │ - var start = this.getContainingTileCoords(upperLeft, lod.resolution); │ │ │ │ - lod.startTileCol = start.x; │ │ │ │ - lod.startTileRow = start.y; │ │ │ │ + var offsetlat = rowSign * (origin.lat - bounds.top + tilelat); │ │ │ │ + var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat / tilelat) - this.buffer * rowSign; │ │ │ │ │ │ │ │ - var end = this.getContainingTileCoords(bottomRight, lod.resolution); │ │ │ │ - lod.endTileCol = end.x; │ │ │ │ - lod.endTileRow = end.y; │ │ │ │ - this.lods.push(lod); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + return { │ │ │ │ + tilelon: tilelon, │ │ │ │ + tilelat: tilelat, │ │ │ │ + startcol: tilecol, │ │ │ │ + startrow: tilerow │ │ │ │ + }; │ │ │ │ │ │ │ │ - this.maxExtent = this.calculateMaxExtentWithLOD(this.lods[0]); │ │ │ │ - this.serverResolutions = this.resolutions; │ │ │ │ - if (this.overrideDPI && info.tileInfo.dpi) { │ │ │ │ - // see comment above for 'overrideDPI' │ │ │ │ - OpenLayers.DOTS_PER_INCH = info.tileInfo.dpi; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getTileOrigin │ │ │ │ + * Determine the origin for aligning the grid of tiles. If a <tileOrigin> │ │ │ │ + * property is supplied, that will be returned. Otherwise, the origin │ │ │ │ + * will be derived from the layer's <maxExtent> property. In this case, │ │ │ │ + * the tile origin will be the corner of the <maxExtent> given by the │ │ │ │ + * <tileOriginCorner> property. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.LonLat>} The tile origin. │ │ │ │ + */ │ │ │ │ + getTileOrigin: function() { │ │ │ │ + var origin = this.tileOrigin; │ │ │ │ + if (!origin) { │ │ │ │ + var extent = this.getMaxExtent(); │ │ │ │ + var edges = ({ │ │ │ │ + "tl": ["left", "top"], │ │ │ │ + "tr": ["right", "top"], │ │ │ │ + "bl": ["left", "bottom"], │ │ │ │ + "br": ["right", "bottom"] │ │ │ │ + })[this.tileOriginCorner]; │ │ │ │ + origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]); │ │ │ │ } │ │ │ │ + return origin; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getContainingTileCoords │ │ │ │ - * Calculates the x/y pixel corresponding to the position of the tile │ │ │ │ - * that contains the given point and for the for the given resolution. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: getTileBoundsForGridIndex │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} │ │ │ │ - * res - {Float} The resolution for which to compute the extent. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position │ │ │ │ - * of the upper left tile for the given resolution. │ │ │ │ + * row - {Number} The row of the grid │ │ │ │ + * col - {Number} The column of the grid │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} The bounds for the tile at (row, col) │ │ │ │ */ │ │ │ │ - getContainingTileCoords: function(point, res) { │ │ │ │ - return new OpenLayers.Pixel( │ │ │ │ - Math.max(Math.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)), 0), │ │ │ │ - Math.max(Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res)), 0) │ │ │ │ + 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: calculateMaxExtentWithLOD │ │ │ │ - * Given a Level of Detail object from the server, this function │ │ │ │ - * calculates the actual max extent │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * lod - {Object} a Level of Detail Object from the server capabilities object │ │ │ │ - representing a particular zoom level │ │ │ │ + /** │ │ │ │ + * Method: initGriddedTiles │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - calculateMaxExtentWithLOD: function(lod) { │ │ │ │ - // the max extent we're provided with just overlaps some tiles │ │ │ │ - // our real extent is the bounds of all the tiles we touch │ │ │ │ + initGriddedTiles: function(bounds) { │ │ │ │ + this.events.triggerEvent("retile"); │ │ │ │ │ │ │ │ - var numTileCols = (lod.endTileCol - lod.startTileCol) + 1; │ │ │ │ - var numTileRows = (lod.endTileRow - lod.startTileRow) + 1; │ │ │ │ + // 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 minX = this.tileOrigin.lon + (lod.startTileCol * this.tileSize.w * lod.resolution); │ │ │ │ - var maxX = minX + (numTileCols * this.tileSize.w * lod.resolution); │ │ │ │ + var viewSize = this.map.getSize(); │ │ │ │ │ │ │ │ - var maxY = this.tileOrigin.lat - (lod.startTileRow * this.tileSize.h * lod.resolution); │ │ │ │ - var minY = maxY - (numTileRows * this.tileSize.h * lod.resolution); │ │ │ │ - return new OpenLayers.Bounds(minX, minY, maxX, maxY); │ │ │ │ - }, │ │ │ │ + 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 │ │ │ │ + }; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: calculateMaxExtentWithExtent │ │ │ │ - * Given a 'suggested' max extent from the server, this function uses │ │ │ │ - * information about the actual tile sizes to determine the actual │ │ │ │ - * extent of the layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * extent - {<OpenLayers.Bounds>} The 'suggested' extent for the layer │ │ │ │ - * res - {Float} The resolution for which to compute the extent. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level │ │ │ │ - */ │ │ │ │ - calculateMaxExtentWithExtent: function(extent, res) { │ │ │ │ - var upperLeft = new OpenLayers.Geometry.Point(extent.left, extent.top); │ │ │ │ - var bottomRight = new OpenLayers.Geometry.Point(extent.right, extent.bottom); │ │ │ │ - var start = this.getContainingTileCoords(upperLeft, res); │ │ │ │ - var end = this.getContainingTileCoords(bottomRight, res); │ │ │ │ - var lod = { │ │ │ │ - resolution: res, │ │ │ │ - startTileCol: start.x, │ │ │ │ - startTileRow: start.y, │ │ │ │ - endTileCol: end.x, │ │ │ │ - endTileRow: end.y │ │ │ │ - }; │ │ │ │ - return this.calculateMaxExtentWithLOD(lod); │ │ │ │ - }, │ │ │ │ + var minRows = Math.ceil(viewSize.h / tileSize.h) + │ │ │ │ + 2 * this.buffer + 1; │ │ │ │ + var minCols = Math.ceil(viewSize.w / tileSize.w) + │ │ │ │ + 2 * this.buffer + 1; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getUpperLeftTileCoord │ │ │ │ - * Calculates the x/y pixel corresponding to the position │ │ │ │ - * of the upper left tile for the given resolution. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * res - {Float} The resolution for which to compute the extent. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position │ │ │ │ - * of the upper left tile for the given resolution. │ │ │ │ - */ │ │ │ │ - getUpperLeftTileCoord: function(res) { │ │ │ │ - var upperLeft = new OpenLayers.Geometry.Point( │ │ │ │ - this.maxExtent.left, │ │ │ │ - this.maxExtent.top); │ │ │ │ - return this.getContainingTileCoords(upperLeft, res); │ │ │ │ - }, │ │ │ │ + var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution); │ │ │ │ + this.gridLayout = tileLayout; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getLowerRightTileCoord │ │ │ │ - * Calculates the x/y pixel corresponding to the position │ │ │ │ - * of the lower right tile for the given resolution. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * res - {Float} The resolution for which to compute the extent. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position │ │ │ │ - * of the lower right tile for the given resolution. │ │ │ │ - */ │ │ │ │ - getLowerRightTileCoord: function(res) { │ │ │ │ - var bottomRight = new OpenLayers.Geometry.Point( │ │ │ │ - this.maxExtent.right, │ │ │ │ - this.maxExtent.bottom); │ │ │ │ - return this.getContainingTileCoords(bottomRight, res); │ │ │ │ - }, │ │ │ │ + var tilelon = tileLayout.tilelon; │ │ │ │ + var tilelat = tileLayout.tilelat; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getMaxExtentForResolution │ │ │ │ - * Since the max extent of a set of tiles can change from zoom level │ │ │ │ - * to zoom level, we need to be able to calculate that max extent │ │ │ │ - * for a given resolution. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * res - {Float} The resolution for which to compute the extent. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} The extent for this resolution │ │ │ │ - */ │ │ │ │ - getMaxExtentForResolution: function(res) { │ │ │ │ - var start = this.getUpperLeftTileCoord(res); │ │ │ │ - var end = this.getLowerRightTileCoord(res); │ │ │ │ + var layerContainerDivLeft = this.map.layerContainerOriginPx.x; │ │ │ │ + var layerContainerDivTop = this.map.layerContainerOriginPx.y; │ │ │ │ │ │ │ │ - var numTileCols = (end.x - start.x) + 1; │ │ │ │ - var numTileRows = (end.y - start.y) + 1; │ │ │ │ + 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 minX = this.tileOrigin.lon + (start.x * this.tileSize.w * res); │ │ │ │ - var maxX = minX + (numTileCols * this.tileSize.w * res); │ │ │ │ + var tileData = [], │ │ │ │ + center = this.map.getCenter(); │ │ │ │ │ │ │ │ - var maxY = this.tileOrigin.lat - (start.y * this.tileSize.h * res); │ │ │ │ - var minY = maxY - (numTileRows * this.tileSize.h * res); │ │ │ │ - return new OpenLayers.Bounds(minX, minY, maxX, maxY); │ │ │ │ - }, │ │ │ │ + var rowidx = 0; │ │ │ │ + do { │ │ │ │ + var row = this.grid[rowidx]; │ │ │ │ + if (!row) { │ │ │ │ + row = []; │ │ │ │ + this.grid.push(row); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Returns an exact clone of this OpenLayers.Layer.ArcGISCache │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * [obj] - {Object} optional object to assign the cloned instance to. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.ArcGISCache>} clone of this instance │ │ │ │ - */ │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.ArcGISCache(this.name, this.url, this.options); │ │ │ │ - } │ │ │ │ - return OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ - }, │ │ │ │ + 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) │ │ │ │ + }); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: initGriddedTiles │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - */ │ │ │ │ - initGriddedTiles: function(bounds) { │ │ │ │ - delete this._tileOrigin; │ │ │ │ - OpenLayers.Layer.XYZ.prototype.initGriddedTiles.apply(this, arguments); │ │ │ │ + colidx += 1; │ │ │ │ + } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) || │ │ │ │ + colidx < minCols); │ │ │ │ + │ │ │ │ + rowidx += 1; │ │ │ │ + } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) || │ │ │ │ + rowidx < minRows); │ │ │ │ + │ │ │ │ + //shave off exceess rows and colums │ │ │ │ + this.removeExcessTiles(rowidx, colidx); │ │ │ │ + │ │ │ │ + var resolution = this.getServerResolution(); │ │ │ │ + // store the resolution of the grid │ │ │ │ + this.gridResolution = resolution; │ │ │ │ + │ │ │ │ + //now actually draw the tiles │ │ │ │ + tileData.sort(function(a, b) { │ │ │ │ + return a.distance - b.distance; │ │ │ │ + }); │ │ │ │ + for (var i = 0, ii = tileData.length; i < ii; ++i) { │ │ │ │ + tileData[i].tile.draw(); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ * Method: getMaxExtent │ │ │ │ - * Get this layer's maximum extent. │ │ │ │ + * Get this layer's maximum extent. (Implemented as a getter for │ │ │ │ + * potential specific implementations in sub-classes.) │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ * {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ getMaxExtent: function() { │ │ │ │ - var resolution = this.map.getResolution(); │ │ │ │ - return this.maxExtent = this.getMaxExtentForResolution(resolution); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getTileOrigin │ │ │ │ - * Determine the origin for aligning the grid of tiles. │ │ │ │ - * The origin will be derived from the layer's <maxExtent> property. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} The tile origin. │ │ │ │ - */ │ │ │ │ - getTileOrigin: function() { │ │ │ │ - if (!this._tileOrigin) { │ │ │ │ - var extent = this.getMaxExtent(); │ │ │ │ - this._tileOrigin = new OpenLayers.LonLat(extent.left, extent.bottom); │ │ │ │ - } │ │ │ │ - return this._tileOrigin; │ │ │ │ + return this.maxExtent; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ - * Determine the URL for a tile given the tile bounds. This is should support │ │ │ │ - * urls that access tiles through an ArcGIS Server MapServer or directly through │ │ │ │ - * the hex folder structure using HTTP. Just be sure to set the useArcGISServer │ │ │ │ - * property appropriately! This is basically the same as │ │ │ │ - * 'OpenLayers.Layer.TMS.getURL', but with the addition of hex addressing, │ │ │ │ - * and tile rounding. │ │ │ │ + * APIMethod: addTile │ │ │ │ + * Create a tile, initialize it, and add it to the layer div. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ + * Parameters │ │ │ │ * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * position - {<OpenLayers.Pixel>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} The URL for a tile based on given bounds. │ │ │ │ + * {<OpenLayers.Tile>} The added OpenLayers.Tile │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - var res = this.getResolution(); │ │ │ │ + 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; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // tile center │ │ │ │ - var originTileX = (this.tileOrigin.lon + (res * this.tileSize.w / 2)); │ │ │ │ - var originTileY = (this.tileOrigin.lat - (res * this.tileSize.h / 2)); │ │ │ │ + /** │ │ │ │ + * Method: addTileMonitoringHooks │ │ │ │ + * This function takes a tile as input and adds the appropriate hooks to │ │ │ │ + * the tile so that the layer can keep track of the loading tiles. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * tile - {<OpenLayers.Tile>} │ │ │ │ + */ │ │ │ │ + addTileMonitoringHooks: function(tile) { │ │ │ │ │ │ │ │ - var center = bounds.getCenterLonLat(); │ │ │ │ - var point = { │ │ │ │ - x: center.lon, │ │ │ │ - y: center.lat │ │ │ │ - }; │ │ │ │ - var x = (Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w)))); │ │ │ │ - var y = (Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h)))); │ │ │ │ - var z = this.map.getZoom(); │ │ │ │ + var replacingCls = 'olTileReplacing'; │ │ │ │ │ │ │ │ - // this prevents us from getting pink tiles (non-existant tiles) │ │ │ │ - if (this.lods) { │ │ │ │ - var lod = this.lods[this.map.getZoom()]; │ │ │ │ - if ((x < lod.startTileCol || x > lod.endTileCol) || │ │ │ │ - (y < lod.startTileRow || y > lod.endTileRow)) { │ │ │ │ - return null; │ │ │ │ + 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"); │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - var start = this.getUpperLeftTileCoord(res); │ │ │ │ - var end = this.getLowerRightTileCoord(res); │ │ │ │ - if ((x < start.x || x >= end.x) || │ │ │ │ - (y < start.y || y >= end.y)) { │ │ │ │ - return null; │ │ │ │ + this.events.triggerEvent("tileloadstart", { │ │ │ │ + tile: tile │ │ │ │ + }); │ │ │ │ + this.numLoadingTiles++; │ │ │ │ + if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ + OpenLayers.Element.addClass(tile.getTile(), replacingCls); │ │ │ │ } │ │ │ │ - } │ │ │ │ + }; │ │ │ │ │ │ │ │ - // Construct the url string │ │ │ │ - var url = this.url; │ │ │ │ - var s = '' + x + y + z; │ │ │ │ + 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"); │ │ │ │ + } │ │ │ │ + }; │ │ │ │ │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(s, url); │ │ │ │ + tile.onLoadError = function() { │ │ │ │ + this.events.triggerEvent("tileerror", { │ │ │ │ + tile: tile │ │ │ │ + }); │ │ │ │ + }; │ │ │ │ + │ │ │ │ + tile.events.on({ │ │ │ │ + "loadstart": tile.onLoadStart, │ │ │ │ + "loadend": tile.onLoadEnd, │ │ │ │ + "unload": tile.onLoadEnd, │ │ │ │ + "loaderror": tile.onLoadError, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: removeTileMonitoringHooks │ │ │ │ + * This function takes a tile as input and removes the tile hooks │ │ │ │ + * that were added in addTileMonitoringHooks() │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * tile - {<OpenLayers.Tile>} │ │ │ │ + */ │ │ │ │ + removeTileMonitoringHooks: function(tile) { │ │ │ │ + tile.unload(); │ │ │ │ + tile.events.un({ │ │ │ │ + "loadstart": tile.onLoadStart, │ │ │ │ + "loadend": tile.onLoadEnd, │ │ │ │ + "unload": tile.onLoadEnd, │ │ │ │ + "loaderror": tile.onLoadError, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveGriddedTiles │ │ │ │ + */ │ │ │ │ + moveGriddedTiles: function() { │ │ │ │ + var buffer = this.buffer + 1; │ │ │ │ + while (true) { │ │ │ │ + var tlTile = this.grid[0][0]; │ │ │ │ + var tlViewPort = { │ │ │ │ + x: tlTile.position.x + │ │ │ │ + this.map.layerContainerOriginPx.x, │ │ │ │ + y: tlTile.position.y + │ │ │ │ + this.map.layerContainerOriginPx.y │ │ │ │ + }; │ │ │ │ + var ratio = this.getServerResolution() / this.map.getResolution(); │ │ │ │ + var tileSize = { │ │ │ │ + w: Math.round(this.tileSize.w * ratio), │ │ │ │ + h: Math.round(this.tileSize.h * ratio) │ │ │ │ + }; │ │ │ │ + if (tlViewPort.x > -tileSize.w * (buffer - 1)) { │ │ │ │ + this.shiftColumn(true, tileSize); │ │ │ │ + } else if (tlViewPort.x < -tileSize.w * buffer) { │ │ │ │ + this.shiftColumn(false, tileSize); │ │ │ │ + } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) { │ │ │ │ + this.shiftRow(true, tileSize); │ │ │ │ + } else if (tlViewPort.y < -tileSize.h * buffer) { │ │ │ │ + this.shiftRow(false, tileSize); │ │ │ │ + } else { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Accessing tiles through ArcGIS Server uses a different path │ │ │ │ - // structure than direct access via the folder structure. │ │ │ │ - if (this.useArcGISServer) { │ │ │ │ - // AGS MapServers have pretty url access to tiles │ │ │ │ - url = url + '/tile/${z}/${y}/${x}'; │ │ │ │ - } else { │ │ │ │ - // The tile images are stored using hex values on disk. │ │ │ │ - x = 'C' + OpenLayers.Number.zeroPad(x, 8, 16); │ │ │ │ - y = 'R' + OpenLayers.Number.zeroPad(y, 8, 16); │ │ │ │ - z = 'L' + OpenLayers.Number.zeroPad(z, 2, 10); │ │ │ │ - url = url + '/${z}/${y}/${x}.' + this.type; │ │ │ │ + /** │ │ │ │ + * 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); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Write the values into our formatted url │ │ │ │ - url = OpenLayers.String.format(url, { │ │ │ │ - 'x': x, │ │ │ │ - 'y': y, │ │ │ │ - 'z': z │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * Method: shiftColumn │ │ │ │ + * Shift grid work in the other dimension │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * prepend - {Boolean} if true, prepend to beginning. │ │ │ │ + * if false, then append to end │ │ │ │ + * tileSize - {Object} rendered tile size; object with w and h properties │ │ │ │ + */ │ │ │ │ + shiftColumn: function(prepend, tileSize) { │ │ │ │ + var grid = this.grid; │ │ │ │ + var colIndex = prepend ? 0 : (grid[0].length - 1); │ │ │ │ + var sign = prepend ? -1 : 1; │ │ │ │ + var tileLayout = this.gridLayout; │ │ │ │ + tileLayout.startcol += sign; │ │ │ │ │ │ │ │ - return OpenLayers.Util.urlAppend( │ │ │ │ - url, OpenLayers.Util.getParameterString(this.params) │ │ │ │ - ); │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: 'OpenLayers.Layer.ArcGISCache' │ │ │ │ + /** │ │ │ │ + * Method: removeExcessTiles │ │ │ │ + * When the size of the map or the buffer changes, we may need to │ │ │ │ + * remove some excess rows and columns. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * rows - {Integer} Maximum number of rows we want our grid to have. │ │ │ │ + * columns - {Integer} Maximum number of columns we want our grid to have. │ │ │ │ + */ │ │ │ │ + removeExcessTiles: function(rows, columns) { │ │ │ │ + var i, l; │ │ │ │ + │ │ │ │ + // remove extra rows │ │ │ │ + while (this.grid.length > rows) { │ │ │ │ + var row = this.grid.pop(); │ │ │ │ + for (i = 0, l = row.length; i < l; i++) { │ │ │ │ + var tile = row[i]; │ │ │ │ + this.destroyTile(tile); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // remove extra columns │ │ │ │ + for (i = 0, l = this.grid.length; i < l; i++) { │ │ │ │ + while (this.grid[i].length > columns) { │ │ │ │ + var row = this.grid[i]; │ │ │ │ + var tile = row.pop(); │ │ │ │ + this.destroyTile(tile); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: onMapResize │ │ │ │ + * For singleTile layers, this will set a new tile size according to the │ │ │ │ + * dimensions of the map pane. │ │ │ │ + */ │ │ │ │ + onMapResize: function() { │ │ │ │ + if (this.singleTile) { │ │ │ │ + this.clearGrid(); │ │ │ │ + this.setTileSize(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getTileBounds │ │ │ │ + * Returns The tile bounds for a layer given a pixel location. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location. │ │ │ │ + */ │ │ │ │ + getTileBounds: function(viewPortPx) { │ │ │ │ + var maxExtent = this.maxExtent; │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var tileMapWidth = resolution * this.tileSize.w; │ │ │ │ + var tileMapHeight = resolution * this.tileSize.h; │ │ │ │ + var mapPoint = this.getLonLatFromViewPortPx(viewPortPx); │ │ │ │ + var tileLeft = maxExtent.left + (tileMapWidth * │ │ │ │ + Math.floor((mapPoint.lon - │ │ │ │ + maxExtent.left) / │ │ │ │ + tileMapWidth)); │ │ │ │ + var tileBottom = maxExtent.bottom + (tileMapHeight * │ │ │ │ + Math.floor((mapPoint.lat - │ │ │ │ + maxExtent.bottom) / │ │ │ │ + tileMapHeight)); │ │ │ │ + return new OpenLayers.Bounds(tileLeft, tileBottom, │ │ │ │ + tileLeft + tileMapWidth, │ │ │ │ + tileBottom + tileMapHeight); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Grid" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/Bing.js │ │ │ │ + OpenLayers/TileManager.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Layer/XYZ.js │ │ │ │ + * @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.Layer.Bing │ │ │ │ - * Bing layer using direct tile access as provided by Bing Maps REST Services. │ │ │ │ - * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more │ │ │ │ - * information. Note: Terms of Service compliant use requires the map to be │ │ │ │ - * configured with an <OpenLayers.Control.Attribution> control and the │ │ │ │ - * attribution placed on or near the map. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.XYZ> │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.TileManager │ │ │ │ + * Provides queueing of image requests and caching of image elements. │ │ │ │ + * │ │ │ │ + * Queueing avoids unnecessary image requests while changing zoom levels │ │ │ │ + * quickly, and helps improve dragging performance on mobile devices that show │ │ │ │ + * a lag in dragging when loading of new images starts. <zoomDelay> and │ │ │ │ + * <moveDelay> are the configuration options to control this behavior. │ │ │ │ + * │ │ │ │ + * Caching avoids setting the src on image elements for images that have already │ │ │ │ + * been used. Several maps can share a TileManager instance, in which case each │ │ │ │ + * map gets its own tile queue, but all maps share the same tile cache. │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ +OpenLayers.TileManager = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: key │ │ │ │ - * {String} API key for Bing maps, get your own key │ │ │ │ - * at http://bingmapsportal.com/ . │ │ │ │ + * APIProperty: cacheSize │ │ │ │ + * {Number} Number of image elements to keep referenced in this instance's │ │ │ │ + * cache for fast reuse. Default is 256. │ │ │ │ */ │ │ │ │ - key: null, │ │ │ │ + cacheSize: 256, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: serverResolutions │ │ │ │ - * {Array} the resolutions provided by the Bing servers. │ │ │ │ + * APIProperty: tilesPerFrame │ │ │ │ + * {Number} Number of queued tiles to load per frame (see <frameDelay>). │ │ │ │ + * Default is 2. │ │ │ │ */ │ │ │ │ - serverResolutions: [ │ │ │ │ - 156543.03390625, 78271.516953125, 39135.7584765625, │ │ │ │ - 19567.87923828125, 9783.939619140625, 4891.9698095703125, │ │ │ │ - 2445.9849047851562, 1222.9924523925781, 611.4962261962891, │ │ │ │ - 305.74811309814453, 152.87405654907226, 76.43702827453613, │ │ │ │ - 38.218514137268066, 19.109257068634033, 9.554628534317017, │ │ │ │ - 4.777314267158508, 2.388657133579254, 1.194328566789627, │ │ │ │ - 0.5971642833948135, 0.29858214169740677, 0.14929107084870338, │ │ │ │ - 0.07464553542435169 │ │ │ │ - ], │ │ │ │ + tilesPerFrame: 2, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: attributionTemplate │ │ │ │ - * {String} │ │ │ │ + * APIProperty: frameDelay │ │ │ │ + * {Number} Delay between tile loading frames (see <tilesPerFrame>) in │ │ │ │ + * milliseconds. Default is 16. │ │ │ │ */ │ │ │ │ - attributionTemplate: '<span class="olBingAttribution ${type}">' + │ │ │ │ - '<div><a target="_blank" href="http://www.bing.com/maps/">' + │ │ │ │ - '<img src="${logo}" /></a></div>${copyrights}' + │ │ │ │ - '<a style="white-space: nowrap" target="_blank" ' + │ │ │ │ - 'href="http://www.microsoft.com/maps/product/terms.html">' + │ │ │ │ - 'Terms of Use</a></span>', │ │ │ │ + frameDelay: 16, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: metadata │ │ │ │ - * {Object} Metadata for this layer, as returned by the callback script │ │ │ │ + * APIProperty: moveDelay │ │ │ │ + * {Number} Delay in milliseconds after a map's move event before loading │ │ │ │ + * tiles. Default is 100. │ │ │ │ */ │ │ │ │ - metadata: null, │ │ │ │ + moveDelay: 100, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: protocolRegex │ │ │ │ - * {RegExp} Regular expression to match and replace http: in bing urls │ │ │ │ + * APIProperty: zoomDelay │ │ │ │ + * {Number} Delay in milliseconds after a map's zoomend event before loading │ │ │ │ + * tiles. Default is 200. │ │ │ │ */ │ │ │ │ - protocolRegex: /^http:/i, │ │ │ │ + zoomDelay: 200, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: type │ │ │ │ - * {String} The layer identifier. Any non-birdseye imageryType │ │ │ │ - * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be │ │ │ │ - * used. Default is "Road". │ │ │ │ + * Property: maps │ │ │ │ + * {Array(<OpenLayers.Map>)} The maps to manage tiles on. │ │ │ │ */ │ │ │ │ - type: "Road", │ │ │ │ + maps: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: culture │ │ │ │ - * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx │ │ │ │ - * for the definition and the possible values. Default is "en-US". │ │ │ │ + * Property: tileQueueId │ │ │ │ + * {Object} The ids of the <drawTilesFromQueue> loop, keyed by map id. │ │ │ │ */ │ │ │ │ - culture: "en-US", │ │ │ │ + tileQueueId: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: metadataParams │ │ │ │ - * {Object} Optional url parameters for the Get Imagery Metadata request │ │ │ │ - * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx │ │ │ │ + * Property: tileQueue │ │ │ │ + * {Object(Array(<OpenLayers.Tile>))} Tiles queued for drawing, keyed by │ │ │ │ + * map id. │ │ │ │ */ │ │ │ │ - metadataParams: null, │ │ │ │ + tileQueue: null, │ │ │ │ │ │ │ │ - /** APIProperty: tileOptions │ │ │ │ - * {Object} optional configuration options for <OpenLayers.Tile> instances │ │ │ │ - * created by this Layer. Default is │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * {crossOriginKeyword: 'anonymous'} │ │ │ │ - * (end) │ │ │ │ + /** │ │ │ │ + * Property: tileCache │ │ │ │ + * {Object} Cached image elements, keyed by URL. │ │ │ │ */ │ │ │ │ - tileOptions: null, │ │ │ │ + tileCache: null, │ │ │ │ │ │ │ │ - /** APIProperty: protocol │ │ │ │ - * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo │ │ │ │ - * Can be 'http:' 'https:' or '' │ │ │ │ - * │ │ │ │ - * Warning: tiles may not be available under both HTTP and HTTPS protocols. │ │ │ │ - * Microsoft approved use of both HTTP and HTTPS urls for tiles. However │ │ │ │ - * this is undocumented and the Imagery Metadata API always returns HTTP │ │ │ │ - * urls. │ │ │ │ - * │ │ │ │ - * Default is '', unless when executed from a file:/// uri, in which case │ │ │ │ - * it is 'http:'. │ │ │ │ + /** │ │ │ │ + * Property: tileCacheIndex │ │ │ │ + * {Array(String)} URLs of cached tiles. First entry is the least recently │ │ │ │ + * used. │ │ │ │ */ │ │ │ │ - protocol: ~window.location.href.indexOf('http') ? '' : 'http:', │ │ │ │ + tileCacheIndex: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.TileManager │ │ │ │ + * Constructor for a new <OpenLayers.TileManager> instance. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Configuration for this instance. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.maps = []; │ │ │ │ + this.tileQueueId = {}; │ │ │ │ + this.tileQueue = {}; │ │ │ │ + this.tileCache = {}; │ │ │ │ + this.tileCacheIndex = []; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.Bing │ │ │ │ - * Create a new Bing layer. │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * var road = new OpenLayers.Layer.Bing({ │ │ │ │ - * name: "My Bing Aerial Layer", │ │ │ │ - * type: "Aerial", │ │ │ │ - * key: "my-api-key-here", │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ + * Method: addMap │ │ │ │ + * Binds this instance to a map │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Configuration properties for the layer. │ │ │ │ - * │ │ │ │ - * Required configuration properties: │ │ │ │ - * key - {String} Bing Maps API key for your application. Get one at │ │ │ │ - * http://bingmapsportal.com/. │ │ │ │ - * type - {String} The layer identifier. Any non-birdseye imageryType │ │ │ │ - * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be │ │ │ │ - * used. │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + addMap: function(map) { │ │ │ │ + if (this._destroyed || !OpenLayers.Layer.Grid) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + this.maps.push(map); │ │ │ │ + this.tileQueue[map.id] = []; │ │ │ │ + for (var i = 0, ii = map.layers.length; i < ii; ++i) { │ │ │ │ + this.addLayer({ │ │ │ │ + layer: map.layers[i] │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + map.events.on({ │ │ │ │ + move: this.move, │ │ │ │ + zoomend: this.zoomEnd, │ │ │ │ + changelayer: this.changeLayer, │ │ │ │ + addlayer: this.addLayer, │ │ │ │ + preremovelayer: this.removeLayer, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: removeMap │ │ │ │ + * Unbinds this instance from a map │ │ │ │ * │ │ │ │ - * Any other documented layer properties can be provided in the config object. │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults({ │ │ │ │ - sphericalMercator: true │ │ │ │ - }, options); │ │ │ │ - var name = options.name || "Bing " + (options.type || this.type); │ │ │ │ + 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); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var newArgs = [name, null, options]; │ │ │ │ - OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs); │ │ │ │ - this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ - crossOriginKeyword: 'anonymous' │ │ │ │ - }, this.options.tileOptions); │ │ │ │ - this.loadMetadata(); │ │ │ │ + /** │ │ │ │ + * Method: move │ │ │ │ + * Handles the map's move event │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} Listener argument │ │ │ │ + */ │ │ │ │ + move: function(evt) { │ │ │ │ + this.updateTimeout(evt.object, this.moveDelay, true); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: loadMetadata │ │ │ │ + * Method: zoomEnd │ │ │ │ + * Handles the map's zoomEnd event │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} Listener argument │ │ │ │ */ │ │ │ │ - loadMetadata: function() { │ │ │ │ - this._callbackId = "_callback_" + this.id.replace(/\./g, "_"); │ │ │ │ - // link the processMetadata method to the global scope and bind it │ │ │ │ - // to this instance │ │ │ │ - window[this._callbackId] = OpenLayers.Function.bind( │ │ │ │ - OpenLayers.Layer.Bing.processMetadata, this │ │ │ │ - ); │ │ │ │ - var params = OpenLayers.Util.applyDefaults({ │ │ │ │ - key: this.key, │ │ │ │ - jsonp: this._callbackId, │ │ │ │ - include: "ImageryProviders" │ │ │ │ - }, this.metadataParams); │ │ │ │ - var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" + │ │ │ │ - this.type + "?" + OpenLayers.Util.getParameterString(params); │ │ │ │ - var script = document.createElement("script"); │ │ │ │ - script.type = "text/javascript"; │ │ │ │ - script.src = url; │ │ │ │ - script.id = this._callbackId; │ │ │ │ - document.getElementsByTagName("head")[0].appendChild(script); │ │ │ │ + zoomEnd: function(evt) { │ │ │ │ + this.updateTimeout(evt.object, this.zoomDelay); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: initLayer │ │ │ │ + * Method: changeLayer │ │ │ │ + * Handles the map's changeLayer event │ │ │ │ * │ │ │ │ - * Sets layer properties according to the metadata provided by the API │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} Listener argument │ │ │ │ */ │ │ │ │ - initLayer: function() { │ │ │ │ - var res = this.metadata.resourceSets[0].resources[0]; │ │ │ │ - var url = res.imageUrl.replace("{quadkey}", "${quadkey}"); │ │ │ │ - url = url.replace("{culture}", this.culture); │ │ │ │ - url = url.replace(this.protocolRegex, this.protocol); │ │ │ │ - this.url = []; │ │ │ │ - for (var i = 0; i < res.imageUrlSubdomains.length; ++i) { │ │ │ │ - this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i])); │ │ │ │ - } │ │ │ │ - this.addOptions({ │ │ │ │ - maxResolution: Math.min( │ │ │ │ - this.serverResolutions[res.zoomMin], │ │ │ │ - this.maxResolution || Number.POSITIVE_INFINITY │ │ │ │ - ), │ │ │ │ - numZoomLevels: Math.min( │ │ │ │ - res.zoomMax + 1 - res.zoomMin, this.numZoomLevels │ │ │ │ - ) │ │ │ │ - }, true); │ │ │ │ - if (!this.isBaseLayer) { │ │ │ │ - this.redraw(); │ │ │ │ + changeLayer: function(evt) { │ │ │ │ + if (evt.property === 'visibility' || evt.property === 'params') { │ │ │ │ + this.updateTimeout(evt.object, 0); │ │ │ │ } │ │ │ │ - this.updateAttribution(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ + * Method: addLayer │ │ │ │ + * Handles the map's addlayer event │ │ │ │ * │ │ │ │ - * Paramters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} The listener argument │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - if (!this.url) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - var xyz = this.getXYZ(bounds), │ │ │ │ - x = xyz.x, │ │ │ │ - y = xyz.y, │ │ │ │ - z = xyz.z; │ │ │ │ - var quadDigits = []; │ │ │ │ - for (var i = z; i > 0; --i) { │ │ │ │ - var digit = '0'; │ │ │ │ - var mask = 1 << (i - 1); │ │ │ │ - if ((x & mask) != 0) { │ │ │ │ - digit++; │ │ │ │ - } │ │ │ │ - if ((y & mask) != 0) { │ │ │ │ - digit++; │ │ │ │ - digit++; │ │ │ │ + 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 │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - quadDigits.push(digit); │ │ │ │ } │ │ │ │ - var quadKey = quadDigits.join(""); │ │ │ │ - var url = this.selectUrl('' + x + y + z, this.url); │ │ │ │ - │ │ │ │ - return OpenLayers.String.format(url, { │ │ │ │ - 'quadkey': quadKey │ │ │ │ - }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: updateAttribution │ │ │ │ - * Updates the attribution according to the requirements outlined in │ │ │ │ - * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html │ │ │ │ + * Method: removeLayer │ │ │ │ + * Handles the map's preremovelayer event │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} The listener argument │ │ │ │ */ │ │ │ │ - updateAttribution: function() { │ │ │ │ - var metadata = this.metadata; │ │ │ │ - if (!metadata.resourceSets || !this.map || !this.map.center) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - var res = metadata.resourceSets[0].resources[0]; │ │ │ │ - var extent = this.map.getExtent().transform( │ │ │ │ - this.map.getProjectionObject(), │ │ │ │ - new OpenLayers.Projection("EPSG:4326") │ │ │ │ - ); │ │ │ │ - var providers = res.imageryProviders || [], │ │ │ │ - zoom = OpenLayers.Util.indexOf(this.serverResolutions, │ │ │ │ - this.getServerResolution()), │ │ │ │ - copyrights = "", │ │ │ │ - provider, i, ii, j, jj, bbox, coverage; │ │ │ │ - for (i = 0, ii = providers.length; i < ii; ++i) { │ │ │ │ - provider = providers[i]; │ │ │ │ - for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) { │ │ │ │ - coverage = provider.coverageAreas[j]; │ │ │ │ - // axis order provided is Y,X │ │ │ │ - bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true); │ │ │ │ - if (extent.intersectsBounds(bbox) && │ │ │ │ - zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) { │ │ │ │ - copyrights += provider.attribution + " "; │ │ │ │ + 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 │ │ │ │ + }); │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ - var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol); │ │ │ │ - this.attribution = OpenLayers.String.format(this.attributionTemplate, { │ │ │ │ - type: this.type.toLowerCase(), │ │ │ │ - logo: logo, │ │ │ │ - copyrights: copyrights │ │ │ │ - }); │ │ │ │ - this.map && this.map.events.triggerEvent("changelayer", { │ │ │ │ - layer: this, │ │ │ │ - property: "attribution" │ │ │ │ - }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setMap │ │ │ │ + * Method: updateTimeout │ │ │ │ + * Applies the <moveDelay> or <zoomDelay> to the <drawTilesFromQueue> loop, │ │ │ │ + * and schedules more queue processing after <frameDelay> if there are still │ │ │ │ + * tiles in the queue. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} The map to update the timeout for │ │ │ │ + * delay - {Number} The delay to apply │ │ │ │ + * nice - {Boolean} If true, the timeout function will only be created if │ │ │ │ + * the tilequeue is not empty. This is used by the move handler to │ │ │ │ + * avoid impacts on dragging performance. For other events, the tile │ │ │ │ + * queue may not be populated yet, so we need to set the timer │ │ │ │ + * regardless of the queue size. │ │ │ │ */ │ │ │ │ - setMap: function() { │ │ │ │ - OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments); │ │ │ │ - this.map.events.register("moveend", this, this.updateAttribution); │ │ │ │ + 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 │ │ │ │ + ); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * │ │ │ │ + * Method: addTile │ │ │ │ + * Listener for the layer's addtile event │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * obj - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing> │ │ │ │ + * evt - {Object} The listener argument │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Bing(this.options); │ │ │ │ + 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 │ │ │ │ + }); │ │ │ │ } │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ + * Method: unloadTile │ │ │ │ + * Listener for the tile's unload event │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} The listener argument │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.map && │ │ │ │ - this.map.events.unregister("moveend", this, this.updateAttribution); │ │ │ │ - OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments); │ │ │ │ + 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); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Bing" │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Function: OpenLayers.Layer.Bing.processMetadata │ │ │ │ - * This function will be bound to an instance, linked to the global scope with │ │ │ │ - * an id, and called by the JSONP script returned by the API. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * metadata - {Object} metadata as returned by the API │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Bing.processMetadata = function(metadata) { │ │ │ │ - this.metadata = metadata; │ │ │ │ - this.initLayer(); │ │ │ │ - var script = document.getElementById(this._callbackId); │ │ │ │ - script.parentNode.removeChild(script); │ │ │ │ - window[this._callbackId] = undefined; // cannot delete from window in IE │ │ │ │ - delete this._callbackId; │ │ │ │ -}; │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/SphericalMercator.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer.js │ │ │ │ - * @requires OpenLayers/Projection.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.SphericalMercator │ │ │ │ - * A mixin for layers that wraps up the pieces neccesary to have a coordinate │ │ │ │ - * conversion for working with commercial APIs which use a spherical │ │ │ │ - * mercator projection. Using this layer as a base layer, additional │ │ │ │ - * layers can be used as overlays if they are in the same projection. │ │ │ │ - * │ │ │ │ - * A layer is given properties of this object by setting the sphericalMercator │ │ │ │ - * property to true. │ │ │ │ - * │ │ │ │ - * More projection information: │ │ │ │ - * - http://spatialreference.org/ref/user/google-projection/ │ │ │ │ - * │ │ │ │ - * Proj4 Text: │ │ │ │ - * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 │ │ │ │ - * +k=1.0 +units=m +nadgrids=@null +no_defs │ │ │ │ - * │ │ │ │ - * WKT: │ │ │ │ - * 900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84", │ │ │ │ - * DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]], │ │ │ │ - * PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295], │ │ │ │ - * AXIS["Longitude", EAST], AXIS["Latitude", NORTH]], │ │ │ │ - * PROJECTION["Mercator_1SP_Google"], │ │ │ │ - * PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0], │ │ │ │ - * PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0], │ │ │ │ - * PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST], │ │ │ │ - * AXIS["y", NORTH], AUTHORITY["EPSG","900913"]] │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.SphericalMercator = { │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: getExtent │ │ │ │ - * Get the map's extent. │ │ │ │ + * Method: queueTileDraw │ │ │ │ + * Adds a tile to the queue that will draw it. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} The map extent. │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} Listener argument of the tile's beforedraw event │ │ │ │ */ │ │ │ │ - getExtent: function() { │ │ │ │ - var extent = null; │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - extent = this.map.calculateBounds(); │ │ │ │ - } else { │ │ │ │ - extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this); │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - return extent; │ │ │ │ + // queue only if image with same url not cached already │ │ │ │ + if (layer.url && (layer.async || !img)) { │ │ │ │ + // add to queue only if not in queue already │ │ │ │ + var tileQueue = this.tileQueue[layer.map.id]; │ │ │ │ + if (!~OpenLayers.Util.indexOf(tileQueue, tile)) { │ │ │ │ + tileQueue.push(tile); │ │ │ │ + } │ │ │ │ + queued = true; │ │ │ │ + } │ │ │ │ + return !queued; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getLonLatFromViewPortPx │ │ │ │ - * Get a map location from a pixel location │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * viewPortPx - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view │ │ │ │ - * port OpenLayers.Pixel, translated into lon/lat by map lib │ │ │ │ - * If the map lib is not loaded or not centered, returns null │ │ │ │ + * Method: drawTilesFromQueue │ │ │ │ + * Draws tiles from the tileQueue, and unqueues the tiles │ │ │ │ */ │ │ │ │ - getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ - return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments); │ │ │ │ + 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: getViewPortPxFromLonLat │ │ │ │ - * Get a pixel location from a map location │ │ │ │ + * Method: manageTileCache │ │ │ │ + * Adds, updates, removes and fetches cache entries. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * lonlat - {<OpenLayers.LonLat>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in │ │ │ │ - * OpenLayers.LonLat, translated into view port pixels by map lib │ │ │ │ - * If map lib is not loaded or not centered, returns null │ │ │ │ + * evt - {Object} Listener argument of the tile's beforeload event │ │ │ │ */ │ │ │ │ - getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ - return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments); │ │ │ │ + manageTileCache: function(evt) { │ │ │ │ + var tile = evt.object; │ │ │ │ + var img = this.tileCache[tile.url]; │ │ │ │ + if (img) { │ │ │ │ + // if image is on its layer's backbuffer, remove it from backbuffer │ │ │ │ + if (img.parentNode && │ │ │ │ + OpenLayers.Element.hasClass(img.parentNode, 'olBackBuffer')) { │ │ │ │ + img.parentNode.removeChild(img); │ │ │ │ + img.id = null; │ │ │ │ + } │ │ │ │ + // only use image from cache if it is not on a layer already │ │ │ │ + if (!img.parentNode) { │ │ │ │ + img.style.visibility = 'hidden'; │ │ │ │ + img.style.opacity = 0; │ │ │ │ + tile.setImage(img); │ │ │ │ + // LRU - move tile to the end of the array to mark it as the most │ │ │ │ + // recently used │ │ │ │ + OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url); │ │ │ │ + this.tileCacheIndex.push(tile.url); │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: initMercatorParameters │ │ │ │ - * Set up the mercator parameters on the layer: resolutions, │ │ │ │ - * projection, units. │ │ │ │ + /** │ │ │ │ + * Method: addToCache │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} Listener argument for the tile's loadend event │ │ │ │ */ │ │ │ │ - initMercatorParameters: function() { │ │ │ │ - // set up properties for Mercator - assume EPSG:900913 │ │ │ │ - this.RESOLUTIONS = []; │ │ │ │ - var maxResolution = 156543.03390625; │ │ │ │ - for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) { │ │ │ │ - this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom); │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.units = "m"; │ │ │ │ - this.projection = this.projection || "EPSG:900913"; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: forwardMercator │ │ │ │ - * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator. │ │ │ │ + * Method: clearTileQueue │ │ │ │ + * Clears the tile queue from tiles of a specific layer │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * lon - {float} │ │ │ │ - * lat - {float} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} The coordinates transformed to Mercator. │ │ │ │ + * evt - {Object} Listener argument of the layer's retile event │ │ │ │ */ │ │ │ │ - forwardMercator: (function() { │ │ │ │ - var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ - var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ - return function(lon, lat) { │ │ │ │ - var point = OpenLayers.Projection.transform({ │ │ │ │ - x: lon, │ │ │ │ - y: lat │ │ │ │ - }, gg, sm); │ │ │ │ - return new OpenLayers.LonLat(point.x, point.y); │ │ │ │ - }; │ │ │ │ - })(), │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: inverseMercator │ │ │ │ - * Given a x,y in Spherical Mercator, return a point in EPSG:4326. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * x - {float} A map x in Spherical Mercator. │ │ │ │ - * y - {float} A map y in Spherical Mercator. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326. │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - inverseMercator: (function() { │ │ │ │ - var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ - var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ - return function(x, y) { │ │ │ │ - var point = OpenLayers.Projection.transform({ │ │ │ │ - x: x, │ │ │ │ - y: y │ │ │ │ - }, sm, gg); │ │ │ │ - return new OpenLayers.LonLat(point.x, point.y); │ │ │ │ - }; │ │ │ │ - })() │ │ │ │ + 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/Layer/EventPane.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/Layer.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.EventPane │ │ │ │ - * Base class for 3rd party layers, providing a DOM element which isolates │ │ │ │ - * the 3rd-party layer from mouse events. │ │ │ │ - * Only used by Google layers. │ │ │ │ - * │ │ │ │ - * Automatically instantiated by the Google constructor, and not usually instantiated directly. │ │ │ │ - * │ │ │ │ - * Create a new event pane layer with the │ │ │ │ - * <OpenLayers.Layer.EventPane> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer> │ │ │ │ + * Class: OpenLayers.Strategy │ │ │ │ + * Abstract vector layer strategy class. Not to be instantiated directly. Use │ │ │ │ + * one of the strategy subclasses instead. │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ +OpenLayers.Strategy = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: smoothDragPan │ │ │ │ - * {Boolean} smoothDragPan determines whether non-public/internal API │ │ │ │ - * methods are used for better performance while dragging EventPane │ │ │ │ - * layers. When not in sphericalMercator mode, the smoother dragging │ │ │ │ - * doesn't actually move north/south directly with the number of │ │ │ │ - * pixels moved, resulting in a slight offset when you drag your mouse │ │ │ │ - * north south with this option on. If this visual disparity bothers │ │ │ │ - * you, you should turn this option off, or use spherical mercator. │ │ │ │ - * Default is on. │ │ │ │ + * Property: layer │ │ │ │ + * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to. │ │ │ │ */ │ │ │ │ - smoothDragPan: true, │ │ │ │ + layer: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: isBaseLayer │ │ │ │ - * {Boolean} EventPaned layers are always base layers, by necessity. │ │ │ │ + * Property: options │ │ │ │ + * {Object} Any options sent to the constructor. │ │ │ │ */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + options: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: isFixed │ │ │ │ - * {Boolean} EventPaned layers are fixed by default. │ │ │ │ + /** │ │ │ │ + * Property: active │ │ │ │ + * {Boolean} The control is active. │ │ │ │ */ │ │ │ │ - isFixed: true, │ │ │ │ + active: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: pane │ │ │ │ - * {DOMElement} A reference to the element that controls the events. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - pane: null, │ │ │ │ - │ │ │ │ + autoActivate: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: mapObject │ │ │ │ - * {Object} This is the object which will be used to load the 3rd party library │ │ │ │ - * in the case of the google layer, this will be of type GMap, │ │ │ │ - * in the case of the ve layer, this will be of type VEMap │ │ │ │ + * Property: autoDestroy │ │ │ │ + * {Boolean} The creator of the strategy can set autoDestroy to false │ │ │ │ + * to fully control when the strategy is destroyed. Defaults to │ │ │ │ + * true. │ │ │ │ */ │ │ │ │ - mapObject: null, │ │ │ │ - │ │ │ │ + autoDestroy: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.EventPane │ │ │ │ - * Create a new event pane layer │ │ │ │ + * Constructor: OpenLayers.Strategy │ │ │ │ + * Abstract class for vector strategies. Create instances of a subclass. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ - initialize: function(name, options) { │ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ - if (this.pane == null) { │ │ │ │ - this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane"); │ │ │ │ - } │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.options = options; │ │ │ │ + // set the active property here, so that user cannot override it │ │ │ │ + this.active = false; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ * APIMethod: destroy │ │ │ │ - * Deconstruct this layer. │ │ │ │ + * Clean up the strategy. │ │ │ │ */ │ │ │ │ destroy: function() { │ │ │ │ - this.mapObject = null; │ │ │ │ - this.pane = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ + this.deactivate(); │ │ │ │ + this.layer = null; │ │ │ │ + this.options = null; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: setLayer │ │ │ │ + * Called to set the <layer> property. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ - │ │ │ │ - this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; │ │ │ │ - this.pane.style.display = this.div.style.display; │ │ │ │ - this.pane.style.width = "100%"; │ │ │ │ - this.pane.style.height = "100%"; │ │ │ │ - if (OpenLayers.BROWSER_NAME == "msie") { │ │ │ │ - this.pane.style.background = │ │ │ │ - "url(" + OpenLayers.Util.getImageLocation("blank.gif") + ")"; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.isFixed) { │ │ │ │ - this.map.viewPortDiv.appendChild(this.pane); │ │ │ │ - } else { │ │ │ │ - this.map.layerContainerDiv.appendChild(this.pane); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // once our layer has been added to the map, we can load it │ │ │ │ - this.loadMapObject(); │ │ │ │ - │ │ │ │ - // if map didn't load, display warning │ │ │ │ - if (this.mapObject == null) { │ │ │ │ - this.loadWarningMessage(); │ │ │ │ - } │ │ │ │ + setLayer: function(layer) { │ │ │ │ + this.layer = layer; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: removeMap │ │ │ │ - * On being removed from the map, we'll like to remove the invisible 'pane' │ │ │ │ - * div that we added to it on creation. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * Method: activate │ │ │ │ + * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} True if the strategy was successfully activated or false if │ │ │ │ + * the strategy was already active. │ │ │ │ */ │ │ │ │ - removeMap: function(map) { │ │ │ │ - if (this.pane && this.pane.parentNode) { │ │ │ │ - this.pane.parentNode.removeChild(this.pane); │ │ │ │ + activate: function() { │ │ │ │ + if (!this.active) { │ │ │ │ + this.active = true; │ │ │ │ + return true; │ │ │ │ } │ │ │ │ - OpenLayers.Layer.prototype.removeMap.apply(this, arguments); │ │ │ │ + return false; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: loadWarningMessage │ │ │ │ - * If we can't load the map lib, then display an error message to the │ │ │ │ - * user and tell them where to go for help. │ │ │ │ - * │ │ │ │ - * This function sets up the layout for the warning message. Each 3rd │ │ │ │ - * party layer must implement its own getWarningHTML() function to │ │ │ │ - * provide the actual warning message. │ │ │ │ + * Method: deactivate │ │ │ │ + * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ + * tear-down. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} True if the strategy was successfully deactivated or false if │ │ │ │ + * the strategy was already inactive. │ │ │ │ */ │ │ │ │ - loadWarningMessage: function() { │ │ │ │ - │ │ │ │ - this.div.style.backgroundColor = "darkblue"; │ │ │ │ - │ │ │ │ - var viewSize = this.map.getSize(); │ │ │ │ - │ │ │ │ - var msgW = Math.min(viewSize.w, 300); │ │ │ │ - var msgH = Math.min(viewSize.h, 200); │ │ │ │ - var size = new OpenLayers.Size(msgW, msgH); │ │ │ │ - │ │ │ │ - var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2); │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + this.active = false; │ │ │ │ + return true; │ │ │ │ + } │ │ │ │ + return false; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var topLeft = centerPx.add(-size.w / 2, -size.h / 2); │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Events/featureclick.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - var div = OpenLayers.Util.createDiv(this.name + "_warning", │ │ │ │ - topLeft, │ │ │ │ - size, │ │ │ │ - null, │ │ │ │ - null, │ │ │ │ - null, │ │ │ │ - "auto"); │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - div.style.padding = "7px"; │ │ │ │ - div.style.backgroundColor = "yellow"; │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Events.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - div.innerHTML = this.getWarningHTML(); │ │ │ │ - this.div.appendChild(div); │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Events.featureclick │ │ │ │ + * │ │ │ │ + * Extension event type for handling feature click events, including overlapping │ │ │ │ + * features. │ │ │ │ + * │ │ │ │ + * Event types provided by this extension: │ │ │ │ + * - featureclick │ │ │ │ + */ │ │ │ │ +OpenLayers.Events.featureclick = OpenLayers.Class({ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getWarningHTML │ │ │ │ - * To be implemented by subclasses. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} String with information on why layer is broken, how to get │ │ │ │ - * it working. │ │ │ │ + /** │ │ │ │ + * Property: cache │ │ │ │ + * {Object} A cache of features under the mouse. │ │ │ │ */ │ │ │ │ - getWarningHTML: function() { │ │ │ │ - //should be implemented by subclasses │ │ │ │ - return ""; │ │ │ │ - }, │ │ │ │ + cache: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: display │ │ │ │ - * Set the display on the pane │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * display - {Boolean} │ │ │ │ + * Property: map │ │ │ │ + * {<OpenLayers.Map>} The map to register browser events on. │ │ │ │ */ │ │ │ │ - display: function(display) { │ │ │ │ - OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ - this.pane.style.display = this.div.style.display; │ │ │ │ - }, │ │ │ │ + map: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setZIndex │ │ │ │ - * Set the z-index order for the pane. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * zIndex - {int} │ │ │ │ + * Property: provides │ │ │ │ + * {Array(String)} The event types provided by this extension. │ │ │ │ */ │ │ │ │ - setZIndex: function(zIndex) { │ │ │ │ - OpenLayers.Layer.prototype.setZIndex.apply(this, arguments); │ │ │ │ - this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; │ │ │ │ - }, │ │ │ │ + provides: ["featureclick", "nofeatureclick", "featureover", "featureout"], │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveByPx │ │ │ │ - * Move the layer based on pixel vector. To be implemented by subclasses. │ │ │ │ + * Constructor: OpenLayers.Events.featureclick │ │ │ │ + * Create a new featureclick event type. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * dx - {Number} The x coord of the displacement vector. │ │ │ │ - * dy - {Number} The y coord of the displacement vector. │ │ │ │ + * target - {<OpenLayers.Events>} The events instance to create the events │ │ │ │ + * for. │ │ │ │ */ │ │ │ │ - moveByPx: function(dx, dy) { │ │ │ │ - OpenLayers.Layer.prototype.moveByPx.apply(this, arguments); │ │ │ │ - │ │ │ │ - if (this.dragPanMapObject) { │ │ │ │ - this.dragPanMapObject(dx, -dy); │ │ │ │ + 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 { │ │ │ │ - this.moveTo(this.map.getCachedCenter()); │ │ │ │ + throw ("Listeners for '" + this.provides.join("', '") + │ │ │ │ + "' events can only be registered for OpenLayers.Layer.Vector " + │ │ │ │ + "or OpenLayers.Map instances"); │ │ │ │ + } │ │ │ │ + for (var i = this.provides.length - 1; i >= 0; --i) { │ │ │ │ + target.extensions[this.provides[i]] = true; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveTo │ │ │ │ - * Handle calls to move the layer. │ │ │ │ - * │ │ │ │ + * Method: setMap │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * zoomChanged - {Boolean} │ │ │ │ - * dragging - {Boolean} │ │ │ │ + * map - {<OpenLayers.Map>} The map to register browser events on. │ │ │ │ */ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ - │ │ │ │ - if (this.mapObject != null) { │ │ │ │ - │ │ │ │ - var newCenter = this.map.getCenter(); │ │ │ │ - var newZoom = this.map.getZoom(); │ │ │ │ - │ │ │ │ - if (newCenter != null) { │ │ │ │ - │ │ │ │ - var moOldCenter = this.getMapObjectCenter(); │ │ │ │ - var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter); │ │ │ │ - │ │ │ │ - var moOldZoom = this.getMapObjectZoom(); │ │ │ │ - var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom); │ │ │ │ - │ │ │ │ - if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) { │ │ │ │ - │ │ │ │ - if (!zoomChanged && oldCenter && this.dragPanMapObject && │ │ │ │ - this.smoothDragPan) { │ │ │ │ - var oldPx = this.map.getViewPortPxFromLonLat(oldCenter); │ │ │ │ - var newPx = this.map.getViewPortPxFromLonLat(newCenter); │ │ │ │ - this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y); │ │ │ │ - } else { │ │ │ │ - var center = this.getMapObjectLonLatFromOLLonLat(newCenter); │ │ │ │ - var zoom = this.getMapObjectZoomFromOLZoom(newZoom); │ │ │ │ - this.setMapObjectCenter(center, zoom, dragging); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + 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 │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ - /********************************************************/ │ │ │ │ - /* */ │ │ │ │ - /* Baselayer Functions */ │ │ │ │ - /* */ │ │ │ │ - /********************************************************/ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: getLonLatFromViewPortPx │ │ │ │ - * Get a map location from a pixel location │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * viewPortPx - {<OpenLayers.Pixel>} │ │ │ │ + * Method: start │ │ │ │ + * Sets startEvt = evt. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view │ │ │ │ - * port OpenLayers.Pixel, translated into lon/lat by map lib │ │ │ │ - * If the map lib is not loaded or not centered, returns null │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ */ │ │ │ │ - getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ - var lonlat = null; │ │ │ │ - if ((this.mapObject != null) && │ │ │ │ - (this.getMapObjectCenter() != null)) { │ │ │ │ - var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx); │ │ │ │ - var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel); │ │ │ │ - lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat); │ │ │ │ - } │ │ │ │ - return lonlat; │ │ │ │ + start: function(evt) { │ │ │ │ + this.startEvt = evt; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: getViewPortPxFromLonLat │ │ │ │ - * Get a pixel location from a map location │ │ │ │ + * Method: cancel │ │ │ │ + * Deletes the start event. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * lonlat - {<OpenLayers.LonLat>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in │ │ │ │ - * OpenLayers.LonLat, translated into view port pixels by map lib │ │ │ │ - * If map lib is not loaded or not centered, returns null │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ */ │ │ │ │ - getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ - var viewPortPx = null; │ │ │ │ - if ((this.mapObject != null) && │ │ │ │ - (this.getMapObjectCenter() != null)) { │ │ │ │ - │ │ │ │ - var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat); │ │ │ │ - var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat); │ │ │ │ - │ │ │ │ - viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel); │ │ │ │ - } │ │ │ │ - return viewPortPx; │ │ │ │ + cancel: function(evt) { │ │ │ │ + delete this.startEvt; │ │ │ │ }, │ │ │ │ │ │ │ │ - /********************************************************/ │ │ │ │ - /* */ │ │ │ │ - /* Translation Functions */ │ │ │ │ - /* */ │ │ │ │ - /* The following functions translate Map Object and */ │ │ │ │ - /* OL formats for Pixel, LonLat */ │ │ │ │ - /* */ │ │ │ │ - /********************************************************/ │ │ │ │ - │ │ │ │ - // │ │ │ │ - // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat │ │ │ │ - // │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: getOLLonLatFromMapObjectLonLat │ │ │ │ - * Get an OL style map location from a 3rd party style map location │ │ │ │ + * Method: onClick │ │ │ │ + * Listener for the click event. │ │ │ │ * │ │ │ │ - * Parameters │ │ │ │ - * moLonLat - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in │ │ │ │ - * MapObject LonLat │ │ │ │ - * Returns null if null value is passed in │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ */ │ │ │ │ - getOLLonLatFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - var olLonLat = null; │ │ │ │ - if (moLonLat != null) { │ │ │ │ - var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - olLonLat = new OpenLayers.LonLat(lon, lat); │ │ │ │ + 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 │ │ │ │ + }); │ │ │ │ + } │ │ │ │ } │ │ │ │ - return olLonLat; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getMapObjectLonLatFromOLLonLat │ │ │ │ - * Get a 3rd party map location from an OL map location. │ │ │ │ + * Method: onMousemove │ │ │ │ + * Listener for the mousemove event. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * olLonLat - {<OpenLayers.LonLat>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} A MapObject LonLat, translated from the passed in │ │ │ │ - * OpenLayers.LonLat │ │ │ │ - * Returns null if null value is passed in │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ */ │ │ │ │ - getMapObjectLonLatFromOLLonLat: function(olLonLat) { │ │ │ │ - var moLatLng = null; │ │ │ │ - if (olLonLat != null) { │ │ │ │ - moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, │ │ │ │ - olLonLat.lat); │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ } │ │ │ │ - return moLatLng; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ - // │ │ │ │ - // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel │ │ │ │ - // │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: getOLPixelFromMapObjectPixel │ │ │ │ - * Get an OL pixel location from a 3rd party pixel location. │ │ │ │ + * Method: triggerEvent │ │ │ │ + * Determines where to trigger the event and triggers it. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * moPixel - {Object} │ │ │ │ - * │ │ │ │ + * type - {String} The event type to trigger │ │ │ │ + * evt - {Object} The listener argument │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in │ │ │ │ - * MapObject Pixel │ │ │ │ - * Returns null if null value is passed in │ │ │ │ + * {Boolean} The last listener return. │ │ │ │ */ │ │ │ │ - getOLPixelFromMapObjectPixel: function(moPixel) { │ │ │ │ - var olPixel = null; │ │ │ │ - if (moPixel != null) { │ │ │ │ - var x = this.getXFromMapObjectPixel(moPixel); │ │ │ │ - var y = this.getYFromMapObjectPixel(moPixel); │ │ │ │ - olPixel = new OpenLayers.Pixel(x, y); │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - return olPixel; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getMapObjectPixelFromOLPixel │ │ │ │ - * Get a 3rd party pixel location from an OL pixel location │ │ │ │ + * Method: getFeatures │ │ │ │ + * Get all features at the given screen location. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * olPixel - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ + * evt - {Object} Event object. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Object} A MapObject Pixel, translated from the passed in │ │ │ │ - * OpenLayers.Pixel │ │ │ │ - * Returns null if null value is passed in │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} List of features at the given point. │ │ │ │ */ │ │ │ │ - getMapObjectPixelFromOLPixel: function(olPixel) { │ │ │ │ - var moPixel = null; │ │ │ │ - if (olPixel != null) { │ │ │ │ - moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y); │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - return moPixel; │ │ │ │ + // restore feature visibility │ │ │ │ + for (i = 0, len = targets.length; i < len; ++i) { │ │ │ │ + targets[i].style.display = ""; │ │ │ │ + } │ │ │ │ + // restore layer visibility │ │ │ │ + for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ + layers[i].div.style.display = "block"; │ │ │ │ + } │ │ │ │ + return features; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.EventPane" │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + for (var i = this.provides.length - 1; i >= 0; --i) { │ │ │ │ + delete this.target.extensions[this.provides[i]]; │ │ │ │ + } │ │ │ │ + this.map.events.un({ │ │ │ │ + mousemove: this.onMousemove, │ │ │ │ + mousedown: this.start, │ │ │ │ + mouseup: this.onClick, │ │ │ │ + touchstart: this.start, │ │ │ │ + touchmove: this.cancel, │ │ │ │ + touchend: this.onClick, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + delete this.cache; │ │ │ │ + delete this.map; │ │ │ │ + delete this.target; │ │ │ │ + } │ │ │ │ + │ │ │ │ }); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Events.nofeatureclick │ │ │ │ + * │ │ │ │ + * Extension event type for handling click events that do not hit a feature. │ │ │ │ + * │ │ │ │ + * Event types provided by this extension: │ │ │ │ + * - nofeatureclick │ │ │ │ + */ │ │ │ │ +OpenLayers.Events.nofeatureclick = OpenLayers.Events.featureclick; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Events.featureover │ │ │ │ + * │ │ │ │ + * Extension event type for handling hovering over a feature. │ │ │ │ + * │ │ │ │ + * Event types provided by this extension: │ │ │ │ + * - featureover │ │ │ │ + */ │ │ │ │ +OpenLayers.Events.featureover = OpenLayers.Events.featureclick; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Events.featureout │ │ │ │ + * │ │ │ │ + * Extension event type for handling leaving a feature. │ │ │ │ + * │ │ │ │ + * Event types provided by this extension: │ │ │ │ + * - featureout │ │ │ │ + */ │ │ │ │ +OpenLayers.Events.featureout = OpenLayers.Events.featureclick; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/FixedZoomLevels.js │ │ │ │ + OpenLayers/Events/buttonclick.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Layer.js │ │ │ │ + * @requires OpenLayers/Events.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.FixedZoomLevels │ │ │ │ - * Some Layers will already have established zoom levels (like google │ │ │ │ - * or ve). Instead of trying to determine them and populate a resolutions[] │ │ │ │ - * Array with those values, we will hijack the resolution functionality │ │ │ │ - * here. │ │ │ │ - * │ │ │ │ - * When you subclass FixedZoomLevels: │ │ │ │ - * │ │ │ │ - * The initResolutions() call gets nullified, meaning no resolutions[] array │ │ │ │ - * is set up. Which would be a big problem getResolution() in Layer, since │ │ │ │ - * it merely takes map.zoom and indexes into resolutions[]... but.... │ │ │ │ - * │ │ │ │ - * The getResolution() call is also overridden. Instead of using the │ │ │ │ - * resolutions[] array, we simply calculate the current resolution based │ │ │ │ - * on the current extent and the current map size. But how will we be able │ │ │ │ - * to calculate the current extent without knowing the resolution...? │ │ │ │ - * │ │ │ │ - * The getExtent() function is also overridden. Instead of calculating extent │ │ │ │ - * based on the center point and the current resolution, we instead │ │ │ │ - * calculate the extent by getting the lonlats at the top-left and │ │ │ │ - * bottom-right by using the getLonLatFromViewPortPx() translation function, │ │ │ │ - * taken from the pixel locations (0,0) and the size of the map. But how │ │ │ │ - * will we be able to do lonlat-px translation without resolution....? │ │ │ │ - * │ │ │ │ - * The getZoomForResolution() method is overridden. Instead of indexing into │ │ │ │ - * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in │ │ │ │ - * the desired resolution. With this extent, we then call getZoomForExtent() │ │ │ │ - * │ │ │ │ - * │ │ │ │ - * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels, │ │ │ │ - * it is your responsibility to provide the following three functions: │ │ │ │ - * │ │ │ │ - * - getLonLatFromViewPortPx │ │ │ │ - * - getViewPortPxFromLonLat │ │ │ │ - * - getZoomForExtent │ │ │ │ - * │ │ │ │ - * ...those three functions should generally be provided by any reasonable │ │ │ │ - * API that you might be working from. │ │ │ │ + * Class: OpenLayers.Events.buttonclick │ │ │ │ + * Extension event type for handling buttons on top of a dom element. This │ │ │ │ + * event type fires "buttonclick" on its <target> when a button was │ │ │ │ + * clicked. Buttons are detected by the "olButton" class. │ │ │ │ * │ │ │ │ + * This event type makes sure that button clicks do not interfere with other │ │ │ │ + * events that are registered on the same <element>. │ │ │ │ + * │ │ │ │ + * Event types provided by this extension: │ │ │ │ + * - *buttonclick* Triggered when a button is clicked. Listeners receive an │ │ │ │ + * object with a *buttonElement* property referencing the dom element of │ │ │ │ + * the clicked button, and an *buttonXY* property with the click position │ │ │ │ + * relative to the button. │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /********************************************************/ │ │ │ │ - /* */ │ │ │ │ - /* Baselayer Functions */ │ │ │ │ - /* */ │ │ │ │ - /* The following functions must all be implemented */ │ │ │ │ - /* by all base layers */ │ │ │ │ - /* */ │ │ │ │ - /********************************************************/ │ │ │ │ +OpenLayers.Events.buttonclick = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.FixedZoomLevels │ │ │ │ - * Create a new fixed zoom levels layer. │ │ │ │ + * Property: target │ │ │ │ + * {<OpenLayers.Events>} The events instance that the buttonclick event will │ │ │ │ + * be triggered on. │ │ │ │ */ │ │ │ │ - initialize: function() { │ │ │ │ - //this class is only just to add the following functions... │ │ │ │ - // nothing to actually do here... but it is probably a good │ │ │ │ - // idea to have layers that use these functions call this │ │ │ │ - // inititalize() anyways, in case at some point we decide we │ │ │ │ - // do want to put some functionality or state in here. │ │ │ │ - }, │ │ │ │ + target: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: initResolutions │ │ │ │ - * Populate the resolutions array │ │ │ │ + * Property: events │ │ │ │ + * {Array} Events to observe and conditionally stop from propagating when │ │ │ │ + * an element with the olButton class (or its olAlphaImg child) is │ │ │ │ + * clicked. │ │ │ │ */ │ │ │ │ - initResolutions: function() { │ │ │ │ - │ │ │ │ - var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels']; │ │ │ │ - │ │ │ │ - for (var i = 0, len = props.length; i < len; i++) { │ │ │ │ - var property = props[i]; │ │ │ │ - this[property] = (this.options[property] != null) ? │ │ │ │ - this.options[property] : │ │ │ │ - this.map[property]; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if ((this.minZoomLevel == null) || │ │ │ │ - (this.minZoomLevel < this.MIN_ZOOM_LEVEL)) { │ │ │ │ - this.minZoomLevel = this.MIN_ZOOM_LEVEL; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // │ │ │ │ - // At this point, we know what the minimum desired zoom level is, and │ │ │ │ - // we must calculate the total number of zoom levels. │ │ │ │ - // │ │ │ │ - // Because we allow for the setting of either the 'numZoomLevels' │ │ │ │ - // or the 'maxZoomLevel' properties... on either the layer or the │ │ │ │ - // map, we have to define some rules to see which we take into │ │ │ │ - // account first in this calculation. │ │ │ │ - // │ │ │ │ - // The following is the precedence list for these properties: │ │ │ │ - // │ │ │ │ - // (1) numZoomLevels set on layer │ │ │ │ - // (2) maxZoomLevel set on layer │ │ │ │ - // (3) numZoomLevels set on map │ │ │ │ - // (4) maxZoomLevel set on map* │ │ │ │ - // (5) none of the above* │ │ │ │ - // │ │ │ │ - // *Note that options (4) and (5) are only possible if the user │ │ │ │ - // _explicitly_ sets the 'numZoomLevels' property on the map to │ │ │ │ - // null, since it is set by default to 16. │ │ │ │ - // │ │ │ │ - │ │ │ │ - // │ │ │ │ - // Note to future: In 3.0, I think we should remove the default │ │ │ │ - // value of 16 for map.numZoomLevels. Rather, I think that value │ │ │ │ - // should be set as a default on the Layer.WMS class. If someone │ │ │ │ - // creates a 3rd party layer and does not specify any 'minZoomLevel', │ │ │ │ - // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly │ │ │ │ - // specified any of those on the map object either.. then I think │ │ │ │ - // it is fair to say that s/he wants all the zoom levels available. │ │ │ │ - // │ │ │ │ - // By making map.numZoomLevels *null* by default, that will be the │ │ │ │ - // case. As it is, I don't feel comfortable changing that right now │ │ │ │ - // as it would be a glaring API change and actually would probably │ │ │ │ - // break many peoples' codes. │ │ │ │ - // │ │ │ │ - │ │ │ │ - //the number of zoom levels we'd like to have. │ │ │ │ - var desiredZoomLevels; │ │ │ │ - │ │ │ │ - //this is the maximum number of zoom levels the layer will allow, │ │ │ │ - // given the specified starting minimum zoom level. │ │ │ │ - var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1; │ │ │ │ - │ │ │ │ - if (((this.options.numZoomLevels == null) && │ │ │ │ - (this.options.maxZoomLevel != null)) // (2) │ │ │ │ - || │ │ │ │ - ((this.numZoomLevels == null) && │ │ │ │ - (this.maxZoomLevel != null)) // (4) │ │ │ │ - ) { │ │ │ │ - //calculate based on specified maxZoomLevel (on layer or map) │ │ │ │ - desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1; │ │ │ │ - } else { │ │ │ │ - //calculate based on specified numZoomLevels (on layer or map) │ │ │ │ - // this covers cases (1) and (3) │ │ │ │ - desiredZoomLevels = this.numZoomLevels; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (desiredZoomLevels != null) { │ │ │ │ - //Now that we know what we would *like* the number of zoom levels │ │ │ │ - // to be, based on layer or map options, we have to make sure that │ │ │ │ - // it does not conflict with the actual limit, as specified by │ │ │ │ - // the constants on the layer itself (and calculated into the │ │ │ │ - // 'limitZoomLevels' variable). │ │ │ │ - this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels); │ │ │ │ - } else { │ │ │ │ - // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was │ │ │ │ - // set on either the layer or the map. So we just use the │ │ │ │ - // maximum limit as calculated by the layer's constants. │ │ │ │ - this.numZoomLevels = limitZoomLevels; │ │ │ │ - } │ │ │ │ - │ │ │ │ - //now that the 'numZoomLevels' is appropriately, safely set, │ │ │ │ - // we go back and re-calculate the 'maxZoomLevel'. │ │ │ │ - this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1; │ │ │ │ + events: [ │ │ │ │ + 'mousedown', 'mouseup', 'click', 'dblclick', │ │ │ │ + 'touchstart', 'touchmove', 'touchend', 'keydown' │ │ │ │ + ], │ │ │ │ │ │ │ │ - if (this.RESOLUTIONS != null) { │ │ │ │ - var resolutionsIndex = 0; │ │ │ │ - this.resolutions = []; │ │ │ │ - for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) { │ │ │ │ - this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i]; │ │ │ │ - } │ │ │ │ - this.maxResolution = this.resolutions[0]; │ │ │ │ - this.minResolution = this.resolutions[this.resolutions.length - 1]; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: startRegEx │ │ │ │ + * {RegExp} Regular expression to test Event.type for events that start │ │ │ │ + * a buttonclick sequence. │ │ │ │ + */ │ │ │ │ + startRegEx: /^mousedown|touchstart$/, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getResolution │ │ │ │ - * Get the current map resolution │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Float} Map units per Pixel │ │ │ │ + * Property: cancelRegEx │ │ │ │ + * {RegExp} Regular expression to test Event.type for events that cancel │ │ │ │ + * a buttonclick sequence. │ │ │ │ */ │ │ │ │ - getResolution: function() { │ │ │ │ + cancelRegEx: /^touchmove$/, │ │ │ │ │ │ │ │ - if (this.resolutions != null) { │ │ │ │ - return OpenLayers.Layer.prototype.getResolution.apply(this, arguments); │ │ │ │ - } else { │ │ │ │ - var resolution = null; │ │ │ │ + /** │ │ │ │ + * Property: completeRegEx │ │ │ │ + * {RegExp} Regular expression to test Event.type for events that complete │ │ │ │ + * a buttonclick sequence. │ │ │ │ + */ │ │ │ │ + completeRegEx: /^mouseup|touchend$/, │ │ │ │ │ │ │ │ - var viewSize = this.map.getSize(); │ │ │ │ - var extent = this.getExtent(); │ │ │ │ + /** │ │ │ │ + * Property: startEvt │ │ │ │ + * {Event} The event that started the click sequence │ │ │ │ + */ │ │ │ │ │ │ │ │ - if ((viewSize != null) && (extent != null)) { │ │ │ │ - resolution = Math.max(extent.getWidth() / viewSize.w, │ │ │ │ - extent.getHeight() / viewSize.h); │ │ │ │ - } │ │ │ │ - return resolution; │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Events.buttonclick │ │ │ │ + * Construct a buttonclick event type. Applications are not supposed to │ │ │ │ + * create instances of this class - they are created on demand by │ │ │ │ + * <OpenLayers.Events> instances. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * target - {<OpenLayers.Events>} The events instance that the buttonclick │ │ │ │ + * event will be triggered on. │ │ │ │ + */ │ │ │ │ + initialize: function(target) { │ │ │ │ + this.target = target; │ │ │ │ + for (var i = this.events.length - 1; i >= 0; --i) { │ │ │ │ + this.target.register(this.events[i], this, this.buttonClick, { │ │ │ │ + extension: true │ │ │ │ + }); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getExtent │ │ │ │ - * Calculates using px-> lonlat translation functions on tl and br │ │ │ │ - * corners of viewport │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat │ │ │ │ - * bounds of the current viewPort. │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - getExtent: function() { │ │ │ │ - var size = this.map.getSize(); │ │ │ │ - var tl = this.getLonLatFromViewPortPx({ │ │ │ │ - x: 0, │ │ │ │ - y: 0 │ │ │ │ - }); │ │ │ │ - var br = this.getLonLatFromViewPortPx({ │ │ │ │ - x: size.w, │ │ │ │ - y: size.h │ │ │ │ - }); │ │ │ │ - │ │ │ │ - if ((tl != null) && (br != null)) { │ │ │ │ - return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat); │ │ │ │ - } else { │ │ │ │ - return null; │ │ │ │ + destroy: function() { │ │ │ │ + for (var i = this.events.length - 1; i >= 0; --i) { │ │ │ │ + this.target.unregister(this.events[i], this, this.buttonClick); │ │ │ │ } │ │ │ │ + delete this.target; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getZoomForResolution │ │ │ │ - * Get the zoom level for a given resolution │ │ │ │ + * Method: getPressedButton │ │ │ │ + * Get the pressed button, if any. Returns undefined if no button │ │ │ │ + * was pressed. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * resolution - {Float} │ │ │ │ + * Arguments: │ │ │ │ + * element - {DOMElement} The event target. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Integer} A suitable zoom level for the specified resolution. │ │ │ │ - * If no baselayer is set, returns null. │ │ │ │ + * {DOMElement} The button element, or undefined. │ │ │ │ */ │ │ │ │ - getZoomForResolution: function(resolution) { │ │ │ │ - │ │ │ │ - if (this.resolutions != null) { │ │ │ │ - return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments); │ │ │ │ - } else { │ │ │ │ - var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []); │ │ │ │ - return this.getZoomForExtent(extent); │ │ │ │ - } │ │ │ │ + getPressedButton: function(element) { │ │ │ │ + var depth = 3, // limit the search depth │ │ │ │ + button; │ │ │ │ + do { │ │ │ │ + if (OpenLayers.Element.hasClass(element, "olButton")) { │ │ │ │ + // hit! │ │ │ │ + button = element; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + element = element.parentNode; │ │ │ │ + } while (--depth > 0 && element); │ │ │ │ + return button; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ - │ │ │ │ - │ │ │ │ - /********************************************************/ │ │ │ │ - /* */ │ │ │ │ - /* Translation Functions */ │ │ │ │ - /* */ │ │ │ │ - /* The following functions translate GMaps and OL */ │ │ │ │ - /* formats for Pixel, LonLat, Bounds, and Zoom */ │ │ │ │ - /* */ │ │ │ │ - /********************************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // │ │ │ │ - // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom │ │ │ │ - // │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: getOLZoomFromMapObjectZoom │ │ │ │ - * Get the OL zoom index from the map object zoom level │ │ │ │ + * Method: ignore │ │ │ │ + * Check for event target elements that should be ignored by OpenLayers. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * moZoom - {Integer} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} An OpenLayers Zoom level, translated from the passed in zoom │ │ │ │ - * Returns null if null value is passed in │ │ │ │ + * element - {DOMElement} The event target. │ │ │ │ */ │ │ │ │ - getOLZoomFromMapObjectZoom: function(moZoom) { │ │ │ │ - var zoom = null; │ │ │ │ - if (moZoom != null) { │ │ │ │ - zoom = moZoom - this.minZoomLevel; │ │ │ │ - if (this.map.baseLayer !== this) { │ │ │ │ - zoom = this.map.baseLayer.getZoomForResolution( │ │ │ │ - this.getResolutionForZoom(zoom) │ │ │ │ - ); │ │ │ │ + ignore: function(element) { │ │ │ │ + var depth = 3, │ │ │ │ + ignore = false; │ │ │ │ + do { │ │ │ │ + if (element.nodeName.toLowerCase() === 'a') { │ │ │ │ + ignore = true; │ │ │ │ + break; │ │ │ │ } │ │ │ │ - } │ │ │ │ - return zoom; │ │ │ │ + element = element.parentNode; │ │ │ │ + } while (--depth > 0 && element); │ │ │ │ + return ignore; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getMapObjectZoomFromOLZoom │ │ │ │ - * Get the map object zoom level from the OL zoom level │ │ │ │ + * Method: buttonClick │ │ │ │ + * Check if a button was clicked, and fire the buttonclick event │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * olZoom - {Integer} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} A MapObject level, translated from the passed in olZoom │ │ │ │ - * Returns null if null value is passed in │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - getMapObjectZoomFromOLZoom: function(olZoom) { │ │ │ │ - var zoom = null; │ │ │ │ - if (olZoom != null) { │ │ │ │ - zoom = olZoom + this.minZoomLevel; │ │ │ │ - if (this.map.baseLayer !== this) { │ │ │ │ - zoom = this.getZoomForResolution( │ │ │ │ - this.map.baseLayer.getResolutionForZoom(zoom) │ │ │ │ - ); │ │ │ │ + buttonClick: function(evt) { │ │ │ │ + var propagate = true, │ │ │ │ + element = OpenLayers.Event.element(evt); │ │ │ │ + if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) { │ │ │ │ + // was a button pressed? │ │ │ │ + var button = this.getPressedButton(element); │ │ │ │ + if (button) { │ │ │ │ + if (evt.type === "keydown") { │ │ │ │ + switch (evt.keyCode) { │ │ │ │ + case OpenLayers.Event.KEY_RETURN: │ │ │ │ + case OpenLayers.Event.KEY_SPACE: │ │ │ │ + this.target.triggerEvent("buttonclick", { │ │ │ │ + buttonElement: button │ │ │ │ + }); │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + propagate = false; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } else if (this.startEvt) { │ │ │ │ + if (this.completeRegEx.test(evt.type)) { │ │ │ │ + var pos = OpenLayers.Util.pagePosition(button); │ │ │ │ + var viewportElement = OpenLayers.Util.getViewportElement(); │ │ │ │ + var scrollTop = window.pageYOffset || viewportElement.scrollTop; │ │ │ │ + var scrollLeft = window.pageXOffset || viewportElement.scrollLeft; │ │ │ │ + pos[0] = pos[0] - scrollLeft; │ │ │ │ + pos[1] = pos[1] - scrollTop; │ │ │ │ + │ │ │ │ + this.target.triggerEvent("buttonclick", { │ │ │ │ + buttonElement: button, │ │ │ │ + buttonXY: { │ │ │ │ + x: this.startEvt.clientX - pos[0], │ │ │ │ + y: this.startEvt.clientY - pos[1] │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + if (this.cancelRegEx.test(evt.type)) { │ │ │ │ + delete this.startEvt; │ │ │ │ + } │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + propagate = false; │ │ │ │ + } │ │ │ │ + if (this.startRegEx.test(evt.type)) { │ │ │ │ + this.startEvt = evt; │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + propagate = false; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + propagate = !this.ignore(OpenLayers.Event.element(evt)); │ │ │ │ + delete this.startEvt; │ │ │ │ } │ │ │ │ } │ │ │ │ - return zoom; │ │ │ │ - }, │ │ │ │ + return propagate; │ │ │ │ + } │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels" │ │ │ │ }); │ │ │ │ - │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/Google.js │ │ │ │ + OpenLayers/Strategy/Filter.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Layer/SphericalMercator.js │ │ │ │ - * @requires OpenLayers/Layer/EventPane.js │ │ │ │ - * @requires OpenLayers/Layer/FixedZoomLevels.js │ │ │ │ - * @requires OpenLayers/Lang.js │ │ │ │ + * @requires OpenLayers/Strategy.js │ │ │ │ + * @requires OpenLayers/Filter.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.Google │ │ │ │ - * │ │ │ │ - * Provides a wrapper for Google's Maps API │ │ │ │ - * Normally the Terms of Use for this API do not allow wrapping, but Google │ │ │ │ - * have provided written consent to OpenLayers for this - see email in │ │ │ │ - * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Strategy.Filter │ │ │ │ + * Strategy for limiting features that get added to a layer by │ │ │ │ + * evaluating a filter. The strategy maintains a cache of │ │ │ │ + * all features until removeFeatures is called on the layer. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.SphericalMercator> │ │ │ │ - * - <OpenLayers.Layer.EventPane> │ │ │ │ - * - <OpenLayers.Layer.FixedZoomLevels> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Google = OpenLayers.Class( │ │ │ │ - OpenLayers.Layer.EventPane, │ │ │ │ - OpenLayers.Layer.FixedZoomLevels, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: MIN_ZOOM_LEVEL │ │ │ │ - * {Integer} 0 │ │ │ │ - */ │ │ │ │ - MIN_ZOOM_LEVEL: 0, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: MAX_ZOOM_LEVEL │ │ │ │ - * {Integer} 21 │ │ │ │ - */ │ │ │ │ - MAX_ZOOM_LEVEL: 21, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: RESOLUTIONS │ │ │ │ - * {Array(Float)} Hardcode these resolutions so that they are more closely │ │ │ │ - * tied with the standard wms projection │ │ │ │ - */ │ │ │ │ - RESOLUTIONS: [ │ │ │ │ - 1.40625, │ │ │ │ - 0.703125, │ │ │ │ - 0.3515625, │ │ │ │ - 0.17578125, │ │ │ │ - 0.087890625, │ │ │ │ - 0.0439453125, │ │ │ │ - 0.02197265625, │ │ │ │ - 0.010986328125, │ │ │ │ - 0.0054931640625, │ │ │ │ - 0.00274658203125, │ │ │ │ - 0.001373291015625, │ │ │ │ - 0.0006866455078125, │ │ │ │ - 0.00034332275390625, │ │ │ │ - 0.000171661376953125, │ │ │ │ - 0.0000858306884765625, │ │ │ │ - 0.00004291534423828125, │ │ │ │ - 0.00002145767211914062, │ │ │ │ - 0.00001072883605957031, │ │ │ │ - 0.00000536441802978515, │ │ │ │ - 0.00000268220901489257, │ │ │ │ - 0.0000013411045074462891, │ │ │ │ - 0.00000067055225372314453 │ │ │ │ - ], │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: type │ │ │ │ - * {GMapType} │ │ │ │ - */ │ │ │ │ - type: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: wrapDateLine │ │ │ │ - * {Boolean} Allow user to pan forever east/west. Default is true. │ │ │ │ - * Setting this to false only restricts panning if │ │ │ │ - * <sphericalMercator> is true. │ │ │ │ - */ │ │ │ │ - wrapDateLine: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: sphericalMercator │ │ │ │ - * {Boolean} Should the map act as a mercator-projected map? This will │ │ │ │ - * cause all interactions with the map to be in the actual map │ │ │ │ - * projection, which allows support for vector drawing, overlaying │ │ │ │ - * other maps, etc. │ │ │ │ - */ │ │ │ │ - sphericalMercator: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: version │ │ │ │ - * {Number} The version of the Google Maps API │ │ │ │ - */ │ │ │ │ - version: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Layer.Google │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} A name for the layer. │ │ │ │ - * options - {Object} An optional object whose properties will be set │ │ │ │ - * on the layer. │ │ │ │ - */ │ │ │ │ - initialize: function(name, options) { │ │ │ │ - options = options || {}; │ │ │ │ - if (!options.version) { │ │ │ │ - options.version = typeof GMap2 === "function" ? "2" : "3"; │ │ │ │ - } │ │ │ │ - var mixin = OpenLayers.Layer.Google["v" + │ │ │ │ - options.version.replace(/\./g, "_")]; │ │ │ │ - if (mixin) { │ │ │ │ - OpenLayers.Util.applyDefaults(options, mixin); │ │ │ │ - } else { │ │ │ │ - throw "Unsupported Google Maps API version: " + options.version; │ │ │ │ - } │ │ │ │ - │ │ │ │ - OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS); │ │ │ │ - if (options.maxExtent) { │ │ │ │ - options.maxExtent = options.maxExtent.clone(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - OpenLayers.Layer.EventPane.prototype.initialize.apply(this, │ │ │ │ - [name, options]); │ │ │ │ - OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, │ │ │ │ - [name, options]); │ │ │ │ - │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator); │ │ │ │ - this.initMercatorParameters(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.Google>} An exact clone of this layer │ │ │ │ - */ │ │ │ │ - clone: function() { │ │ │ │ - /** │ │ │ │ - * This method isn't intended to be called by a subclass and it │ │ │ │ - * doesn't call the same method on the superclass. We don't call │ │ │ │ - * the super's clone because we don't want properties that are set │ │ │ │ - * on this layer after initialize (i.e. this.mapObject etc.). │ │ │ │ - */ │ │ │ │ - return new OpenLayers.Layer.Google( │ │ │ │ - this.name, this.getOptions() │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setVisibility │ │ │ │ - * Set the visibility flag for the layer and hide/show & redraw │ │ │ │ - * accordingly. Fire event unless otherwise specified │ │ │ │ - * │ │ │ │ - * Note that visibility is no longer simply whether or not the layer's │ │ │ │ - * style.display is set to "block". Now we store a 'visibility' state │ │ │ │ - * property on the layer class, this allows us to remember whether or │ │ │ │ - * not we *desire* for a layer to be visible. In the case where the │ │ │ │ - * map's resolution is out of the layer's range, this desire may be │ │ │ │ - * subverted. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * visible - {Boolean} Display the layer (if in range) │ │ │ │ - */ │ │ │ │ - setVisibility: function(visible) { │ │ │ │ - // sharing a map container, opacity has to be set per layer │ │ │ │ - var opacity = this.opacity == null ? 1 : this.opacity; │ │ │ │ - OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments); │ │ │ │ - this.setOpacity(opacity); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: display │ │ │ │ - * Hide or show the Layer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * visible - {Boolean} │ │ │ │ - */ │ │ │ │ - display: function(visible) { │ │ │ │ - if (!this._dragging) { │ │ │ │ - this.setGMapVisibility(visible); │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to │ │ │ │ - * do some init work in that case. │ │ │ │ - * dragging - {Boolean} │ │ │ │ - */ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - this._dragging = dragging; │ │ │ │ - OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments); │ │ │ │ - delete this._dragging; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setOpacity │ │ │ │ - * Sets the opacity for the entire layer (all images) │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * opacity - {Float} │ │ │ │ - */ │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ - if (opacity !== this.opacity) { │ │ │ │ - if (this.map != null) { │ │ │ │ - this.map.events.triggerEvent("changelayer", { │ │ │ │ - layer: this, │ │ │ │ - property: "opacity" │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - this.opacity = opacity; │ │ │ │ - } │ │ │ │ - // Though this layer's opacity may not change, we're sharing a container │ │ │ │ - // and need to update the opacity for the entire container. │ │ │ │ - if (this.getVisibility()) { │ │ │ │ - var container = this.getMapContainer(); │ │ │ │ - OpenLayers.Util.modifyDOMElement( │ │ │ │ - container, null, null, null, null, null, null, opacity │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Clean up this layer. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - /** │ │ │ │ - * We have to override this method because the event pane destroy │ │ │ │ - * deletes the mapObject reference before removing this layer from │ │ │ │ - * the map. │ │ │ │ - */ │ │ │ │ - if (this.map) { │ │ │ │ - this.setGMapVisibility(false); │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache && cache.count <= 1) { │ │ │ │ - this.removeGMapElements(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: removeGMapElements │ │ │ │ - * Remove all elements added to the dom. This should only be called if │ │ │ │ - * this is the last of the Google layers for the given map. │ │ │ │ - */ │ │ │ │ - removeGMapElements: function() { │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - // remove shared elements from dom │ │ │ │ - var container = this.mapObject && this.getMapContainer(); │ │ │ │ - if (container && container.parentNode) { │ │ │ │ - container.parentNode.removeChild(container); │ │ │ │ - } │ │ │ │ - var termsOfUse = cache.termsOfUse; │ │ │ │ - if (termsOfUse && termsOfUse.parentNode) { │ │ │ │ - termsOfUse.parentNode.removeChild(termsOfUse); │ │ │ │ - } │ │ │ │ - var poweredBy = cache.poweredBy; │ │ │ │ - if (poweredBy && poweredBy.parentNode) { │ │ │ │ - poweredBy.parentNode.removeChild(poweredBy); │ │ │ │ - } │ │ │ │ - if (this.mapObject && window.google && google.maps && │ │ │ │ - google.maps.event && google.maps.event.clearListeners) { │ │ │ │ - google.maps.event.clearListeners(this.mapObject, 'tilesloaded'); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: removeMap │ │ │ │ - * On being removed from the map, also remove termsOfUse and poweredBy divs │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - removeMap: function(map) { │ │ │ │ - // hide layer before removing │ │ │ │ - if (this.visibility && this.mapObject) { │ │ │ │ - this.setGMapVisibility(false); │ │ │ │ - } │ │ │ │ - // check to see if last Google layer in this map │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[map.id]; │ │ │ │ - if (cache) { │ │ │ │ - if (cache.count <= 1) { │ │ │ │ - this.removeGMapElements(); │ │ │ │ - delete OpenLayers.Layer.Google.cache[map.id]; │ │ │ │ - } else { │ │ │ │ - // decrement the layer count │ │ │ │ - --cache.count; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - // remove references to gmap elements │ │ │ │ - delete this.termsOfUse; │ │ │ │ - delete this.poweredBy; │ │ │ │ - delete this.mapObject; │ │ │ │ - delete this.dragObject; │ │ │ │ - OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - // │ │ │ │ - // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds │ │ │ │ - // │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getOLBoundsFromMapObjectBounds │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moBounds - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the │ │ │ │ - * passed-in MapObject Bounds. │ │ │ │ - * Returns null if null value is passed in. │ │ │ │ - */ │ │ │ │ - getOLBoundsFromMapObjectBounds: function(moBounds) { │ │ │ │ - var olBounds = null; │ │ │ │ - if (moBounds != null) { │ │ │ │ - var sw = moBounds.getSouthWest(); │ │ │ │ - var ne = moBounds.getNorthEast(); │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - sw = this.forwardMercator(sw.lng(), sw.lat()); │ │ │ │ - ne = this.forwardMercator(ne.lng(), ne.lat()); │ │ │ │ - } else { │ │ │ │ - sw = new OpenLayers.LonLat(sw.lng(), sw.lat()); │ │ │ │ - ne = new OpenLayers.LonLat(ne.lng(), ne.lat()); │ │ │ │ - } │ │ │ │ - olBounds = new OpenLayers.Bounds(sw.lon, │ │ │ │ - sw.lat, │ │ │ │ - ne.lon, │ │ │ │ - ne.lat); │ │ │ │ - } │ │ │ │ - return olBounds; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getWarningHTML │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} String with information on why layer is broken, how to get │ │ │ │ - * it working. │ │ │ │ - */ │ │ │ │ - getWarningHTML: function() { │ │ │ │ - return OpenLayers.i18n("googleWarning"); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - /************************************ │ │ │ │ - * * │ │ │ │ - * MapObject Interface Controls * │ │ │ │ - * * │ │ │ │ - ************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // Get&Set Center, Zoom │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectCenter │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} The mapObject's current center in Map Object format │ │ │ │ - */ │ │ │ │ - getMapObjectCenter: function() { │ │ │ │ - return this.mapObject.getCenter(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectZoom │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} The mapObject's current zoom, in Map Object format │ │ │ │ - */ │ │ │ │ - getMapObjectZoom: function() { │ │ │ │ - return this.mapObject.getZoom(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - /************************************ │ │ │ │ - * * │ │ │ │ - * MapObject Primitives * │ │ │ │ - * * │ │ │ │ - ************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // LonLat │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getLongitudeFromMapObjectLonLat │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moLonLat - {Object} MapObject LonLat format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Float} Longitude of the given MapObject LonLat │ │ │ │ - */ │ │ │ │ - getLongitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - return this.sphericalMercator ? │ │ │ │ - this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : │ │ │ │ - moLonLat.lng(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getLatitudeFromMapObjectLonLat │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moLonLat - {Object} MapObject LonLat format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Float} Latitude of the given MapObject LonLat │ │ │ │ - */ │ │ │ │ - getLatitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - var lat = this.sphericalMercator ? │ │ │ │ - this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : │ │ │ │ - moLonLat.lat(); │ │ │ │ - return lat; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - // Pixel │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getXFromMapObjectPixel │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moPixel - {Object} MapObject Pixel format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} X value of the MapObject Pixel │ │ │ │ - */ │ │ │ │ - getXFromMapObjectPixel: function(moPixel) { │ │ │ │ - return moPixel.x; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getYFromMapObjectPixel │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moPixel - {Object} MapObject Pixel format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} Y value of the MapObject Pixel │ │ │ │ - */ │ │ │ │ - getYFromMapObjectPixel: function(moPixel) { │ │ │ │ - return moPixel.y; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Google" │ │ │ │ - }); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Property: OpenLayers.Layer.Google.cache │ │ │ │ - * {Object} Cache for elements that should only be created once per map. │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Google.cache = {}; │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Layer.Google.v2 │ │ │ │ - * │ │ │ │ - * Mixin providing functionality specific to the Google Maps API v2. │ │ │ │ - * │ │ │ │ - * This API has been deprecated by Google. │ │ │ │ - * Developers are encouraged to migrate to v3 of the API; support for this │ │ │ │ - * is provided by <OpenLayers.Layer.Google.v3> │ │ │ │ + * - <OpenLayers.Strategy> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.Google.v2 = { │ │ │ │ +OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: termsOfUse │ │ │ │ - * {DOMElement} Div for Google's copyright and terms of use link │ │ │ │ + * APIProperty: filter │ │ │ │ + * {<OpenLayers.Filter>} Filter for limiting features sent to the layer. │ │ │ │ + * Use the <setFilter> method to update this filter after construction. │ │ │ │ */ │ │ │ │ - termsOfUse: null, │ │ │ │ + filter: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: poweredBy │ │ │ │ - * {DOMElement} Div for Google's powered by logo and link │ │ │ │ + * Property: cache │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} List of currently cached │ │ │ │ + * features. │ │ │ │ */ │ │ │ │ - poweredBy: null, │ │ │ │ + cache: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: dragObject │ │ │ │ - * {GDraggableObject} Since 2.93, Google has exposed the ability to get │ │ │ │ - * the maps GDraggableObject. We can now use this for smooth panning │ │ │ │ + * Property: caching │ │ │ │ + * {Boolean} The filter is currently caching features. │ │ │ │ */ │ │ │ │ - dragObject: null, │ │ │ │ + caching: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: loadMapObject │ │ │ │ - * Load the GMap and register appropriate event listeners. If we can't │ │ │ │ - * load GMap2, then display a warning message. │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Strategy.Filter │ │ │ │ + * Create a new filter strategy. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ - loadMapObject: function() { │ │ │ │ - if (!this.type) { │ │ │ │ - this.type = G_NORMAL_MAP; │ │ │ │ - } │ │ │ │ - var mapObject, termsOfUse, poweredBy; │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - // there are already Google layers added to this map │ │ │ │ - mapObject = cache.mapObject; │ │ │ │ - termsOfUse = cache.termsOfUse; │ │ │ │ - poweredBy = cache.poweredBy; │ │ │ │ - // increment the layer count │ │ │ │ - ++cache.count; │ │ │ │ - } else { │ │ │ │ - // this is the first Google layer for this map │ │ │ │ - │ │ │ │ - var container = this.map.viewPortDiv; │ │ │ │ - var div = document.createElement("div"); │ │ │ │ - div.id = this.map.id + "_GMap2Container"; │ │ │ │ - div.style.position = "absolute"; │ │ │ │ - div.style.width = "100%"; │ │ │ │ - div.style.height = "100%"; │ │ │ │ - container.appendChild(div); │ │ │ │ - │ │ │ │ - // create GMap and shuffle elements │ │ │ │ - try { │ │ │ │ - mapObject = new GMap2(div); │ │ │ │ - │ │ │ │ - // move the ToS and branding stuff up to the container div │ │ │ │ - termsOfUse = div.lastChild; │ │ │ │ - container.appendChild(termsOfUse); │ │ │ │ - termsOfUse.style.zIndex = "1100"; │ │ │ │ - termsOfUse.style.right = ""; │ │ │ │ - termsOfUse.style.bottom = ""; │ │ │ │ - termsOfUse.className = "olLayerGoogleCopyright"; │ │ │ │ - │ │ │ │ - poweredBy = div.lastChild; │ │ │ │ - container.appendChild(poweredBy); │ │ │ │ - poweredBy.style.zIndex = "1100"; │ │ │ │ - poweredBy.style.right = ""; │ │ │ │ - poweredBy.style.bottom = ""; │ │ │ │ - poweredBy.className = "olLayerGooglePoweredBy gmnoprint"; │ │ │ │ - │ │ │ │ - } catch (e) { │ │ │ │ - throw (e); │ │ │ │ - } │ │ │ │ - // cache elements for use by any other google layers added to │ │ │ │ - // this same map │ │ │ │ - OpenLayers.Layer.Google.cache[this.map.id] = { │ │ │ │ - mapObject: mapObject, │ │ │ │ - termsOfUse: termsOfUse, │ │ │ │ - poweredBy: poweredBy, │ │ │ │ - count: 1 │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.mapObject = mapObject; │ │ │ │ - this.termsOfUse = termsOfUse; │ │ │ │ - this.poweredBy = poweredBy; │ │ │ │ - │ │ │ │ - // ensure this layer type is one of the mapObject types │ │ │ │ - if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), │ │ │ │ - this.type) === -1) { │ │ │ │ - this.mapObject.addMapType(this.type); │ │ │ │ - } │ │ │ │ - │ │ │ │ - //since v 2.93 getDragObject is now available. │ │ │ │ - if (typeof mapObject.getDragObject == "function") { │ │ │ │ - this.dragObject = mapObject.getDragObject(); │ │ │ │ - } else { │ │ │ │ - this.dragPanMapObject = null; │ │ │ │ - } │ │ │ │ │ │ │ │ - if (this.isBaseLayer === false) { │ │ │ │ - this.setGMapVisibility(this.div.style.display !== "none"); │ │ │ │ + /** │ │ │ │ + * APIMethod: activate │ │ │ │ + * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ + * By default, this strategy automatically activates itself when a layer │ │ │ │ + * is added to a map. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} True if the strategy was successfully activated or false if │ │ │ │ + * the strategy was already active. │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); │ │ │ │ + if (activated) { │ │ │ │ + this.cache = []; │ │ │ │ + this.layer.events.on({ │ │ │ │ + "beforefeaturesadded": this.handleAdd, │ │ │ │ + "beforefeaturesremoved": this.handleRemove, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ - │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: onMapResize │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivate the strategy. Clear the feature cache. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} True if the strategy was successfully deactivated or false if │ │ │ │ + * the strategy was already inactive. │ │ │ │ */ │ │ │ │ - onMapResize: function() { │ │ │ │ - // workaround for resizing of invisible or not yet fully loaded layers │ │ │ │ - // where GMap2.checkResize() does not work. We need to load the GMap │ │ │ │ - // for the old div size, then checkResize(), and then call │ │ │ │ - // layer.moveTo() to trigger GMap.setCenter() (which will finish │ │ │ │ - // the GMap initialization). │ │ │ │ - if (this.visibility && this.mapObject.isLoaded()) { │ │ │ │ - this.mapObject.checkResize(); │ │ │ │ - } else { │ │ │ │ - if (!this._resized) { │ │ │ │ - var layer = this; │ │ │ │ - var handle = GEvent.addListener(this.mapObject, "load", function() { │ │ │ │ - GEvent.removeListener(handle); │ │ │ │ - delete layer._resized; │ │ │ │ - layer.mapObject.checkResize(); │ │ │ │ - layer.moveTo(layer.map.getCenter(), layer.map.getZoom()); │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - this._resized = true; │ │ │ │ + deactivate: function() { │ │ │ │ + this.cache = null; │ │ │ │ + if (this.layer && this.layer.events) { │ │ │ │ + this.layer.events.un({ │ │ │ │ + "beforefeaturesadded": this.handleAdd, │ │ │ │ + "beforefeaturesremoved": this.handleRemove, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ + return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setGMapVisibility │ │ │ │ - * Display the GMap container and associated elements. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * visible - {Boolean} Display the GMap elements. │ │ │ │ + * Method: handleAdd │ │ │ │ */ │ │ │ │ - setGMapVisibility: function(visible) { │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - var container = this.mapObject.getContainer(); │ │ │ │ - if (visible === true) { │ │ │ │ - this.mapObject.setMapType(this.type); │ │ │ │ - container.style.display = ""; │ │ │ │ - this.termsOfUse.style.left = ""; │ │ │ │ - this.termsOfUse.style.display = ""; │ │ │ │ - this.poweredBy.style.display = ""; │ │ │ │ - cache.displayed = this.id; │ │ │ │ - } else { │ │ │ │ - if (cache.displayed === this.id) { │ │ │ │ - delete cache.displayed; │ │ │ │ - } │ │ │ │ - if (!cache.displayed) { │ │ │ │ - container.style.display = "none"; │ │ │ │ - this.termsOfUse.style.display = "none"; │ │ │ │ - // move ToU far to the left in addition to setting display │ │ │ │ - // to "none", because at the end of the GMap2 load │ │ │ │ - // sequence, display: none will be unset and ToU would be │ │ │ │ - // visible after loading a map with a google layer that is │ │ │ │ - // initially hidden. │ │ │ │ - this.termsOfUse.style.left = "-9999px"; │ │ │ │ - this.poweredBy.style.display = "none"; │ │ │ │ + handleAdd: function(event) { │ │ │ │ + if (!this.caching && this.filter) { │ │ │ │ + var features = event.features; │ │ │ │ + event.features = []; │ │ │ │ + var feature; │ │ │ │ + for (var i = 0, ii = features.length; i < ii; ++i) { │ │ │ │ + feature = features[i]; │ │ │ │ + if (this.filter.evaluate(feature)) { │ │ │ │ + event.features.push(feature); │ │ │ │ + } else { │ │ │ │ + this.cache.push(feature); │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getMapContainer │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} the GMap container's div │ │ │ │ + * Method: handleRemove │ │ │ │ */ │ │ │ │ - getMapContainer: function() { │ │ │ │ - return this.mapObject.getContainer(); │ │ │ │ + handleRemove: function(event) { │ │ │ │ + if (!this.caching) { │ │ │ │ + this.cache = []; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - // │ │ │ │ - // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds │ │ │ │ - // │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectBoundsFromOLBounds │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * APIMethod: setFilter │ │ │ │ + * Update the filter for this strategy. This will re-evaluate │ │ │ │ + * any features on the layer and in the cache. Only features │ │ │ │ + * for which filter.evalute(feature) returns true will be │ │ │ │ + * added to the layer. Others will be cached by the strategy. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * olBounds - {<OpenLayers.Bounds>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} A MapObject Bounds, translated from olBounds │ │ │ │ - * Returns null if null value is passed in │ │ │ │ + * filter - {<OpenLayers.Filter>} A filter for evaluating features. │ │ │ │ */ │ │ │ │ - getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ - var moBounds = null; │ │ │ │ - if (olBounds != null) { │ │ │ │ - var sw = this.sphericalMercator ? │ │ │ │ - this.inverseMercator(olBounds.bottom, olBounds.left) : │ │ │ │ - new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ - var ne = this.sphericalMercator ? │ │ │ │ - this.inverseMercator(olBounds.top, olBounds.right) : │ │ │ │ - new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ - moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), │ │ │ │ - new GLatLng(ne.lat, ne.lon)); │ │ │ │ + setFilter: function(filter) { │ │ │ │ + this.filter = filter; │ │ │ │ + var previousCache = this.cache; │ │ │ │ + this.cache = []; │ │ │ │ + // look through layer for features to remove from layer │ │ │ │ + this.handleAdd({ │ │ │ │ + features: this.layer.features │ │ │ │ + }); │ │ │ │ + // cache now contains features to remove from layer │ │ │ │ + if (this.cache.length > 0) { │ │ │ │ + this.caching = true; │ │ │ │ + this.layer.removeFeatures(this.cache.slice()); │ │ │ │ + this.caching = false; │ │ │ │ + } │ │ │ │ + // now look through previous cache for features to add to layer │ │ │ │ + if (previousCache.length > 0) { │ │ │ │ + var event = { │ │ │ │ + features: previousCache │ │ │ │ + }; │ │ │ │ + this.handleAdd(event); │ │ │ │ + if (event.features.length > 0) { │ │ │ │ + // event has features to add to layer │ │ │ │ + this.caching = true; │ │ │ │ + this.layer.addFeatures(event.features); │ │ │ │ + this.caching = false; │ │ │ │ + } │ │ │ │ } │ │ │ │ - return moBounds; │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Filter" │ │ │ │ │ │ │ │ - /************************************ │ │ │ │ - * * │ │ │ │ - * MapObject Interface Controls * │ │ │ │ - * * │ │ │ │ - ************************************/ │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Strategy/Save.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - // Get&Set Center, Zoom │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Strategy.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: setMapObjectCenter │ │ │ │ - * Set the mapObject to the specified center and zoom │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * center - {Object} MapObject LonLat format │ │ │ │ - * zoom - {int} MapObject zoom format │ │ │ │ - */ │ │ │ │ - setMapObjectCenter: function(center, zoom) { │ │ │ │ - this.mapObject.setCenter(center, zoom); │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Strategy.Save │ │ │ │ + * A strategy that commits newly created or modified features. By default │ │ │ │ + * the strategy waits for a call to <save> before persisting changes. By │ │ │ │ + * configuring the strategy with the <auto> option, changes can be saved │ │ │ │ + * automatically. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Strategy> │ │ │ │ + */ │ │ │ │ +OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: dragPanMapObject │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} An events object that handles all │ │ │ │ + * events on the strategy object. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * strategy.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types: │ │ │ │ + * start - Triggered before saving │ │ │ │ + * success - Triggered after a successful transaction │ │ │ │ + * fail - Triggered after a failed transaction │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * dX - {Integer} │ │ │ │ - * dY - {Integer} │ │ │ │ */ │ │ │ │ - dragPanMapObject: function(dX, dY) { │ │ │ │ - this.dragObject.moveBy(new GSize(-dX, dY)); │ │ │ │ - }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Property: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for triggering this protocol │ │ │ │ + * events. │ │ │ │ + */ │ │ │ │ + events: null, │ │ │ │ │ │ │ │ - // LonLat - Pixel Translation │ │ │ │ + /** │ │ │ │ + * APIProperty: auto │ │ │ │ + * {Boolean | Number} Auto-save. Default is false. If true, features will be │ │ │ │ + * saved immediately after being added to the layer and with each │ │ │ │ + * modification or deletion. If auto is a number, features will be │ │ │ │ + * saved on an interval provided by the value (in seconds). │ │ │ │ + */ │ │ │ │ + auto: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getMapObjectLonLatFromMapObjectPixel │ │ │ │ - * │ │ │ │ + * Property: timer │ │ │ │ + * {Number} The id of the timer. │ │ │ │ + */ │ │ │ │ + timer: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Strategy.Save │ │ │ │ + * Create a new Save strategy. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * moPixel - {Object} MapObject Pixel format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} MapObject LonLat translated from MapObject Pixel │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ - getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ - return this.mapObject.fromContainerPixelToLatLng(moPixel); │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Strategy.prototype.initialize.apply(this, [options]); │ │ │ │ + this.events = new OpenLayers.Events(this); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getMapObjectPixelFromMapObjectLonLat │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moLonLat - {Object} MapObject LonLat format │ │ │ │ + * APIMethod: activate │ │ │ │ + * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} MapObject Pixel transtlated from MapObject LonLat │ │ │ │ + * {Boolean} The strategy was successfully activated. │ │ │ │ */ │ │ │ │ - getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - return this.mapObject.fromLatLngToContainerPixel(moLonLat); │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + if (this.auto) { │ │ │ │ + if (typeof this.auto === "number") { │ │ │ │ + this.timer = window.setInterval( │ │ │ │ + OpenLayers.Function.bind(this.save, this), │ │ │ │ + this.auto * 1000 │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + this.layer.events.on({ │ │ │ │ + "featureadded": this.triggerSave, │ │ │ │ + "afterfeaturemodified": this.triggerSave, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ - // Bounds │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectZoomFromMapObjectBounds │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moBounds - {Object} MapObject Bounds format │ │ │ │ + /** │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ + * tear-down. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} MapObject Zoom for specified MapObject Bounds │ │ │ │ + * {Boolean} The strategy was successfully deactivated. │ │ │ │ */ │ │ │ │ - getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ - return this.mapObject.getBoundsZoomLevel(moBounds); │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + if (this.auto) { │ │ │ │ + if (typeof this.auto === "number") { │ │ │ │ + window.clearInterval(this.timer); │ │ │ │ + } else { │ │ │ │ + this.layer.events.un({ │ │ │ │ + "featureadded": this.triggerSave, │ │ │ │ + "afterfeaturemodified": this.triggerSave, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ - /************************************ │ │ │ │ - * * │ │ │ │ - * MapObject Primitives * │ │ │ │ - * * │ │ │ │ - ************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // LonLat │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIMethod: getMapObjectLonLatFromLonLat │ │ │ │ - * │ │ │ │ + * Method: triggerSave │ │ │ │ + * Registered as a listener. Calls save if a feature has insert, update, │ │ │ │ + * or delete state. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * lon - {Float} │ │ │ │ - * lat - {Float} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} MapObject LonLat built from lon and lat params │ │ │ │ + * event - {Object} The event this function is listening for. │ │ │ │ */ │ │ │ │ - getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ - var gLatLng; │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - var lonlat = this.inverseMercator(lon, lat); │ │ │ │ - gLatLng = new GLatLng(lonlat.lat, lonlat.lon); │ │ │ │ - } else { │ │ │ │ - gLatLng = new GLatLng(lat, lon); │ │ │ │ + triggerSave: function(event) { │ │ │ │ + var feature = event.feature; │ │ │ │ + if (feature.state === OpenLayers.State.INSERT || │ │ │ │ + feature.state === OpenLayers.State.UPDATE || │ │ │ │ + feature.state === OpenLayers.State.DELETE) { │ │ │ │ + this.save([event.feature]); │ │ │ │ } │ │ │ │ - return gLatLng; │ │ │ │ }, │ │ │ │ │ │ │ │ - // Pixel │ │ │ │ + /** │ │ │ │ + * APIMethod: save │ │ │ │ + * Tell the layer protocol to commit unsaved features. If the layer │ │ │ │ + * projection differs from the map projection, features will be │ │ │ │ + * transformed into the layer projection before being committed. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array} Features to be saved. If null, then default is all │ │ │ │ + * features in the layer. Features are assumed to be in the map │ │ │ │ + * projection. │ │ │ │ + */ │ │ │ │ + save: function(features) { │ │ │ │ + if (!features) { │ │ │ │ + features = this.layer.features; │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("start", { │ │ │ │ + features: features │ │ │ │ + }); │ │ │ │ + var remote = this.layer.projection; │ │ │ │ + var local = this.layer.map.getProjectionObject(); │ │ │ │ + if (!local.equals(remote)) { │ │ │ │ + var len = features.length; │ │ │ │ + var clones = new Array(len); │ │ │ │ + var orig, clone; │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ + orig = features[i]; │ │ │ │ + clone = orig.clone(); │ │ │ │ + clone.fid = orig.fid; │ │ │ │ + clone.state = orig.state; │ │ │ │ + if (orig.url) { │ │ │ │ + clone.url = orig.url; │ │ │ │ + } │ │ │ │ + clone._original = orig; │ │ │ │ + clone.geometry.transform(local, remote); │ │ │ │ + clones[i] = clone; │ │ │ │ + } │ │ │ │ + features = clones; │ │ │ │ + } │ │ │ │ + this.layer.protocol.commit(features, { │ │ │ │ + callback: this.onCommit, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getMapObjectPixelFromXY │ │ │ │ - * │ │ │ │ + * Method: onCommit │ │ │ │ + * Called after protocol commit. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * x - {Integer} │ │ │ │ - * y - {Integer} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} MapObject Pixel from x and y parameters │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} A response object. │ │ │ │ */ │ │ │ │ - getMapObjectPixelFromXY: function(x, y) { │ │ │ │ - return new GPoint(x, y); │ │ │ │ - } │ │ │ │ + onCommit: function(response) { │ │ │ │ + var evt = { │ │ │ │ + "response": response │ │ │ │ + }; │ │ │ │ + if (response.success()) { │ │ │ │ + var features = response.reqFeatures; │ │ │ │ + // deal with inserts, updates, and deletes │ │ │ │ + var state, feature; │ │ │ │ + var destroys = []; │ │ │ │ + var insertIds = response.insertIds || []; │ │ │ │ + var j = 0; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + feature = features[i]; │ │ │ │ + // if projection was different, we may be dealing with clones │ │ │ │ + feature = feature._original || feature; │ │ │ │ + state = feature.state; │ │ │ │ + if (state) { │ │ │ │ + if (state == OpenLayers.State.DELETE) { │ │ │ │ + destroys.push(feature); │ │ │ │ + } else if (state == OpenLayers.State.INSERT) { │ │ │ │ + feature.fid = insertIds[j]; │ │ │ │ + ++j; │ │ │ │ + } │ │ │ │ + feature.state = null; │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ -}; │ │ │ │ + if (destroys.length > 0) { │ │ │ │ + this.layer.destroyFeatures(destroys); │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.events.triggerEvent("success", evt); │ │ │ │ + │ │ │ │ + } else { │ │ │ │ + this.events.triggerEvent("fail", evt); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Save" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/WorldWind.js │ │ │ │ + OpenLayers/Strategy/BBOX.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Layer/Grid.js │ │ │ │ + * @requires OpenLayers/Strategy.js │ │ │ │ + * @requires OpenLayers/Filter/Spatial.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.WorldWind │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Strategy.BBOX │ │ │ │ + * A simple strategy that reads new features when the viewport invalidates │ │ │ │ + * some bounds. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ + * - <OpenLayers.Strategy> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ +OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ │ │ │ │ - DEFAULT_PARAMS: {}, │ │ │ │ + /** │ │ │ │ + * Property: bounds │ │ │ │ + * {<OpenLayers.Bounds>} The current data bounds (in the same projection │ │ │ │ + * as the layer - not always the same projection as the map). │ │ │ │ + */ │ │ │ │ + bounds: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: resolution │ │ │ │ + * {Float} The current data resolution. │ │ │ │ + */ │ │ │ │ + resolution: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} WorldWind layer is a base layer by default. │ │ │ │ + * APIProperty: ratio │ │ │ │ + * {Float} The ratio of the data bounds to the viewport bounds (in each │ │ │ │ + * dimension). Default is 2. │ │ │ │ */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + ratio: 2, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: lzd │ │ │ │ - * {Float} LevelZeroTileSizeDegrees │ │ │ │ + * Property: resFactor │ │ │ │ + * {Float} Optional factor used to determine when previously requested │ │ │ │ + * features are invalid. If set, the resFactor will be compared to the │ │ │ │ + * resolution of the previous request to the current map resolution. │ │ │ │ + * If resFactor > (old / new) and 1/resFactor < (old / new). If you │ │ │ │ + * set a resFactor of 1, data will be requested every time the │ │ │ │ + * resolution changes. If you set a resFactor of 3, data will be │ │ │ │ + * requested if the old resolution is 3 times the new, or if the new is │ │ │ │ + * 3 times the old. If the old bounds do not contain the new bounds │ │ │ │ + * new data will always be requested (with or without considering │ │ │ │ + * resFactor). │ │ │ │ */ │ │ │ │ - lzd: null, │ │ │ │ + resFactor: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: zoomLevels │ │ │ │ - * {Integer} Number of zoom levels. │ │ │ │ + * Property: response │ │ │ │ + * {<OpenLayers.Protocol.Response>} The protocol response object returned │ │ │ │ + * by the layer protocol. │ │ │ │ */ │ │ │ │ - zoomLevels: null, │ │ │ │ + response: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.WorldWind │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Strategy.BBOX │ │ │ │ + * Create a new BBOX strategy. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} Name of Layer │ │ │ │ - * url - {String} Base URL │ │ │ │ - * lzd - {Float} Level zero tile size degrees │ │ │ │ - * zoomLevels - {Integer} number of zoom levels │ │ │ │ - * params - {Object} additional parameters │ │ │ │ - * options - {Object} additional options │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ - initialize: function(name, url, lzd, zoomLevels, params, options) { │ │ │ │ - this.lzd = lzd; │ │ │ │ - this.zoomLevels = zoomLevels; │ │ │ │ - var newArguments = []; │ │ │ │ - newArguments.push(name, url, params, options); │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ - this.params = OpenLayers.Util.applyDefaults( │ │ │ │ - this.params, this.DEFAULT_PARAMS │ │ │ │ - ); │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: activate │ │ │ │ + * Set up strategy with regard to reading new batches of remote data. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The strategy was successfully activated. │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + "moveend": this.update, │ │ │ │ + "refresh": this.update, │ │ │ │ + "visibilitychanged": this.update, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.update(); │ │ │ │ + } │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getZoom │ │ │ │ - * Convert map zoom to WW zoom. │ │ │ │ + * Method: deactivate │ │ │ │ + * Tear down strategy with regard to reading new batches of remote data. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The strategy was successfully deactivated. │ │ │ │ */ │ │ │ │ - getZoom: function() { │ │ │ │ - var zoom = this.map.getZoom(); │ │ │ │ - var extent = this.map.getMaxExtent(); │ │ │ │ - zoom = zoom - Math.log(this.maxResolution / (this.lzd / 512)) / Math.log(2); │ │ │ │ - return zoom; │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.layer.events.un({ │ │ │ │ + "moveend": this.update, │ │ │ │ + "refresh": this.update, │ │ │ │ + "visibilitychanged": this.update, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ + * Method: update │ │ │ │ + * Callback function called on "moveend" or "refresh" layer events. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * options - {Object} Optional object whose properties will determine │ │ │ │ + * the behaviour of this Strategy │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {String} A string with the layer's url and parameters and also the │ │ │ │ - * passed-in bounds and appropriate tile size specified as │ │ │ │ - * parameters │ │ │ │ + * Valid options include: │ │ │ │ + * force - {Boolean} if true, new data must be unconditionally read. │ │ │ │ + * noAbort - {Boolean} if true, do not abort previous requests. │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var zoom = this.getZoom(); │ │ │ │ - var extent = this.map.getMaxExtent(); │ │ │ │ - var deg = this.lzd / Math.pow(2, this.getZoom()); │ │ │ │ - var x = Math.floor((bounds.left - extent.left) / deg); │ │ │ │ - var y = Math.floor((bounds.bottom - extent.bottom) / deg); │ │ │ │ - if (this.map.getResolution() <= (this.lzd / 512) && │ │ │ │ - this.getZoom() <= this.zoomLevels) { │ │ │ │ - return this.getFullRequestString({ │ │ │ │ - L: zoom, │ │ │ │ - X: x, │ │ │ │ - Y: y │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - return OpenLayers.Util.getImageLocation("blank.gif"); │ │ │ │ + update: function(options) { │ │ │ │ + var mapBounds = this.getMapBounds(); │ │ │ │ + if (mapBounds !== null && ((options && options.force) || │ │ │ │ + (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) { │ │ │ │ + this.calculateBounds(mapBounds); │ │ │ │ + this.resolution = this.layer.map.getResolution(); │ │ │ │ + this.triggerRead(options); │ │ │ │ } │ │ │ │ - │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.WorldWind" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/TileCache.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/Grid.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.TileCache │ │ │ │ - * A read only TileCache layer. Used to requests tiles cached by TileCache in │ │ │ │ - * a web accessible cache. This means that you have to pre-populate your │ │ │ │ - * cache before this layer can be used. It is meant only to read tiles │ │ │ │ - * created by TileCache, and not to make calls to TileCache for tile │ │ │ │ - * creation. Create a new instance with the │ │ │ │ - * <OpenLayers.Layer.TileCache> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} Treat this layer as a base layer. Default is true. │ │ │ │ + /** │ │ │ │ + * Method: getMapBounds │ │ │ │ + * Get the map bounds expressed in the same projection as this layer. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} Map bounds in the projection of the layer. │ │ │ │ */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + getMapBounds: function() { │ │ │ │ + if (this.layer.map === null) { │ │ │ │ + return null; │ │ │ │ + } │ │ │ │ + var bounds = this.layer.map.getExtent(); │ │ │ │ + if (bounds && !this.layer.projection.equals( │ │ │ │ + this.layer.map.getProjectionObject())) { │ │ │ │ + bounds = bounds.clone().transform( │ │ │ │ + this.layer.map.getProjectionObject(), this.layer.projection │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return bounds; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: format │ │ │ │ - * {String} Mime type of the images returned. Default is image/png. │ │ │ │ + /** │ │ │ │ + * Method: invalidBounds │ │ │ │ + * Determine whether the previously requested set of features is invalid. │ │ │ │ + * This occurs when the new map bounds do not contain the previously │ │ │ │ + * requested bounds. In addition, if <resFactor> is set, it will be │ │ │ │ + * considered. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be │ │ │ │ + * retrieved from the map object if not provided │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - format: 'image/png', │ │ │ │ + invalidBounds: function(mapBounds) { │ │ │ │ + if (!mapBounds) { │ │ │ │ + mapBounds = this.getMapBounds(); │ │ │ │ + } │ │ │ │ + var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds); │ │ │ │ + if (!invalid && this.resFactor) { │ │ │ │ + var ratio = this.resolution / this.layer.map.getResolution(); │ │ │ │ + invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor)); │ │ │ │ + } │ │ │ │ + return invalid; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: serverResolutions │ │ │ │ - * {Array} A list of all resolutions available on the server. Only set this │ │ │ │ - * property if the map resolutions differ from the server. This │ │ │ │ - * property serves two purposes. (a) <serverResolutions> can include │ │ │ │ - * resolutions that the server supports and that you don't want to │ │ │ │ - * provide with this layer. (b) The map can work with resolutions │ │ │ │ - * that aren't supported by the server, i.e. that aren't in │ │ │ │ - * <serverResolutions>. When the map is displayed in such a resolution │ │ │ │ - * data for the closest server-supported resolution is loaded and the │ │ │ │ - * layer div is stretched as necessary. │ │ │ │ + * Method: calculateBounds │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be │ │ │ │ + * retrieved from the map object if not provided │ │ │ │ */ │ │ │ │ - serverResolutions: null, │ │ │ │ + calculateBounds: function(mapBounds) { │ │ │ │ + if (!mapBounds) { │ │ │ │ + mapBounds = this.getMapBounds(); │ │ │ │ + } │ │ │ │ + var center = mapBounds.getCenterLonLat(); │ │ │ │ + var dataWidth = mapBounds.getWidth() * this.ratio; │ │ │ │ + var dataHeight = mapBounds.getHeight() * this.ratio; │ │ │ │ + this.bounds = new OpenLayers.Bounds( │ │ │ │ + center.lon - (dataWidth / 2), │ │ │ │ + center.lat - (dataHeight / 2), │ │ │ │ + center.lon + (dataWidth / 2), │ │ │ │ + center.lat + (dataHeight / 2) │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.TileCache │ │ │ │ - * Create a new read only TileCache layer. │ │ │ │ + * Method: triggerRead │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} Name of the layer displayed in the interface │ │ │ │ - * url - {String} Location of the web accessible cache (not the location of │ │ │ │ - * your tilecache script!) │ │ │ │ - * layername - {String} Layer name as defined in the TileCache │ │ │ │ - * configuration │ │ │ │ - * options - {Object} Optional object with properties to be set on the │ │ │ │ - * layer. Note that you should speficy your resolutions to match │ │ │ │ - * your TileCache configuration. This can be done by setting │ │ │ │ - * the resolutions array directly (here or on the map), by setting │ │ │ │ - * maxResolution and numZoomLevels, or by using scale based properties. │ │ │ │ + * options - {Object} Additional options for the protocol's read method │ │ │ │ + * (optional) │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.Response>} The protocol response object │ │ │ │ + * returned by the layer protocol. │ │ │ │ */ │ │ │ │ - initialize: function(name, url, layername, options) { │ │ │ │ - this.layername = layername; │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, │ │ │ │ - [name, url, {}, options]); │ │ │ │ - this.extension = this.format.split('/')[1].toLowerCase(); │ │ │ │ - this.extension = (this.extension == 'jpg') ? 'jpeg' : this.extension; │ │ │ │ + triggerRead: function(options) { │ │ │ │ + if (this.response && !(options && options.noAbort === true)) { │ │ │ │ + this.layer.protocol.abort(this.response); │ │ │ │ + this.layer.events.triggerEvent("loadend"); │ │ │ │ + } │ │ │ │ + var evt = { │ │ │ │ + filter: this.createFilter() │ │ │ │ + }; │ │ │ │ + this.layer.events.triggerEvent("loadstart", evt); │ │ │ │ + this.response = this.layer.protocol.read( │ │ │ │ + OpenLayers.Util.applyDefaults({ │ │ │ │ + filter: evt.filter, │ │ │ │ + callback: this.merge, │ │ │ │ + scope: this │ │ │ │ + }, options)); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * obj - {Object} │ │ │ │ + * Method: createFilter │ │ │ │ + * Creates a spatial BBOX filter. If the layer that this strategy belongs │ │ │ │ + * to has a filter property, this filter will be combined with the BBOX │ │ │ │ + * filter. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.TileCache>} An exact clone of this │ │ │ │ - * <OpenLayers.Layer.TileCache> │ │ │ │ + * Returns │ │ │ │ + * {<OpenLayers.Filter>} The filter object. │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.TileCache(this.name, │ │ │ │ - this.url, │ │ │ │ - this.layername, │ │ │ │ - this.getOptions()); │ │ │ │ + createFilter: function() { │ │ │ │ + var filter = new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ + value: this.bounds, │ │ │ │ + projection: this.layer.projection │ │ │ │ + }); │ │ │ │ + if (this.layer.filter) { │ │ │ │ + filter = new OpenLayers.Filter.Logical({ │ │ │ │ + type: OpenLayers.Filter.Logical.AND, │ │ │ │ + filters: [this.layer.filter, filter] │ │ │ │ + }); │ │ │ │ } │ │ │ │ - │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - │ │ │ │ - return obj; │ │ │ │ + return filter; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ + * Method: merge │ │ │ │ + * Given a list of features, determine which ones to add to the layer. │ │ │ │ + * If the layer projection differs from the map projection, features │ │ │ │ + * will be transformed from the layer projection to the map projection. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A string with the layer's url and parameters and also the │ │ │ │ - * passed-in bounds and appropriate tile size specified as parameters. │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object passed │ │ │ │ + * by the protocol. │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var bbox = this.maxExtent; │ │ │ │ - var size = this.tileSize; │ │ │ │ - var tileX = Math.round((bounds.left - bbox.left) / (res * size.w)); │ │ │ │ - var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h)); │ │ │ │ - var tileZ = this.serverResolutions != null ? │ │ │ │ - OpenLayers.Util.indexOf(this.serverResolutions, res) : │ │ │ │ - this.map.getZoom(); │ │ │ │ - │ │ │ │ - var components = [ │ │ │ │ - this.layername, │ │ │ │ - OpenLayers.Number.zeroPad(tileZ, 2), │ │ │ │ - OpenLayers.Number.zeroPad(parseInt(tileX / 1000000), 3), │ │ │ │ - OpenLayers.Number.zeroPad((parseInt(tileX / 1000) % 1000), 3), │ │ │ │ - OpenLayers.Number.zeroPad((parseInt(tileX) % 1000), 3), │ │ │ │ - OpenLayers.Number.zeroPad(parseInt(tileY / 1000000), 3), │ │ │ │ - OpenLayers.Number.zeroPad((parseInt(tileY / 1000) % 1000), 3), │ │ │ │ - OpenLayers.Number.zeroPad((parseInt(tileY) % 1000), 3) + '.' + this.extension │ │ │ │ - ]; │ │ │ │ - var path = components.join('/'); │ │ │ │ - var url = this.url; │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(path, url); │ │ │ │ + merge: function(resp) { │ │ │ │ + this.layer.destroyFeatures(); │ │ │ │ + if (resp.success()) { │ │ │ │ + var features = resp.features; │ │ │ │ + if (features && features.length > 0) { │ │ │ │ + var remote = this.layer.projection; │ │ │ │ + var local = this.layer.map.getProjectionObject(); │ │ │ │ + if (!local.equals(remote)) { │ │ │ │ + var geom; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + geom = features[i].geometry; │ │ │ │ + if (geom) { │ │ │ │ + geom.transform(remote, local); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.layer.addFeatures(features); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.bounds = null; │ │ │ │ } │ │ │ │ - url = (url.charAt(url.length - 1) == '/') ? url : url + '/'; │ │ │ │ - return url + path; │ │ │ │ + this.response = null; │ │ │ │ + this.layer.events.triggerEvent("loadend", { │ │ │ │ + response: resp │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.TileCache" │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.BBOX" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/Text.js │ │ │ │ + OpenLayers/Strategy/Cluster.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ - * @requires OpenLayers/Geometry/Point.js │ │ │ │ + * @requires OpenLayers/Strategy.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.Text │ │ │ │ - * Read Text format. Create a new instance with the <OpenLayers.Format.Text> │ │ │ │ - * constructor. This reads text which is formatted like CSV text, using │ │ │ │ - * tabs as the seperator by default. It provides parsing of data originally │ │ │ │ - * used in the MapViewerService, described on the wiki. This Format is used │ │ │ │ - * by the <OpenLayers.Layer.Text> class. │ │ │ │ + * Class: OpenLayers.Strategy.Cluster │ │ │ │ + * Strategy for vector feature clustering. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format> │ │ │ │ + * - <OpenLayers.Strategy> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, { │ │ │ │ +OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: defaultStyle │ │ │ │ - * defaultStyle allows one to control the default styling of the features. │ │ │ │ - * It should be a symbolizer hash. By default, this is set to match the │ │ │ │ - * Layer.Text behavior, which is to use the default OpenLayers Icon. │ │ │ │ + * APIProperty: distance │ │ │ │ + * {Integer} Pixel distance between features that should be considered a │ │ │ │ + * single cluster. Default is 20 pixels. │ │ │ │ */ │ │ │ │ - defaultStyle: null, │ │ │ │ + distance: 20, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: extractStyles │ │ │ │ - * set to true to extract styles from the TSV files, using information │ │ │ │ - * from the image or icon, iconSize and iconOffset fields. This will result │ │ │ │ - * in features with a symbolizer (style) property set, using the │ │ │ │ - * default symbolizer specified in <defaultStyle>. Set to false if you │ │ │ │ - * wish to use a styleMap or OpenLayers.Style options to style your │ │ │ │ - * layer instead. │ │ │ │ + * APIProperty: threshold │ │ │ │ + * {Integer} Optional threshold below which original features will be │ │ │ │ + * added to the layer instead of clusters. For example, a threshold │ │ │ │ + * of 3 would mean that any time there are 2 or fewer features in │ │ │ │ + * a cluster, those features will be added directly to the layer instead │ │ │ │ + * of a cluster representing those features. Default is null (which is │ │ │ │ + * equivalent to 1 - meaning that clusters may contain just one feature). │ │ │ │ */ │ │ │ │ - extractStyles: true, │ │ │ │ + threshold: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.Text │ │ │ │ - * Create a new parser for TSV Text. │ │ │ │ + * Property: features │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} Cached features. │ │ │ │ + */ │ │ │ │ + features: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: clusters │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} Calculated clusters. │ │ │ │ + */ │ │ │ │ + clusters: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: clustering │ │ │ │ + * {Boolean} The strategy is currently clustering features. │ │ │ │ + */ │ │ │ │ + clustering: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: resolution │ │ │ │ + * {Float} The resolution (map units per pixel) of the current cluster set. │ │ │ │ + */ │ │ │ │ + resolution: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Strategy.Cluster │ │ │ │ + * Create a new clustering strategy. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ │ │ │ │ - if (options.extractStyles !== false) { │ │ │ │ - options.defaultStyle = { │ │ │ │ - 'externalGraphic': OpenLayers.Util.getImageLocation("marker.png"), │ │ │ │ - 'graphicWidth': 21, │ │ │ │ - 'graphicHeight': 25, │ │ │ │ - 'graphicXOffset': -10.5, │ │ │ │ - 'graphicYOffset': -12.5 │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * APIMethod: activate │ │ │ │ + * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The strategy was successfully activated. │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + "beforefeaturesadded": this.cacheFeatures, │ │ │ │ + "featuresremoved": this.clearCache, │ │ │ │ + "moveend": this.cluster, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ - │ │ │ │ - OpenLayers.Format.prototype.initialize.apply(this, [options]); │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Return a list of features from a Tab Seperated Values text string. │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ + * tear-down. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * text - {String} │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The strategy was successfully deactivated. │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.clearCache(); │ │ │ │ + this.layer.events.un({ │ │ │ │ + "beforefeaturesadded": this.cacheFeatures, │ │ │ │ + "featuresremoved": this.clearCache, │ │ │ │ + "moveend": this.cluster, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + return deactivated; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: cacheFeatures │ │ │ │ + * Cache features before they are added to the layer. │ │ │ │ * │ │ │ │ + * Parameters: │ │ │ │ + * event - {Object} The event that this was listening for. This will come │ │ │ │ + * with a batch of features to be clustered. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * Array({<OpenLayers.Feature.Vector>}) │ │ │ │ + * {Boolean} False to stop features from being added to the layer. │ │ │ │ */ │ │ │ │ - read: function(text) { │ │ │ │ - var lines = text.split('\n'); │ │ │ │ - var columns; │ │ │ │ - var features = []; │ │ │ │ - // length - 1 to allow for trailing new line │ │ │ │ - for (var lcv = 0; lcv < (lines.length - 1); lcv++) { │ │ │ │ - var currLine = lines[lcv].replace(/^\s*/, '').replace(/\s*$/, ''); │ │ │ │ + cacheFeatures: function(event) { │ │ │ │ + var propagate = true; │ │ │ │ + if (!this.clustering) { │ │ │ │ + this.clearCache(); │ │ │ │ + this.features = event.features; │ │ │ │ + this.cluster(); │ │ │ │ + propagate = false; │ │ │ │ + } │ │ │ │ + return propagate; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (currLine.charAt(0) != '#') { │ │ │ │ - /* not a comment */ │ │ │ │ + /** │ │ │ │ + * Method: clearCache │ │ │ │ + * Clear out the cached features. │ │ │ │ + */ │ │ │ │ + clearCache: function() { │ │ │ │ + if (!this.clustering) { │ │ │ │ + this.features = null; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (!columns) { │ │ │ │ - //First line is columns │ │ │ │ - columns = currLine.split('\t'); │ │ │ │ - } else { │ │ │ │ - var vals = currLine.split('\t'); │ │ │ │ - var geometry = new OpenLayers.Geometry.Point(0, 0); │ │ │ │ - var attributes = {}; │ │ │ │ - var style = this.defaultStyle ? │ │ │ │ - OpenLayers.Util.applyDefaults({}, this.defaultStyle) : │ │ │ │ - null; │ │ │ │ - var icon, iconSize, iconOffset, overflow; │ │ │ │ - var set = false; │ │ │ │ - for (var valIndex = 0; valIndex < vals.length; valIndex++) { │ │ │ │ - if (vals[valIndex]) { │ │ │ │ - if (columns[valIndex] == 'point') { │ │ │ │ - var coords = vals[valIndex].split(','); │ │ │ │ - geometry.y = parseFloat(coords[0]); │ │ │ │ - geometry.x = parseFloat(coords[1]); │ │ │ │ - set = true; │ │ │ │ - } else if (columns[valIndex] == 'lat') { │ │ │ │ - geometry.y = parseFloat(vals[valIndex]); │ │ │ │ - set = true; │ │ │ │ - } else if (columns[valIndex] == 'lon') { │ │ │ │ - geometry.x = parseFloat(vals[valIndex]); │ │ │ │ - set = true; │ │ │ │ - } else if (columns[valIndex] == 'title') │ │ │ │ - attributes['title'] = vals[valIndex]; │ │ │ │ - else if (columns[valIndex] == 'image' || │ │ │ │ - columns[valIndex] == 'icon' && style) { │ │ │ │ - style['externalGraphic'] = vals[valIndex]; │ │ │ │ - } else if (columns[valIndex] == 'iconSize' && style) { │ │ │ │ - var size = vals[valIndex].split(','); │ │ │ │ - style['graphicWidth'] = parseFloat(size[0]); │ │ │ │ - style['graphicHeight'] = parseFloat(size[1]); │ │ │ │ - } else if (columns[valIndex] == 'iconOffset' && style) { │ │ │ │ - var offset = vals[valIndex].split(','); │ │ │ │ - style['graphicXOffset'] = parseFloat(offset[0]); │ │ │ │ - style['graphicYOffset'] = parseFloat(offset[1]); │ │ │ │ - } else if (columns[valIndex] == 'description') { │ │ │ │ - attributes['description'] = vals[valIndex]; │ │ │ │ - } else if (columns[valIndex] == 'overflow') { │ │ │ │ - attributes['overflow'] = vals[valIndex]; │ │ │ │ - } else { │ │ │ │ - // For StyleMap filtering, allow additional │ │ │ │ - // columns to be stored as attributes. │ │ │ │ - attributes[columns[valIndex]] = vals[valIndex]; │ │ │ │ + /** │ │ │ │ + * Method: cluster │ │ │ │ + * Cluster features based on some threshold distance. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * event - {Object} The event received when cluster is called as a │ │ │ │ + * result of a moveend event. │ │ │ │ + */ │ │ │ │ + cluster: function(event) { │ │ │ │ + if ((!event || event.zoomChanged) && this.features) { │ │ │ │ + var resolution = this.layer.map.getResolution(); │ │ │ │ + if (resolution != this.resolution || !this.clustersExist()) { │ │ │ │ + this.resolution = resolution; │ │ │ │ + var clusters = []; │ │ │ │ + var feature, clustered, cluster; │ │ │ │ + for (var i = 0; i < this.features.length; ++i) { │ │ │ │ + feature = this.features[i]; │ │ │ │ + if (feature.geometry) { │ │ │ │ + clustered = false; │ │ │ │ + for (var j = clusters.length - 1; j >= 0; --j) { │ │ │ │ + cluster = clusters[j]; │ │ │ │ + if (this.shouldCluster(cluster, feature)) { │ │ │ │ + this.addToCluster(cluster, feature); │ │ │ │ + clustered = true; │ │ │ │ + break; │ │ │ │ } │ │ │ │ } │ │ │ │ + if (!clustered) { │ │ │ │ + clusters.push(this.createCluster(this.features[i])); │ │ │ │ + } │ │ │ │ } │ │ │ │ - if (set) { │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - geometry.transform(this.externalProjection, │ │ │ │ - this.internalProjection); │ │ │ │ + } │ │ │ │ + this.clustering = true; │ │ │ │ + this.layer.removeAllFeatures(); │ │ │ │ + this.clustering = false; │ │ │ │ + if (clusters.length > 0) { │ │ │ │ + if (this.threshold > 1) { │ │ │ │ + var clone = clusters.slice(); │ │ │ │ + clusters = []; │ │ │ │ + var candidate; │ │ │ │ + for (var i = 0, len = clone.length; i < len; ++i) { │ │ │ │ + candidate = clone[i]; │ │ │ │ + if (candidate.attributes.count < this.threshold) { │ │ │ │ + Array.prototype.push.apply(clusters, candidate.cluster); │ │ │ │ + } else { │ │ │ │ + clusters.push(candidate); │ │ │ │ + } │ │ │ │ } │ │ │ │ - var feature = new OpenLayers.Feature.Vector(geometry, attributes, style); │ │ │ │ - features.push(feature); │ │ │ │ } │ │ │ │ + this.clustering = true; │ │ │ │ + // A legitimate feature addition could occur during this │ │ │ │ + // addFeatures call. For clustering to behave well, features │ │ │ │ + // should be removed from a layer before requesting a new batch. │ │ │ │ + this.layer.addFeatures(clusters); │ │ │ │ + this.clustering = false; │ │ │ │ } │ │ │ │ + this.clusters = clusters; │ │ │ │ } │ │ │ │ } │ │ │ │ - return features; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.Text" │ │ │ │ + /** │ │ │ │ + * Method: clustersExist │ │ │ │ + * Determine whether calculated clusters are already on the layer. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The calculated clusters are already on the layer. │ │ │ │ + */ │ │ │ │ + clustersExist: function() { │ │ │ │ + var exist = false; │ │ │ │ + if (this.clusters && this.clusters.length > 0 && │ │ │ │ + this.clusters.length == this.layer.features.length) { │ │ │ │ + exist = true; │ │ │ │ + for (var i = 0; i < this.clusters.length; ++i) { │ │ │ │ + if (this.clusters[i] != this.layer.features[i]) { │ │ │ │ + exist = false; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return exist; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: shouldCluster │ │ │ │ + * Determine whether to include a feature in a given cluster. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * cluster - {<OpenLayers.Feature.Vector>} A cluster. │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} A feature. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The feature should be included in the cluster. │ │ │ │ + */ │ │ │ │ + shouldCluster: function(cluster, feature) { │ │ │ │ + var cc = cluster.geometry.getBounds().getCenterLonLat(); │ │ │ │ + var fc = feature.geometry.getBounds().getCenterLonLat(); │ │ │ │ + var distance = ( │ │ │ │ + Math.sqrt( │ │ │ │ + Math.pow((cc.lon - fc.lon), 2) + Math.pow((cc.lat - fc.lat), 2) │ │ │ │ + ) / this.resolution │ │ │ │ + ); │ │ │ │ + return (distance <= this.distance); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: addToCluster │ │ │ │ + * Add a feature to a cluster. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * cluster - {<OpenLayers.Feature.Vector>} A cluster. │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} A feature. │ │ │ │ + */ │ │ │ │ + addToCluster: function(cluster, feature) { │ │ │ │ + cluster.cluster.push(feature); │ │ │ │ + cluster.attributes.count += 1; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: createCluster │ │ │ │ + * Given a feature, create a cluster. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Feature.Vector>} A cluster. │ │ │ │ + */ │ │ │ │ + createCluster: function(feature) { │ │ │ │ + var center = feature.geometry.getBounds().getCenterLonLat(); │ │ │ │ + var cluster = new OpenLayers.Feature.Vector( │ │ │ │ + new OpenLayers.Geometry.Point(center.lon, center.lat), { │ │ │ │ + count: 1 │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + cluster.cluster = [feature]; │ │ │ │ + return cluster; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Cluster" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/Text.js │ │ │ │ + OpenLayers/Strategy/Paging.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Layer/Markers.js │ │ │ │ - * @requires OpenLayers/Format/Text.js │ │ │ │ - * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ + * @requires OpenLayers/Strategy.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.Text │ │ │ │ - * This layer creates markers given data in a text file. The <location> │ │ │ │ - * property of the layer (specified as a property of the options argument │ │ │ │ - * in the <OpenLayers.Layer.Text> constructor) points to a tab delimited │ │ │ │ - * file with data used to create markers. │ │ │ │ - * │ │ │ │ - * The first row of the data file should be a header line with the column names │ │ │ │ - * of the data. Each column should be delimited by a tab space. The │ │ │ │ - * possible columns are: │ │ │ │ - * - *point* lat,lon of the point where a marker is to be placed │ │ │ │ - * - *lat* Latitude of the point where a marker is to be placed │ │ │ │ - * - *lon* Longitude of the point where a marker is to be placed │ │ │ │ - * - *icon* or *image* URL of marker icon to use. │ │ │ │ - * - *iconSize* Size of Icon to use. │ │ │ │ - * - *iconOffset* Where the top-left corner of the icon is to be placed │ │ │ │ - * relative to the latitude and longitude of the point. │ │ │ │ - * - *title* The text of the 'title' is placed inside an 'h2' marker │ │ │ │ - * inside a popup, which opens when the marker is clicked. │ │ │ │ - * - *description* The text of the 'description' is placed below the h2 │ │ │ │ - * in the popup. this can be plain text or HTML. │ │ │ │ - * │ │ │ │ - * Example text file: │ │ │ │ - * (code) │ │ │ │ - * lat lon title description iconSize iconOffset icon │ │ │ │ - * 10 20 title description 21,25 -10,-25 http://www.openlayers.org/dev/img/marker.png │ │ │ │ - * (end) │ │ │ │ + * Class: OpenLayers.Strategy.Paging │ │ │ │ + * Strategy for vector feature paging │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Markers> │ │ │ │ + * - <OpenLayers.Strategy> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, { │ │ │ │ +OpenLayers.Strategy.Paging = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: location │ │ │ │ - * {String} URL of text file. Must be specified in the "options" argument │ │ │ │ - * of the constructor. Can not be changed once passed in. │ │ │ │ - */ │ │ │ │ - location: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ * Property: features │ │ │ │ - * {Array(<OpenLayers.Feature>)} │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} Cached features. │ │ │ │ */ │ │ │ │ features: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: formatOptions │ │ │ │ - * {Object} Hash of options which should be passed to the format when it is │ │ │ │ - * created. Must be passed in the constructor. │ │ │ │ + * Property: length │ │ │ │ + * {Integer} Number of features per page. Default is 10. │ │ │ │ */ │ │ │ │ - formatOptions: null, │ │ │ │ + length: 10, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: selectedFeature │ │ │ │ - * {<OpenLayers.Feature>} │ │ │ │ + /** │ │ │ │ + * Property: num │ │ │ │ + * {Integer} The currently displayed page number. │ │ │ │ */ │ │ │ │ - selectedFeature: null, │ │ │ │ + num: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.Text │ │ │ │ - * Create a text layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * options - {Object} Object with properties to be set on the layer. │ │ │ │ - * Must include <location> property. │ │ │ │ + * Property: paging │ │ │ │ + * {Boolean} The strategy is currently changing pages. │ │ │ │ */ │ │ │ │ - initialize: function(name, options) { │ │ │ │ - OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments); │ │ │ │ - this.features = []; │ │ │ │ - }, │ │ │ │ + paging: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ + * Constructor: OpenLayers.Strategy.Paging │ │ │ │ + * Create a new paging strategy. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - // Warning: Layer.Markers.destroy() must be called prior to calling │ │ │ │ - // clearFeatures() here, otherwise we leak memory. Indeed, if │ │ │ │ - // Layer.Markers.destroy() is called after clearFeatures(), it won't be │ │ │ │ - // able to remove the marker image elements from the layer's div since │ │ │ │ - // the markers will have been destroyed by clearFeatures(). │ │ │ │ - OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments); │ │ │ │ - this.clearFeatures(); │ │ │ │ - this.features = null; │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: loadText │ │ │ │ - * Start the load of the Text data. Don't do this when we first add the layer, │ │ │ │ - * since we may not be visible at any point, and it would therefore be a waste. │ │ │ │ + * APIMethod: activate │ │ │ │ + * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The strategy was successfully activated. │ │ │ │ */ │ │ │ │ - loadText: function() { │ │ │ │ - if (!this.loaded) { │ │ │ │ - if (this.location != null) { │ │ │ │ - │ │ │ │ - var onFail = function(e) { │ │ │ │ - this.events.triggerEvent("loadend"); │ │ │ │ - }; │ │ │ │ - │ │ │ │ - this.events.triggerEvent("loadstart"); │ │ │ │ - OpenLayers.Request.GET({ │ │ │ │ - url: this.location, │ │ │ │ - success: this.parseData, │ │ │ │ - failure: onFail, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.loaded = true; │ │ │ │ - } │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + "beforefeaturesadded": this.cacheFeatures, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveTo │ │ │ │ - * If layer is visible and Text has not been loaded, load Text. │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ + * tear-down. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {Object} │ │ │ │ - * zoomChanged - {Object} │ │ │ │ - * minor - {Object} │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The strategy was successfully deactivated. │ │ │ │ */ │ │ │ │ - moveTo: function(bounds, zoomChanged, minor) { │ │ │ │ - OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments); │ │ │ │ - if (this.visibility && !this.loaded) { │ │ │ │ - this.loadText(); │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.clearCache(); │ │ │ │ + this.layer.events.un({ │ │ │ │ + "beforefeaturesadded": this.cacheFeatures, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseData │ │ │ │ + * Method: cacheFeatures │ │ │ │ + * Cache features before they are added to the layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>} │ │ │ │ + * event - {Object} The event that this was listening for. This will come │ │ │ │ + * with a batch of features to be paged. │ │ │ │ */ │ │ │ │ - parseData: function(ajaxRequest) { │ │ │ │ - var text = ajaxRequest.responseText; │ │ │ │ - │ │ │ │ - var options = {}; │ │ │ │ - │ │ │ │ - OpenLayers.Util.extend(options, this.formatOptions); │ │ │ │ + cacheFeatures: function(event) { │ │ │ │ + if (!this.paging) { │ │ │ │ + this.clearCache(); │ │ │ │ + this.features = event.features; │ │ │ │ + this.pageNext(event); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.map && !this.projection.equals(this.map.getProjectionObject())) { │ │ │ │ - options.externalProjection = this.projection; │ │ │ │ - options.internalProjection = this.map.getProjectionObject(); │ │ │ │ + /** │ │ │ │ + * Method: clearCache │ │ │ │ + * Clear out the cached features. This destroys features, assuming │ │ │ │ + * nothing else has a reference. │ │ │ │ + */ │ │ │ │ + clearCache: function() { │ │ │ │ + if (this.features) { │ │ │ │ + for (var i = 0; i < this.features.length; ++i) { │ │ │ │ + this.features[i].destroy(); │ │ │ │ + } │ │ │ │ } │ │ │ │ + this.features = null; │ │ │ │ + this.num = null; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var parser = new OpenLayers.Format.Text(options); │ │ │ │ - var features = parser.read(text); │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - var data = {}; │ │ │ │ - var feature = features[i]; │ │ │ │ - var location; │ │ │ │ - var iconSize, iconOffset; │ │ │ │ - │ │ │ │ - location = new OpenLayers.LonLat(feature.geometry.x, │ │ │ │ - feature.geometry.y); │ │ │ │ - │ │ │ │ - if (feature.style.graphicWidth && │ │ │ │ - feature.style.graphicHeight) { │ │ │ │ - iconSize = new OpenLayers.Size( │ │ │ │ - feature.style.graphicWidth, │ │ │ │ - feature.style.graphicHeight); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // FIXME: At the moment, we only use this if we have an │ │ │ │ - // externalGraphic, because icon has no setOffset API Method. │ │ │ │ - /** │ │ │ │ - * FIXME FIRST!! │ │ │ │ - * The Text format does all sorts of parseFloating │ │ │ │ - * The result of a parseFloat for a bogus string is NaN. That │ │ │ │ - * means the three possible values here are undefined, NaN, or a │ │ │ │ - * number. The previous check was an identity check for null. This │ │ │ │ - * means it was failing for all undefined or NaN. A slightly better │ │ │ │ - * check is for undefined. An even better check is to see if the │ │ │ │ - * value is a number (see #1441). │ │ │ │ - */ │ │ │ │ - if (feature.style.graphicXOffset !== undefined && │ │ │ │ - feature.style.graphicYOffset !== undefined) { │ │ │ │ - iconOffset = new OpenLayers.Pixel( │ │ │ │ - feature.style.graphicXOffset, │ │ │ │ - feature.style.graphicYOffset); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (feature.style.externalGraphic != null) { │ │ │ │ - data.icon = new OpenLayers.Icon(feature.style.externalGraphic, │ │ │ │ - iconSize, │ │ │ │ - iconOffset); │ │ │ │ - } else { │ │ │ │ - data.icon = OpenLayers.Marker.defaultIcon(); │ │ │ │ - │ │ │ │ - //allows for the case where the image url is not │ │ │ │ - // specified but the size is. use a default icon │ │ │ │ - // but change the size │ │ │ │ - if (iconSize != null) { │ │ │ │ - data.icon.setSize(iconSize); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - if ((feature.attributes.title != null) && │ │ │ │ - (feature.attributes.description != null)) { │ │ │ │ - data['popupContentHTML'] = │ │ │ │ - '<h2>' + feature.attributes.title + '</h2>' + │ │ │ │ - '<p>' + feature.attributes.description + '</p>'; │ │ │ │ - } │ │ │ │ - │ │ │ │ - data['overflow'] = feature.attributes.overflow || "auto"; │ │ │ │ - │ │ │ │ - var markerFeature = new OpenLayers.Feature(this, location, data); │ │ │ │ - this.features.push(markerFeature); │ │ │ │ - var marker = markerFeature.createMarker(); │ │ │ │ - if ((feature.attributes.title != null) && │ │ │ │ - (feature.attributes.description != null)) { │ │ │ │ - marker.events.register('click', markerFeature, this.markerClick); │ │ │ │ - } │ │ │ │ - this.addMarker(marker); │ │ │ │ - } │ │ │ │ - this.events.triggerEvent("loadend"); │ │ │ │ + /** │ │ │ │ + * APIMethod: pageCount │ │ │ │ + * Get the total count of pages given the current cache of features. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} The page count. │ │ │ │ + */ │ │ │ │ + pageCount: function() { │ │ │ │ + var numFeatures = this.features ? this.features.length : 0; │ │ │ │ + return Math.ceil(numFeatures / this.length); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: markerClick │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * APIMethod: pageNum │ │ │ │ + * Get the zero based page number. │ │ │ │ * │ │ │ │ - * Context: │ │ │ │ - * - {<OpenLayers.Feature>} │ │ │ │ + * Returns: │ │ │ │ + * {Integer} The current page number being displayed. │ │ │ │ */ │ │ │ │ - markerClick: function(evt) { │ │ │ │ - var sameMarkerClicked = (this == this.layer.selectedFeature); │ │ │ │ - this.layer.selectedFeature = (!sameMarkerClicked) ? this : null; │ │ │ │ - for (var i = 0, len = this.layer.map.popups.length; i < len; i++) { │ │ │ │ - this.layer.map.removePopup(this.layer.map.popups[i]); │ │ │ │ - } │ │ │ │ - if (!sameMarkerClicked) { │ │ │ │ - this.layer.map.addPopup(this.createPopup()); │ │ │ │ - } │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ + pageNum: function() { │ │ │ │ + return this.num; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clearFeatures │ │ │ │ + * APIMethod: pageLength │ │ │ │ + * Gets or sets page length. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * newLength - {Integer} Optional length to be set. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} The length of a page (number of features per page). │ │ │ │ */ │ │ │ │ - clearFeatures: function() { │ │ │ │ - if (this.features != null) { │ │ │ │ - while (this.features.length > 0) { │ │ │ │ - var feature = this.features[0]; │ │ │ │ - OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ - feature.destroy(); │ │ │ │ - } │ │ │ │ + pageLength: function(newLength) { │ │ │ │ + if (newLength && newLength > 0) { │ │ │ │ + this.length = newLength; │ │ │ │ } │ │ │ │ + return this.length; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Text" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/Boxes.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer.js │ │ │ │ - * @requires OpenLayers/Layer/Markers.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.Boxes │ │ │ │ - * Draw divs as 'boxes' on the layer. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Markers> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, { │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.Boxes │ │ │ │ + * APIMethod: pageNext │ │ │ │ + * Display the next page of features. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} A new page was displayed. │ │ │ │ */ │ │ │ │ + pageNext: function(event) { │ │ │ │ + var changed = false; │ │ │ │ + if (this.features) { │ │ │ │ + if (this.num === null) { │ │ │ │ + this.num = -1; │ │ │ │ + } │ │ │ │ + var start = (this.num + 1) * this.length; │ │ │ │ + changed = this.page(start, event); │ │ │ │ + } │ │ │ │ + return changed; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawMarker │ │ │ │ - * Calculate the pixel location for the marker, create it, and │ │ │ │ - * add it to the layer's div │ │ │ │ + * APIMethod: pagePrevious │ │ │ │ + * Display the previous page of features. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * marker - {<OpenLayers.Marker.Box>} │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} A new page was displayed. │ │ │ │ */ │ │ │ │ - drawMarker: function(marker) { │ │ │ │ - var topleft = this.map.getLayerPxFromLonLat({ │ │ │ │ - lon: marker.bounds.left, │ │ │ │ - lat: marker.bounds.top │ │ │ │ - }); │ │ │ │ - var botright = this.map.getLayerPxFromLonLat({ │ │ │ │ - lon: marker.bounds.right, │ │ │ │ - lat: marker.bounds.bottom │ │ │ │ - }); │ │ │ │ - if (botright == null || topleft == null) { │ │ │ │ - marker.display(false); │ │ │ │ - } else { │ │ │ │ - var markerDiv = marker.draw(topleft, { │ │ │ │ - w: Math.max(1, botright.x - topleft.x), │ │ │ │ - h: Math.max(1, botright.y - topleft.y) │ │ │ │ - }); │ │ │ │ - if (!marker.drawn) { │ │ │ │ - this.div.appendChild(markerDiv); │ │ │ │ - marker.drawn = true; │ │ │ │ + pagePrevious: function() { │ │ │ │ + var changed = false; │ │ │ │ + if (this.features) { │ │ │ │ + if (this.num === null) { │ │ │ │ + this.num = this.pageCount(); │ │ │ │ } │ │ │ │ + var start = (this.num - 1) * this.length; │ │ │ │ + changed = this.page(start); │ │ │ │ } │ │ │ │ + return changed; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIMethod: removeMarker │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * marker - {<OpenLayers.Marker.Box>} │ │ │ │ + * Method: page │ │ │ │ + * Display the page starting at the given index from the cache. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} A new page was displayed. │ │ │ │ */ │ │ │ │ - removeMarker: function(marker) { │ │ │ │ - OpenLayers.Util.removeItem(this.markers, marker); │ │ │ │ - if ((marker.div != null) && │ │ │ │ - (marker.div.parentNode == this.div)) { │ │ │ │ - this.div.removeChild(marker.div); │ │ │ │ + page: function(start, event) { │ │ │ │ + var changed = false; │ │ │ │ + if (this.features) { │ │ │ │ + if (start >= 0 && start < this.features.length) { │ │ │ │ + var num = Math.floor(start / this.length); │ │ │ │ + if (num != this.num) { │ │ │ │ + this.paging = true; │ │ │ │ + var features = this.features.slice(start, start + this.length); │ │ │ │ + this.layer.removeFeatures(this.layer.features); │ │ │ │ + this.num = num; │ │ │ │ + // modify the event if any │ │ │ │ + if (event && event.features) { │ │ │ │ + // this.was called by an event listener │ │ │ │ + event.features = features; │ │ │ │ + } else { │ │ │ │ + // this was called directly on the strategy │ │ │ │ + this.layer.addFeatures(features); │ │ │ │ + } │ │ │ │ + this.paging = false; │ │ │ │ + changed = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + return changed; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Boxes" │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Paging" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/Vector.js │ │ │ │ + OpenLayers/Strategy/Fixed.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Layer.js │ │ │ │ - * @requires OpenLayers/Renderer.js │ │ │ │ - * @requires OpenLayers/StyleMap.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ - * @requires OpenLayers/Console.js │ │ │ │ - * @requires OpenLayers/Lang.js │ │ │ │ + * @requires OpenLayers/Strategy.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.Vector │ │ │ │ - * Instances of OpenLayers.Layer.Vector are used to render vector data from │ │ │ │ - * a variety of sources. Create a new vector layer with the │ │ │ │ - * <OpenLayers.Layer.Vector> constructor. │ │ │ │ + * Class: OpenLayers.Strategy.Fixed │ │ │ │ + * A simple strategy that requests features once and never requests new data. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Layer> │ │ │ │ + * - <OpenLayers.Strategy> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * layer.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Listeners will be called with a reference to an event object. The │ │ │ │ - * properties of this event depends on exactly what happened. │ │ │ │ - * │ │ │ │ - * All event objects have at least the following properties: │ │ │ │ - * object - {Object} A reference to layer.events.object. │ │ │ │ - * element - {DOMElement} A reference to layer.events.element. │ │ │ │ - * │ │ │ │ - * Supported map event types (in addition to those from <OpenLayers.Layer.events>): │ │ │ │ - * beforefeatureadded - Triggered before a feature is added. Listeners │ │ │ │ - * will receive an object with a *feature* property referencing the │ │ │ │ - * feature to be added. To stop the feature from being added, a │ │ │ │ - * listener should return false. │ │ │ │ - * beforefeaturesadded - Triggered before an array of features is added. │ │ │ │ - * Listeners will receive an object with a *features* property │ │ │ │ - * referencing the feature to be added. To stop the features from │ │ │ │ - * being added, a listener should return false. │ │ │ │ - * featureadded - Triggered after a feature is added. The event │ │ │ │ - * object passed to listeners will have a *feature* property with a │ │ │ │ - * reference to the added feature. │ │ │ │ - * featuresadded - Triggered after features are added. The event │ │ │ │ - * object passed to listeners will have a *features* property with a │ │ │ │ - * reference to an array of added features. │ │ │ │ - * beforefeatureremoved - Triggered before a feature is removed. Listeners │ │ │ │ - * will receive an object with a *feature* property referencing the │ │ │ │ - * feature to be removed. │ │ │ │ - * beforefeaturesremoved - Triggered before multiple features are removed. │ │ │ │ - * Listeners will receive an object with a *features* property │ │ │ │ - * referencing the features to be removed. │ │ │ │ - * featureremoved - Triggerd after a feature is removed. The event │ │ │ │ - * object passed to listeners will have a *feature* property with a │ │ │ │ - * reference to the removed feature. │ │ │ │ - * featuresremoved - Triggered after features are removed. The event │ │ │ │ - * object passed to listeners will have a *features* property with a │ │ │ │ - * reference to an array of removed features. │ │ │ │ - * beforefeatureselected - Triggered before a feature is selected. Listeners │ │ │ │ - * will receive an object with a *feature* property referencing the │ │ │ │ - * feature to be selected. To stop the feature from being selectd, a │ │ │ │ - * listener should return false. │ │ │ │ - * featureselected - Triggered after a feature is selected. Listeners │ │ │ │ - * will receive an object with a *feature* property referencing the │ │ │ │ - * selected feature. │ │ │ │ - * featureunselected - Triggered after a feature is unselected. │ │ │ │ - * Listeners will receive an object with a *feature* property │ │ │ │ - * referencing the unselected feature. │ │ │ │ - * beforefeaturemodified - Triggered when a feature is selected to │ │ │ │ - * be modified. Listeners will receive an object with a *feature* │ │ │ │ - * property referencing the selected feature. │ │ │ │ - * featuremodified - Triggered when a feature has been modified. │ │ │ │ - * Listeners will receive an object with a *feature* property referencing │ │ │ │ - * the modified feature. │ │ │ │ - * afterfeaturemodified - Triggered when a feature is finished being modified. │ │ │ │ - * Listeners will receive an object with a *feature* property referencing │ │ │ │ - * the modified feature. │ │ │ │ - * vertexmodified - Triggered when a vertex within any feature geometry │ │ │ │ - * has been modified. Listeners will receive an object with a │ │ │ │ - * *feature* property referencing the modified feature, a *vertex* │ │ │ │ - * property referencing the vertex modified (always a point geometry), │ │ │ │ - * and a *pixel* property referencing the pixel location of the │ │ │ │ - * modification. │ │ │ │ - * vertexremoved - Triggered when a vertex within any feature geometry │ │ │ │ - * has been deleted. Listeners will receive an object with a │ │ │ │ - * *feature* property referencing the modified feature, a *vertex* │ │ │ │ - * property referencing the vertex modified (always a point geometry), │ │ │ │ - * and a *pixel* property referencing the pixel location of the │ │ │ │ - * removal. │ │ │ │ - * sketchstarted - Triggered when a feature sketch bound for this layer │ │ │ │ - * is started. Listeners will receive an object with a *feature* │ │ │ │ - * property referencing the new sketch feature and a *vertex* property │ │ │ │ - * referencing the creation point. │ │ │ │ - * sketchmodified - Triggered when a feature sketch bound for this layer │ │ │ │ - * is modified. Listeners will receive an object with a *vertex* │ │ │ │ - * property referencing the modified vertex and a *feature* property │ │ │ │ - * referencing the sketch feature. │ │ │ │ - * sketchcomplete - Triggered when a feature sketch bound for this layer │ │ │ │ - * is complete. Listeners will receive an object with a *feature* │ │ │ │ - * property referencing the sketch feature. By returning false, a │ │ │ │ - * listener can stop the sketch feature from being added to the layer. │ │ │ │ - * refresh - Triggered when something wants a strategy to ask the protocol │ │ │ │ - * for a new set of features. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} The layer is a base layer. Default is false. Set this property │ │ │ │ - * in the layer options. │ │ │ │ - */ │ │ │ │ - isBaseLayer: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: isFixed │ │ │ │ - * {Boolean} Whether the layer remains in one place while dragging the │ │ │ │ - * map. Note that setting this to true will move the layer to the bottom │ │ │ │ - * of the layer stack. │ │ │ │ - */ │ │ │ │ - isFixed: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: features │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ - */ │ │ │ │ - features: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: filter │ │ │ │ - * {<OpenLayers.Filter>} The filter set in this layer, │ │ │ │ - * a strategy launching read requests can combined │ │ │ │ - * this filter with its own filter. │ │ │ │ - */ │ │ │ │ - filter: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: selectedFeatures │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ - */ │ │ │ │ - selectedFeatures: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: unrenderedFeatures │ │ │ │ - * {Object} hash of features, keyed by feature.id, that the renderer │ │ │ │ - * failed to draw │ │ │ │ - */ │ │ │ │ - unrenderedFeatures: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: reportError │ │ │ │ - * {Boolean} report friendly error message when loading of renderer │ │ │ │ - * fails. │ │ │ │ - */ │ │ │ │ - reportError: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: style │ │ │ │ - * {Object} Default style for the layer │ │ │ │ - */ │ │ │ │ - style: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: styleMap │ │ │ │ - * {<OpenLayers.StyleMap>} │ │ │ │ - */ │ │ │ │ - styleMap: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: strategies │ │ │ │ - * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer. │ │ │ │ - */ │ │ │ │ - strategies: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: protocol │ │ │ │ - * {<OpenLayers.Protocol>} Optional protocol for the layer. │ │ │ │ - */ │ │ │ │ - protocol: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: renderers │ │ │ │ - * {Array(String)} List of supported Renderer classes. Add to this list to │ │ │ │ - * add support for additional renderers. This list is ordered: │ │ │ │ - * the first renderer which returns true for the 'supported()' │ │ │ │ - * method will be used, if not defined in the 'renderer' option. │ │ │ │ - */ │ │ │ │ - renderers: ['SVG', 'VML', 'Canvas'], │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: renderer │ │ │ │ - * {<OpenLayers.Renderer>} │ │ │ │ - */ │ │ │ │ - renderer: null, │ │ │ │ +OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: rendererOptions │ │ │ │ - * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for │ │ │ │ - * supported options. │ │ │ │ - */ │ │ │ │ - rendererOptions: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: geometryType │ │ │ │ - * {String} geometryType allows you to limit the types of geometries this │ │ │ │ - * layer supports. This should be set to something like │ │ │ │ - * "OpenLayers.Geometry.Point" to limit types. │ │ │ │ - */ │ │ │ │ - geometryType: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: drawn │ │ │ │ - * {Boolean} Whether the Vector Layer features have been drawn yet. │ │ │ │ - */ │ │ │ │ - drawn: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: ratio │ │ │ │ - * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map. │ │ │ │ + * APIProperty: preload │ │ │ │ + * {Boolean} Load data before layer made visible. Enabling this may result │ │ │ │ + * in considerable overhead if your application loads many data layers │ │ │ │ + * that are not visible by default. Default is false. │ │ │ │ */ │ │ │ │ - ratio: 1, │ │ │ │ + preload: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.Vector │ │ │ │ - * Create a new vector layer │ │ │ │ + * Constructor: OpenLayers.Strategy.Fixed │ │ │ │ + * Create a new Fixed strategy. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} A name for the layer │ │ │ │ - * options - {Object} Optional object with non-default properties to set on │ │ │ │ - * the layer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.Vector>} A new vector layer │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ - initialize: function(name, options) { │ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ - │ │ │ │ - // allow user-set renderer, otherwise assign one │ │ │ │ - if (!this.renderer || !this.renderer.supported()) { │ │ │ │ - this.assignRenderer(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // if no valid renderer found, display error │ │ │ │ - if (!this.renderer || !this.renderer.supported()) { │ │ │ │ - this.renderer = null; │ │ │ │ - this.displayError(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (!this.styleMap) { │ │ │ │ - this.styleMap = new OpenLayers.StyleMap(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.features = []; │ │ │ │ - this.selectedFeatures = []; │ │ │ │ - this.unrenderedFeatures = {}; │ │ │ │ - │ │ │ │ - // Allow for custom layer behavior │ │ │ │ - if (this.strategies) { │ │ │ │ - for (var i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - this.strategies[i].setLayer(this); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Destroy this layer │ │ │ │ + * Method: activate │ │ │ │ + * Activate the strategy: load data or add listener to load when visible │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} True if the strategy was successfully activated or false if │ │ │ │ + * the strategy was already active. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.strategies) { │ │ │ │ - var strategy, i, len; │ │ │ │ - for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - strategy = this.strategies[i]; │ │ │ │ - if (strategy.autoDestroy) { │ │ │ │ - strategy.destroy(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.strategies = null; │ │ │ │ - } │ │ │ │ - if (this.protocol) { │ │ │ │ - if (this.protocol.autoDestroy) { │ │ │ │ - this.protocol.destroy(); │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); │ │ │ │ + if (activated) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + "refresh": this.load, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + if (this.layer.visibility == true || this.preload) { │ │ │ │ + this.load(); │ │ │ │ + } else { │ │ │ │ + this.layer.events.on({ │ │ │ │ + "visibilitychanged": this.load, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ - this.protocol = null; │ │ │ │ - } │ │ │ │ - this.destroyFeatures(); │ │ │ │ - this.features = null; │ │ │ │ - this.selectedFeatures = null; │ │ │ │ - this.unrenderedFeatures = null; │ │ │ │ - if (this.renderer) { │ │ │ │ - this.renderer.destroy(); │ │ │ │ } │ │ │ │ - this.renderer = null; │ │ │ │ - this.geometryType = null; │ │ │ │ - this.drawn = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clone │ │ │ │ - * Create a clone of this layer. │ │ │ │ + * Method: deactivate │ │ │ │ + * Deactivate the strategy. Undo what is done in <activate>. │ │ │ │ * │ │ │ │ - * Note: Features of the layer are also cloned. │ │ │ │ - * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer.Vector>} An exact clone of this layer │ │ │ │ + * {Boolean} The strategy was successfully deactivated. │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Vector(this.name, this.getOptions()); │ │ │ │ - } │ │ │ │ - │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ - │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - var features = this.features; │ │ │ │ - var len = features.length; │ │ │ │ - var clonedFeatures = new Array(len); │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ - clonedFeatures[i] = features[i].clone(); │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.layer.events.un({ │ │ │ │ + "refresh": this.load, │ │ │ │ + "visibilitychanged": this.load, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ - obj.features = clonedFeatures; │ │ │ │ - │ │ │ │ - return obj; │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: refresh │ │ │ │ - * Ask the layer to request features again and redraw them. Triggers │ │ │ │ - * the refresh event if the layer is in range and visible. │ │ │ │ + * Method: load │ │ │ │ + * Tells protocol to load data and unhooks the visibilitychanged event │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * obj - {Object} Optional object with properties for any listener of │ │ │ │ - * the refresh event. │ │ │ │ - */ │ │ │ │ - refresh: function(obj) { │ │ │ │ - if (this.calculateInRange() && this.visibility) { │ │ │ │ - this.events.triggerEvent("refresh", obj); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: assignRenderer │ │ │ │ - * Iterates through the available renderer implementations and selects │ │ │ │ - * and assigns the first one whose "supported()" function returns true. │ │ │ │ - */ │ │ │ │ - assignRenderer: function() { │ │ │ │ - for (var i = 0, len = this.renderers.length; i < len; i++) { │ │ │ │ - var rendererClass = this.renderers[i]; │ │ │ │ - var renderer = (typeof rendererClass == "function") ? │ │ │ │ - rendererClass : │ │ │ │ - OpenLayers.Renderer[rendererClass]; │ │ │ │ - if (renderer && renderer.prototype.supported()) { │ │ │ │ - this.renderer = new renderer(this.div, this.rendererOptions); │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: displayError │ │ │ │ - * Let the user know their browser isn't supported. │ │ │ │ - */ │ │ │ │ - displayError: function() { │ │ │ │ - if (this.reportError) { │ │ │ │ - OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", { │ │ │ │ - renderers: this.renderers.join('\n') │ │ │ │ - })); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * The layer has been added to the map. │ │ │ │ - * │ │ │ │ - * If there is no renderer set, the layer can't be used. Remove it. │ │ │ │ - * Otherwise, give the renderer a reference to the map and set its size. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ - │ │ │ │ - if (!this.renderer) { │ │ │ │ - this.map.removeLayer(this); │ │ │ │ - } else { │ │ │ │ - this.renderer.map = this.map; │ │ │ │ - │ │ │ │ - var newSize = this.map.getSize(); │ │ │ │ - newSize.w = newSize.w * this.ratio; │ │ │ │ - newSize.h = newSize.h * this.ratio; │ │ │ │ - this.renderer.setSize(newSize); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: afterAdd │ │ │ │ - * Called at the end of the map.addLayer sequence. At this point, the map │ │ │ │ - * will have a base layer. Any autoActivate strategies will be │ │ │ │ - * activated here. │ │ │ │ + * options - {Object} options to pass to protocol read. │ │ │ │ */ │ │ │ │ - afterAdd: function() { │ │ │ │ - if (this.strategies) { │ │ │ │ - var strategy, i, len; │ │ │ │ - for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - strategy = this.strategies[i]; │ │ │ │ - if (strategy.autoActivate) { │ │ │ │ - strategy.activate(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + load: function(options) { │ │ │ │ + var layer = this.layer; │ │ │ │ + layer.events.triggerEvent("loadstart", { │ │ │ │ + filter: layer.filter │ │ │ │ + }); │ │ │ │ + layer.protocol.read(OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: this.merge, │ │ │ │ + filter: layer.filter, │ │ │ │ + scope: this │ │ │ │ + }, options)); │ │ │ │ + layer.events.un({ │ │ │ │ + "visibilitychanged": this.load, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: removeMap │ │ │ │ - * The layer has been removed from the map. │ │ │ │ + * Method: merge │ │ │ │ + * Add all features to the layer. │ │ │ │ + * If the layer projection differs from the map projection, features │ │ │ │ + * will be transformed from the layer projection to the map projection. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - removeMap: function(map) { │ │ │ │ - this.drawn = false; │ │ │ │ - if (this.strategies) { │ │ │ │ - var strategy, i, len; │ │ │ │ - for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - strategy = this.strategies[i]; │ │ │ │ - if (strategy.autoActivate) { │ │ │ │ - strategy.deactivate(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: onMapResize │ │ │ │ - * Notify the renderer of the change in size. │ │ │ │ - * │ │ │ │ - */ │ │ │ │ - onMapResize: function() { │ │ │ │ - OpenLayers.Layer.prototype.onMapResize.apply(this, arguments); │ │ │ │ - │ │ │ │ - var newSize = this.map.getSize(); │ │ │ │ - newSize.w = newSize.w * this.ratio; │ │ │ │ - newSize.h = newSize.h * this.ratio; │ │ │ │ - this.renderer.setSize(newSize); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * Reset the vector layer's div so that it once again is lined up with │ │ │ │ - * the map. Notify the renderer of the change of extent, and in the │ │ │ │ - * case of a change of zoom level (resolution), have the │ │ │ │ - * renderer redraw features. │ │ │ │ - * │ │ │ │ - * If the layer has not yet been drawn, cycle through the layer's │ │ │ │ - * features and draw each one. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * zoomChanged - {Boolean} │ │ │ │ - * dragging - {Boolean} │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object passed │ │ │ │ + * by the protocol. │ │ │ │ */ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ - │ │ │ │ - var coordSysUnchanged = true; │ │ │ │ - if (!dragging) { │ │ │ │ - this.renderer.root.style.visibility = 'hidden'; │ │ │ │ - │ │ │ │ - var viewSize = this.map.getSize(), │ │ │ │ - viewWidth = viewSize.w, │ │ │ │ - viewHeight = viewSize.h, │ │ │ │ - offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2, │ │ │ │ - offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2; │ │ │ │ - offsetLeft += this.map.layerContainerOriginPx.x; │ │ │ │ - offsetLeft = -Math.round(offsetLeft); │ │ │ │ - offsetTop += this.map.layerContainerOriginPx.y; │ │ │ │ - offsetTop = -Math.round(offsetTop); │ │ │ │ - │ │ │ │ - this.div.style.left = offsetLeft + 'px'; │ │ │ │ - this.div.style.top = offsetTop + 'px'; │ │ │ │ - │ │ │ │ - var extent = this.map.getExtent().scale(this.ratio); │ │ │ │ - coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged); │ │ │ │ - │ │ │ │ - this.renderer.root.style.visibility = 'visible'; │ │ │ │ - │ │ │ │ - // Force a reflow on gecko based browsers to prevent jump/flicker. │ │ │ │ - // This seems to happen on only certain configurations; it was originally │ │ │ │ - // noticed in FF 2.0 and Linux. │ │ │ │ - if (OpenLayers.IS_GECKO === true) { │ │ │ │ - this.div.scrollLeft = this.div.scrollLeft; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (!zoomChanged && coordSysUnchanged) { │ │ │ │ - for (var i in this.unrenderedFeatures) { │ │ │ │ - var feature = this.unrenderedFeatures[i]; │ │ │ │ - this.drawFeature(feature); │ │ │ │ + merge: function(resp) { │ │ │ │ + var layer = this.layer; │ │ │ │ + layer.destroyFeatures(); │ │ │ │ + var features = resp.features; │ │ │ │ + if (features && features.length > 0) { │ │ │ │ + var remote = layer.projection; │ │ │ │ + var local = layer.map.getProjectionObject(); │ │ │ │ + if (!local.equals(remote)) { │ │ │ │ + var geom; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + geom = features[i].geometry; │ │ │ │ + if (geom) { │ │ │ │ + geom.transform(remote, local); │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ + layer.addFeatures(features); │ │ │ │ } │ │ │ │ - if (!this.drawn || zoomChanged || !coordSysUnchanged) { │ │ │ │ - this.drawn = true; │ │ │ │ - var feature; │ │ │ │ - for (var i = 0, len = this.features.length; i < len; i++) { │ │ │ │ - this.renderer.locked = (i !== (len - 1)); │ │ │ │ - feature = this.features[i]; │ │ │ │ - this.drawFeature(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: display │ │ │ │ - * Hide or show the Layer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * display - {Boolean} │ │ │ │ - */ │ │ │ │ - display: function(display) { │ │ │ │ - OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ - // we need to set the display style of the root in case it is attached │ │ │ │ - // to a foreign layer │ │ │ │ - var currentDisplay = this.div.style.display; │ │ │ │ - if (currentDisplay != this.renderer.root.style.display) { │ │ │ │ - this.renderer.root.style.display = currentDisplay; │ │ │ │ - } │ │ │ │ + layer.events.triggerEvent("loadend", { │ │ │ │ + response: resp │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: addFeatures │ │ │ │ - * Add Features to the layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ - addFeatures: function(features, options) { │ │ │ │ - if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ - features = [features]; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var notify = !options || !options.silent; │ │ │ │ - if (notify) { │ │ │ │ - var event = { │ │ │ │ - features: features │ │ │ │ - }; │ │ │ │ - var ret = this.events.triggerEvent("beforefeaturesadded", event); │ │ │ │ - if (ret === false) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - features = event.features; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // Track successfully added features for featuresadded event, since │ │ │ │ - // beforefeatureadded can veto single features. │ │ │ │ - var featuresAdded = []; │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - if (i != (features.length - 1)) { │ │ │ │ - this.renderer.locked = true; │ │ │ │ - } else { │ │ │ │ - this.renderer.locked = false; │ │ │ │ - } │ │ │ │ - var feature = features[i]; │ │ │ │ - │ │ │ │ - if (this.geometryType && │ │ │ │ - !(feature.geometry instanceof this.geometryType)) { │ │ │ │ - throw new TypeError('addFeatures: component should be an ' + │ │ │ │ - this.geometryType.prototype.CLASS_NAME); │ │ │ │ - } │ │ │ │ - │ │ │ │ - //give feature reference to its layer │ │ │ │ - feature.layer = this; │ │ │ │ - │ │ │ │ - if (!feature.style && this.style) { │ │ │ │ - feature.style = OpenLayers.Util.extend({}, this.style); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (notify) { │ │ │ │ - if (this.events.triggerEvent("beforefeatureadded", { │ │ │ │ - feature: feature │ │ │ │ - }) === false) { │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ - this.preFeatureInsert(feature); │ │ │ │ - } │ │ │ │ - │ │ │ │ - featuresAdded.push(feature); │ │ │ │ - this.features.push(feature); │ │ │ │ - this.drawFeature(feature); │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Fixed" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Strategy/Refresh.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featureadded", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.onFeatureInsert(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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 (notify) { │ │ │ │ - this.events.triggerEvent("featuresadded", { │ │ │ │ - features: featuresAdded │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Strategy.js │ │ │ │ + */ │ │ │ │ │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Strategy.Refresh │ │ │ │ + * A strategy that refreshes the layer. By default the strategy waits for a │ │ │ │ + * call to <refresh> before refreshing. By configuring the strategy with │ │ │ │ + * the <interval> option, refreshing can take place automatically. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Strategy> │ │ │ │ + */ │ │ │ │ +OpenLayers.Strategy.Refresh = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: removeFeatures │ │ │ │ - * Remove features from the layer. This erases any drawn features and │ │ │ │ - * removes them from the layer's control. The beforefeatureremoved │ │ │ │ - * and featureremoved events will be triggered for each feature. The │ │ │ │ - * featuresremoved event will be triggered after all features have │ │ │ │ - * been removed. To supress event triggering, use the silent option. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be │ │ │ │ - * removed. │ │ │ │ - * options - {Object} Optional properties for changing behavior of the │ │ │ │ - * removal. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * silent - {Boolean} Supress event triggering. Default is false. │ │ │ │ - */ │ │ │ │ - removeFeatures: function(features, options) { │ │ │ │ - if (!features || features.length === 0) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (features === this.features) { │ │ │ │ - return this.removeAllFeatures(options); │ │ │ │ - } │ │ │ │ - if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ - features = [features]; │ │ │ │ - } │ │ │ │ - if (features === this.selectedFeatures) { │ │ │ │ - features = features.slice(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var notify = !options || !options.silent; │ │ │ │ - │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent( │ │ │ │ - "beforefeaturesremoved", { │ │ │ │ - features: features │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - // We remain locked so long as we're not at 0 │ │ │ │ - // and the 'next' feature has a geometry. We do the geometry check │ │ │ │ - // because if all the features after the current one are 'null', we │ │ │ │ - // won't call eraseGeometry, so we break the 'renderer functions │ │ │ │ - // will always be called with locked=false *last*' rule. The end result │ │ │ │ - // is a possible gratiutious unlocking to save a loop through the rest │ │ │ │ - // of the list checking the remaining features every time. So long as │ │ │ │ - // null geoms are rare, this is probably okay. │ │ │ │ - if (i != 0 && features[i - 1].geometry) { │ │ │ │ - this.renderer.locked = true; │ │ │ │ - } else { │ │ │ │ - this.renderer.locked = false; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var feature = features[i]; │ │ │ │ - delete this.unrenderedFeatures[feature.id]; │ │ │ │ - │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.features = OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ - // feature has no layer at this point │ │ │ │ - feature.layer = null; │ │ │ │ - │ │ │ │ - if (feature.geometry) { │ │ │ │ - this.renderer.eraseFeatures(feature); │ │ │ │ - } │ │ │ │ - │ │ │ │ - //in the case that this feature is one of the selected features, │ │ │ │ - // remove it from that array as well. │ │ │ │ - if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) { │ │ │ │ - OpenLayers.Util.removeItem(this.selectedFeatures, feature); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featuresremoved", { │ │ │ │ - features: features │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: removeAllFeatures │ │ │ │ - * Remove all features from the layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional properties for changing behavior of the │ │ │ │ - * removal. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * silent - {Boolean} Supress event triggering. Default is false. │ │ │ │ + * Property: force │ │ │ │ + * {Boolean} Force a refresh on the layer. Default is false. │ │ │ │ */ │ │ │ │ - removeAllFeatures: function(options) { │ │ │ │ - var notify = !options || !options.silent; │ │ │ │ - var features = this.features; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent( │ │ │ │ - "beforefeaturesremoved", { │ │ │ │ - features: features │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - var feature; │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - feature = features[i]; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - feature.layer = null; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.renderer.clear(); │ │ │ │ - this.features = []; │ │ │ │ - this.unrenderedFeatures = {}; │ │ │ │ - this.selectedFeatures = []; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featuresremoved", { │ │ │ │ - features: features │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + force: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroyFeatures │ │ │ │ - * Erase and destroy features on the layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of │ │ │ │ - * features to destroy. If not supplied, all features on the layer │ │ │ │ - * will be destroyed. │ │ │ │ - * options - {Object} │ │ │ │ + * Property: interval │ │ │ │ + * {Number} Auto-refresh. Default is 0. If > 0, layer will be refreshed │ │ │ │ + * every N milliseconds. │ │ │ │ */ │ │ │ │ - destroyFeatures: function(features, options) { │ │ │ │ - var all = (features == undefined); // evaluates to true if │ │ │ │ - // features is null │ │ │ │ - if (all) { │ │ │ │ - features = this.features; │ │ │ │ - } │ │ │ │ - if (features) { │ │ │ │ - this.removeFeatures(features, options); │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - features[i].destroy(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + interval: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: drawFeature │ │ │ │ - * Draw (or redraw) a feature on the layer. If the optional style argument │ │ │ │ - * is included, this style will be used. If no style is included, the │ │ │ │ - * feature's style will be used. If the feature doesn't have a style, │ │ │ │ - * the layer's style will be used. │ │ │ │ - * │ │ │ │ - * This function is not designed to be used when adding features to │ │ │ │ - * the layer (use addFeatures instead). It is meant to be used when │ │ │ │ - * the style of a feature has changed, or in some other way needs to │ │ │ │ - * visually updated *after* it has already been added to a layer. You │ │ │ │ - * must add the feature to the layer for most layer-related events to │ │ │ │ - * happen. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * style - {String | Object} Named render intent or full symbolizer object. │ │ │ │ + * Property: timer │ │ │ │ + * {Number} The id of the timer. │ │ │ │ */ │ │ │ │ - drawFeature: function(feature, style) { │ │ │ │ - // don't try to draw the feature with the renderer if the layer is not │ │ │ │ - // drawn itself │ │ │ │ - if (!this.drawn) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (typeof style != "object") { │ │ │ │ - if (!style && feature.state === OpenLayers.State.DELETE) { │ │ │ │ - style = "delete"; │ │ │ │ - } │ │ │ │ - var renderIntent = style || feature.renderIntent; │ │ │ │ - style = feature.style || this.style; │ │ │ │ - if (!style) { │ │ │ │ - style = this.styleMap.createSymbolizer(feature, renderIntent); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - var drawn = this.renderer.drawFeature(feature, style); │ │ │ │ - //TODO remove the check for null when we get rid of Renderer.SVG │ │ │ │ - if (drawn === false || drawn === null) { │ │ │ │ - this.unrenderedFeatures[feature.id] = feature; │ │ │ │ - } else { │ │ │ │ - delete this.unrenderedFeatures[feature.id]; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + timer: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: eraseFeatures │ │ │ │ - * Erase features from the layer. │ │ │ │ + * Constructor: OpenLayers.Strategy.Refresh │ │ │ │ + * Create a new Refresh strategy. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ - eraseFeatures: function(features) { │ │ │ │ - this.renderer.eraseFeatures(features); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getFeatureFromEvent │ │ │ │ - * Given an event, return a feature if the event occurred over one. │ │ │ │ - * Otherwise, return null. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ + * APIMethod: activate │ │ │ │ + * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A feature if one was under the event. │ │ │ │ + * {Boolean} True if the strategy was successfully activated. │ │ │ │ */ │ │ │ │ - getFeatureFromEvent: function(evt) { │ │ │ │ - if (!this.renderer) { │ │ │ │ - throw new Error('getFeatureFromEvent called on layer with no ' + │ │ │ │ - 'renderer. This usually means you destroyed a ' + │ │ │ │ - 'layer, but not some handler which is associated ' + │ │ │ │ - 'with it.'); │ │ │ │ - } │ │ │ │ - var feature = null; │ │ │ │ - var featureId = this.renderer.getFeatureIdFromEvent(evt); │ │ │ │ - if (featureId) { │ │ │ │ - if (typeof featureId === "string") { │ │ │ │ - feature = this.getFeatureById(featureId); │ │ │ │ - } else { │ │ │ │ - feature = featureId; │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + if (this.layer.visibility === true) { │ │ │ │ + this.start(); │ │ │ │ } │ │ │ │ + this.layer.events.on({ │ │ │ │ + "visibilitychanged": this.reset, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ - return feature; │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getFeatureBy │ │ │ │ - * Given a property value, return the feature if it exists in the features array │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * property - {String} │ │ │ │ - * value - {String} │ │ │ │ - * │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ + * tear-down. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A feature corresponding to the given │ │ │ │ - * property value or null if there is no such feature. │ │ │ │ + * {Boolean} True if the strategy was successfully deactivated. │ │ │ │ */ │ │ │ │ - getFeatureBy: function(property, value) { │ │ │ │ - //TBD - would it be more efficient to use a hash for this.features? │ │ │ │ - var feature = null; │ │ │ │ - for (var i = 0, len = this.features.length; i < len; ++i) { │ │ │ │ - if (this.features[i][property] == value) { │ │ │ │ - feature = this.features[i]; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.stop(); │ │ │ │ + this.layer.events.un({ │ │ │ │ + "visibilitychanged": this.reset, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ - return feature; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getFeatureById │ │ │ │ - * Given a feature id, return the feature if it exists in the features array │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * featureId - {String} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A feature corresponding to the given │ │ │ │ - * featureId or null if there is no such feature. │ │ │ │ - */ │ │ │ │ - getFeatureById: function(featureId) { │ │ │ │ - return this.getFeatureBy('id', featureId); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getFeatureByFid │ │ │ │ - * Given a feature fid, return the feature if it exists in the features array │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * featureFid - {String} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A feature corresponding to the given │ │ │ │ - * featureFid or null if there is no such feature. │ │ │ │ - */ │ │ │ │ - getFeatureByFid: function(featureFid) { │ │ │ │ - return this.getFeatureBy('fid', featureFid); │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getFeaturesByAttribute │ │ │ │ - * Returns an array of features that have the given attribute key set to the │ │ │ │ - * given value. Comparison of attribute values takes care of datatypes, e.g. │ │ │ │ - * the string '1234' is not equal to the number 1234. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * attrName - {String} │ │ │ │ - * attrValue - {Mixed} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * Array({<OpenLayers.Feature.Vector>}) An array of features that have the │ │ │ │ - * passed named attribute set to the given value. │ │ │ │ + * Method: reset │ │ │ │ + * Start or cancel the refresh interval depending on the visibility of │ │ │ │ + * the layer. │ │ │ │ */ │ │ │ │ - getFeaturesByAttribute: function(attrName, attrValue) { │ │ │ │ - var i, │ │ │ │ - feature, │ │ │ │ - len = this.features.length, │ │ │ │ - foundFeatures = []; │ │ │ │ - for (i = 0; i < len; i++) { │ │ │ │ - feature = this.features[i]; │ │ │ │ - if (feature && feature.attributes) { │ │ │ │ - if (feature.attributes[attrName] === attrValue) { │ │ │ │ - foundFeatures.push(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + reset: function() { │ │ │ │ + if (this.layer.visibility === true) { │ │ │ │ + this.start(); │ │ │ │ + } else { │ │ │ │ + this.stop(); │ │ │ │ } │ │ │ │ - return foundFeatures; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Unselect the selected features │ │ │ │ - * i.e. clears the featureSelection array │ │ │ │ - * change the style back │ │ │ │ - clearSelection: function() { │ │ │ │ + * Method: start │ │ │ │ + * Start the refresh interval. │ │ │ │ + */ │ │ │ │ + start: function() { │ │ │ │ + if (this.interval && typeof this.interval === "number" && │ │ │ │ + this.interval > 0) { │ │ │ │ │ │ │ │ - var vectorLayer = this.map.vectorLayer; │ │ │ │ - for (var i = 0; i < this.map.featureSelection.length; i++) { │ │ │ │ - var featureSelection = this.map.featureSelection[i]; │ │ │ │ - vectorLayer.drawFeature(featureSelection, vectorLayer.style); │ │ │ │ + this.timer = window.setInterval( │ │ │ │ + OpenLayers.Function.bind(this.refresh, this), │ │ │ │ + this.interval); │ │ │ │ } │ │ │ │ - this.map.featureSelection = []; │ │ │ │ }, │ │ │ │ - */ │ │ │ │ - │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: onFeatureInsert │ │ │ │ - * method called after a feature is inserted. │ │ │ │ - * Does nothing by default. Override this if you │ │ │ │ - * need to do something on feature updates. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - */ │ │ │ │ - onFeatureInsert: function(feature) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: preFeatureInsert │ │ │ │ - * method called before a feature is inserted. │ │ │ │ - * Does nothing by default. Override this if you │ │ │ │ - * need to do something when features are first added to the │ │ │ │ - * layer, but before they are drawn, such as adjust the style. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * APIMethod: refresh │ │ │ │ + * Tell the strategy to refresh which will refresh the layer. │ │ │ │ */ │ │ │ │ - preFeatureInsert: function(feature) {}, │ │ │ │ + refresh: function() { │ │ │ │ + if (this.layer && this.layer.refresh && │ │ │ │ + typeof this.layer.refresh == "function") { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getDataExtent │ │ │ │ - * Calculates the max extent which includes all of the features. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} or null if the layer has no features with │ │ │ │ - * geometries. │ │ │ │ - */ │ │ │ │ - getDataExtent: function() { │ │ │ │ - var maxExtent = null; │ │ │ │ - var features = this.features; │ │ │ │ - if (features && (features.length > 0)) { │ │ │ │ - var geometry = null; │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - geometry = features[i].geometry; │ │ │ │ - if (geometry) { │ │ │ │ - if (maxExtent === null) { │ │ │ │ - maxExtent = new OpenLayers.Bounds(); │ │ │ │ - } │ │ │ │ - maxExtent.extend(geometry.getBounds()); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + this.layer.refresh({ │ │ │ │ + force: this.force │ │ │ │ + }); │ │ │ │ } │ │ │ │ - return maxExtent; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Vector" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/PointTrack.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/Vector.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.PointTrack │ │ │ │ - * Vector layer to display ordered point features as a line, creating one │ │ │ │ - * LineString feature for each pair of two points. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Vector> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: dataFrom │ │ │ │ - * {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or │ │ │ │ - * {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines │ │ │ │ - * should get the data/attributes from one of the two points it is │ │ │ │ - * composed of, which one should it be? │ │ │ │ - */ │ │ │ │ - dataFrom: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: styleFrom │ │ │ │ - * {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or │ │ │ │ - * {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines │ │ │ │ - * should get the style from one of the two points it is composed of, │ │ │ │ - * which one should it be? │ │ │ │ - */ │ │ │ │ - styleFrom: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.PointTrack │ │ │ │ - * Constructor for a new OpenLayers.PointTrack instance. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} name of the layer │ │ │ │ - * options - {Object} Optional object with properties to tag onto the │ │ │ │ - * instance. │ │ │ │ - */ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIMethod: addNodes │ │ │ │ - * Adds point features that will be used to create lines from, using point │ │ │ │ - * pairs. The first point of a pair will be the source node, the second │ │ │ │ - * will be the target node. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * pointFeatures - {Array(<OpenLayers.Feature>)} │ │ │ │ - * options - {Object} │ │ │ │ - * │ │ │ │ - * Supported options: │ │ │ │ - * silent - {Boolean} true to suppress (before)feature(s)added events │ │ │ │ + * Method: stop │ │ │ │ + * Cancels the refresh interval. │ │ │ │ */ │ │ │ │ - addNodes: function(pointFeatures, options) { │ │ │ │ - if (pointFeatures.length < 2) { │ │ │ │ - throw new Error("At least two point features have to be added to " + │ │ │ │ - "create a line from"); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var lines = new Array(pointFeatures.length - 1); │ │ │ │ - │ │ │ │ - var pointFeature, startPoint, endPoint; │ │ │ │ - for (var i = 0, len = pointFeatures.length; i < len; i++) { │ │ │ │ - pointFeature = pointFeatures[i]; │ │ │ │ - endPoint = pointFeature.geometry; │ │ │ │ - │ │ │ │ - if (!endPoint) { │ │ │ │ - var lonlat = pointFeature.lonlat; │ │ │ │ - endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat); │ │ │ │ - } else if (endPoint.CLASS_NAME != "OpenLayers.Geometry.Point") { │ │ │ │ - throw new TypeError("Only features with point geometries are supported."); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (i > 0) { │ │ │ │ - var attributes = (this.dataFrom != null) ? │ │ │ │ - (pointFeatures[i + this.dataFrom].data || │ │ │ │ - pointFeatures[i + this.dataFrom].attributes) : │ │ │ │ - null; │ │ │ │ - var style = (this.styleFrom != null) ? │ │ │ │ - (pointFeatures[i + this.styleFrom].style) : │ │ │ │ - null; │ │ │ │ - var line = new OpenLayers.Geometry.LineString([startPoint, │ │ │ │ - endPoint │ │ │ │ - ]); │ │ │ │ - │ │ │ │ - lines[i - 1] = new OpenLayers.Feature.Vector(line, attributes, │ │ │ │ - style); │ │ │ │ - } │ │ │ │ - │ │ │ │ - startPoint = endPoint; │ │ │ │ + stop: function() { │ │ │ │ + if (this.timer !== null) { │ │ │ │ + window.clearInterval(this.timer); │ │ │ │ + this.timer = null; │ │ │ │ } │ │ │ │ - │ │ │ │ - this.addFeatures(lines, options); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.PointTrack" │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Refresh" │ │ │ │ }); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Layer.PointTrack.SOURCE_NODE │ │ │ │ - * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and │ │ │ │ - * <OpenLayers.Layer.PointTrack.styleFrom> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.PointTrack.SOURCE_NODE = -1; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Layer.PointTrack.TARGET_NODE │ │ │ │ - * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and │ │ │ │ - * <OpenLayers.Layer.PointTrack.styleFrom> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.PointTrack.TARGET_NODE = 0; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Layer.PointTrack.dataFrom │ │ │ │ - * {Object} with the following keys - *deprecated* │ │ │ │ - * - SOURCE_NODE: take data/attributes from the source node of the line │ │ │ │ - * - TARGET_NODE: take data/attributes from the target node of the line │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.PointTrack.dataFrom = { │ │ │ │ - 'SOURCE_NODE': -1, │ │ │ │ - 'TARGET_NODE': 0 │ │ │ │ -}; │ │ │ │ /* ====================================================================== │ │ │ │ OpenLayers/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 │ │ │ │ @@ -39003,7004 +35953,4409 @@ │ │ │ │ this.json = null; │ │ │ │ }, │ │ │ │ │ │ │ │ CLASS_NAME: "OpenLayers.Tile.UTFGrid" │ │ │ │ │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/UTFGrid.js │ │ │ │ + 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/Layer/XYZ.js │ │ │ │ - * @requires OpenLayers/Tile/UTFGrid.js │ │ │ │ + * @requires OpenLayers/Tile/Image.js │ │ │ │ */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.UTFGrid │ │ │ │ - * This Layer reads from UTFGrid tiled data sources. Since UTFGrids are │ │ │ │ - * essentially JSON-based ASCII art with attached attributes, they are not │ │ │ │ - * visibly rendered. In order to use them in the map, you must add a │ │ │ │ - * <OpenLayers.Control.UTFGrid> control as well. │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * │ │ │ │ - * (start code) │ │ │ │ - * var world_utfgrid = new OpenLayers.Layer.UTFGrid({ │ │ │ │ - * url: "/tiles/world_utfgrid/${z}/${x}/${y}.json", │ │ │ │ - * utfgridResolution: 4, │ │ │ │ - * displayInLayerSwitcher: false │ │ │ │ - * ); │ │ │ │ - * map.addLayer(world_utfgrid); │ │ │ │ - * │ │ │ │ - * var control = new OpenLayers.Control.UTFGrid({ │ │ │ │ - * layers: [world_utfgrid], │ │ │ │ - * handlerMode: 'move', │ │ │ │ - * callback: function(dataLookup) { │ │ │ │ - * // do something with returned data │ │ │ │ - * } │ │ │ │ - * }) │ │ │ │ - * (end code) │ │ │ │ +/** │ │ │ │ + * 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. │ │ │ │ * │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.XYZ> │ │ │ │ + * This mixin will be applied to <OpenLayers.Tile.Image> instances │ │ │ │ + * configured with <OpenLayers.Tile.Image.maxGetUrlLength> set. │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.UTFGrid = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ +OpenLayers.Tile.Image.IFrame = { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * Default is false, as UTFGrids are designed to be a transparent overlay layer. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - isBaseLayer: false, │ │ │ │ + useIFrame: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: projection │ │ │ │ - * {<OpenLayers.Projection>} │ │ │ │ - * Source projection for the UTFGrids. Default is "EPSG:900913". │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - projection: new OpenLayers.Projection("EPSG:900913"), │ │ │ │ + blankImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAQAIBRAA7", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: useJSONP │ │ │ │ - * {Boolean} │ │ │ │ - * Should we use a JSONP script approach instead of a standard AJAX call? │ │ │ │ + * Method: draw │ │ │ │ + * Set useIFrame in the instance, and operate the image/iframe switch. │ │ │ │ + * Then call Tile.Image.draw. │ │ │ │ * │ │ │ │ - * Set to true for using utfgrids from another server. │ │ │ │ - * Avoids same-domain policy restrictions. │ │ │ │ - * Note that this only works if the server accepts │ │ │ │ - * the callback GET parameter and dynamically │ │ │ │ - * wraps the returned json in a function call. │ │ │ │ - * │ │ │ │ - * Default is false │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - useJSONP: false, │ │ │ │ + draw: function() { │ │ │ │ + var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this); │ │ │ │ + if (draw) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: url │ │ │ │ - * {String} │ │ │ │ - * URL tempate for UTFGrid tiles. Include x, y, and z parameters. │ │ │ │ - * E.g. "/tiles/${z}/${x}/${y}.json" │ │ │ │ - */ │ │ │ │ + // 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); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: utfgridResolution │ │ │ │ - * {Number} │ │ │ │ - * Ratio of the pixel width to the width of a UTFGrid data point. If an │ │ │ │ - * entry in the grid represents a 4x4 block of pixels, the │ │ │ │ - * utfgridResolution would be 4. Default is 2 (specified in │ │ │ │ - * <OpenLayers.Tile.UTFGrid>). │ │ │ │ - */ │ │ │ │ + var usedIFrame = this.useIFrame; │ │ │ │ + this.useIFrame = this.maxGetUrlLength !== null && │ │ │ │ + !this.layer.async && │ │ │ │ + url.length > this.maxGetUrlLength; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: tileClass │ │ │ │ - * {<OpenLayers.Tile>} The tile class to use for this layer. │ │ │ │ - * Defaults is <OpenLayers.Tile.UTFGrid>. │ │ │ │ - */ │ │ │ │ - tileClass: OpenLayers.Tile.UTFGrid, │ │ │ │ + var fromIFrame = usedIFrame && !this.useIFrame; │ │ │ │ + var toIFrame = !usedIFrame && this.useIFrame; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Layer.UTFGrid │ │ │ │ - * Create a new UTFGrid layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * config - {Object} Configuration properties for the layer. │ │ │ │ - * │ │ │ │ - * Required configuration properties: │ │ │ │ - * url - {String} The url template for UTFGrid tiles. See the <url> property. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply( │ │ │ │ - this, [options.name, options.url, {}, options] │ │ │ │ - ); │ │ │ │ - this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ - utfgridResolution: this.utfgridResolution │ │ │ │ - }, this.tileOptions); │ │ │ │ + if (fromIFrame || toIFrame) { │ │ │ │ + │ │ │ │ + // Switching between GET (image) and POST (iframe). │ │ │ │ + │ │ │ │ + // We remove the imgDiv (really either an image or an iframe) │ │ │ │ + // from the frame and set it to null to make sure initImage │ │ │ │ + // will call getImage. │ │ │ │ + │ │ │ │ + if (this.imgDiv && this.imgDiv.parentNode === this.frame) { │ │ │ │ + this.frame.removeChild(this.imgDiv); │ │ │ │ + } │ │ │ │ + this.imgDiv = null; │ │ │ │ + │ │ │ │ + // And if we had an iframe we also remove the event pane. │ │ │ │ + │ │ │ │ + if (fromIFrame) { │ │ │ │ + this.frame.removeChild(this.frame.firstChild); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createBackBuffer │ │ │ │ - * The UTFGrid cannot create a back buffer, so this method is overriden. │ │ │ │ + * Method: getImage │ │ │ │ + * Creates the content for the frame on the tile. │ │ │ │ */ │ │ │ │ - createBackBuffer: function() {}, │ │ │ │ + getImage: function() { │ │ │ │ + if (this.useIFrame === true) { │ │ │ │ + if (!this.frame.childNodes.length) { │ │ │ │ + var eventPane = document.createElement("div"), │ │ │ │ + style = eventPane.style; │ │ │ │ + style.position = "absolute"; │ │ │ │ + style.width = "100%"; │ │ │ │ + style.height = "100%"; │ │ │ │ + style.zIndex = 1; │ │ │ │ + style.backgroundImage = "url(" + this.blankImageUrl + ")"; │ │ │ │ + this.frame.appendChild(eventPane); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var id = this.id + '_iFrame', │ │ │ │ + iframe; │ │ │ │ + if (parseFloat(navigator.appVersion.split("MSIE")[1]) < 9) { │ │ │ │ + // Older IE versions do not set the name attribute of an iFrame │ │ │ │ + // properly via DOM manipulation, so we need to do it on our own with │ │ │ │ + // this hack. │ │ │ │ + iframe = document.createElement('<iframe name="' + id + '">'); │ │ │ │ + │ │ │ │ + // IFrames in older IE versions are not transparent, if you set │ │ │ │ + // the backgroundColor transparent. This is a workaround to get │ │ │ │ + // transparent iframes. │ │ │ │ + iframe.style.backgroundColor = '#FFFFFF'; │ │ │ │ + iframe.style.filter = 'chroma(color=#FFFFFF)'; │ │ │ │ + } else { │ │ │ │ + iframe = document.createElement('iframe'); │ │ │ │ + iframe.style.backgroundColor = 'transparent'; │ │ │ │ + │ │ │ │ + // iframe.name needs to be an unique id, otherwise it │ │ │ │ + // could happen that other iframes are overwritten. │ │ │ │ + iframe.name = id; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // some special properties to avoid scaling the images and scrollbars │ │ │ │ + // in the iframe │ │ │ │ + iframe.scrolling = 'no'; │ │ │ │ + iframe.marginWidth = '0px'; │ │ │ │ + iframe.marginHeight = '0px'; │ │ │ │ + iframe.frameBorder = '0'; │ │ │ │ + │ │ │ │ + iframe.style.position = "absolute"; │ │ │ │ + iframe.style.width = "100%"; │ │ │ │ + iframe.style.height = "100%"; │ │ │ │ + │ │ │ │ + if (this.layer.opacity < 1) { │ │ │ │ + OpenLayers.Util.modifyDOMElement(iframe, null, null, null, │ │ │ │ + null, null, null, this.layer.opacity); │ │ │ │ + } │ │ │ │ + this.frame.appendChild(iframe); │ │ │ │ + this.imgDiv = iframe; │ │ │ │ + return iframe; │ │ │ │ + } else { │ │ │ │ + return OpenLayers.Tile.Image.prototype.getImage.apply(this, arguments); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ + * Method: createRequestForm │ │ │ │ + * Create the html <form> element with width, height, bbox and all │ │ │ │ + * parameters specified in the layer params. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {Object} Only used by a subclass of this layer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.UTFGrid>} An exact clone of this OpenLayers.Layer.UTFGrid │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The form element which sends the HTTP-POST request to the │ │ │ │ + * WMS. │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.UTFGrid(this.getOptions()); │ │ │ │ - } │ │ │ │ + createRequestForm: function() { │ │ │ │ + // creation of the form element │ │ │ │ + var form = document.createElement('form'); │ │ │ │ + form.method = 'POST'; │ │ │ │ + var cacheId = this.layer.params["_OLSALT"]; │ │ │ │ + cacheId = (cacheId ? cacheId + "_" : "") + this.bounds.toBBOX(); │ │ │ │ + form.action = OpenLayers.Util.urlAppend(this.layer.url, cacheId); │ │ │ │ + form.target = this.id + '_iFrame'; │ │ │ │ │ │ │ │ - // get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + // adding all parameters in layer params as hidden fields to the html │ │ │ │ + // form element │ │ │ │ + var imageSize = this.layer.getImageSize(), │ │ │ │ + params = OpenLayers.Util.getParameters(this.url), │ │ │ │ + field; │ │ │ │ │ │ │ │ - return obj; │ │ │ │ + for (var par in params) { │ │ │ │ + field = document.createElement('input'); │ │ │ │ + field.type = 'hidden'; │ │ │ │ + field.name = par; │ │ │ │ + field.value = params[par]; │ │ │ │ + form.appendChild(field); │ │ │ │ + } │ │ │ │ + │ │ │ │ + return form; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: getFeatureInfo │ │ │ │ - * Get details about a feature associated with a map location. The object │ │ │ │ - * returned will have id and data properties. If the given location │ │ │ │ - * doesn't correspond to a feature, null will be returned. │ │ │ │ + * Method: setImgSrc │ │ │ │ + * Sets the source for the tile image │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * location - {<OpenLayers.LonLat>} map location │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Object representing the feature id and UTFGrid data │ │ │ │ - * corresponding to the given map location. Returns null if the given │ │ │ │ - * location doesn't hit a feature. │ │ │ │ + * url - {String} │ │ │ │ */ │ │ │ │ - getFeatureInfo: function(location) { │ │ │ │ - var info = null; │ │ │ │ - var tileInfo = this.getTileData(location); │ │ │ │ - if (tileInfo && tileInfo.tile) { │ │ │ │ - info = tileInfo.tile.getFeatureInfo(tileInfo.i, tileInfo.j); │ │ │ │ + setImgSrc: function(url) { │ │ │ │ + if (this.useIFrame === true) { │ │ │ │ + if (url) { │ │ │ │ + var form = this.createRequestForm(); │ │ │ │ + this.frame.appendChild(form); │ │ │ │ + form.submit(); │ │ │ │ + this.frame.removeChild(form); │ │ │ │ + } else if (this.imgDiv.parentNode === this.frame) { │ │ │ │ + // we don't reuse iframes to avoid caching issues │ │ │ │ + this.frame.removeChild(this.imgDiv); │ │ │ │ + this.imgDiv = null; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + OpenLayers.Tile.Image.prototype.setImgSrc.apply(this, arguments); │ │ │ │ } │ │ │ │ - return info; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getFeatureId │ │ │ │ - * Get the identifier for the feature associated with a map location. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * location - {<OpenLayers.LonLat>} map location │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The feature identifier corresponding to the given map location. │ │ │ │ - * Returns null if the location doesn't hit a feature. │ │ │ │ + * Method: onImageLoad │ │ │ │ + * Handler for the image onload event │ │ │ │ */ │ │ │ │ - getFeatureId: function(location) { │ │ │ │ - var id = null; │ │ │ │ - var info = this.getTileData(location); │ │ │ │ - if (info.tile) { │ │ │ │ - id = info.tile.getFeatureId(info.i, info.j); │ │ │ │ + onImageLoad: function() { │ │ │ │ + //TODO de-uglify opacity handling │ │ │ │ + OpenLayers.Tile.Image.prototype.onImageLoad.apply(this, arguments); │ │ │ │ + if (this.useIFrame === true) { │ │ │ │ + this.imgDiv.style.opacity = 1; │ │ │ │ + this.frame.style.opacity = this.layer.opacity; │ │ │ │ } │ │ │ │ - return id; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.UTFGrid" │ │ │ │ -}); │ │ │ │ + /** │ │ │ │ + * Method: createBackBuffer │ │ │ │ + * Override createBackBuffer to do nothing when we use an iframe. Moving an │ │ │ │ + * iframe from one element to another makes it necessary to reload the iframe │ │ │ │ + * because its content is lost. So we just give up. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + createBackBuffer: function() { │ │ │ │ + var backBuffer; │ │ │ │ + if (this.useIFrame === false) { │ │ │ │ + backBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer.call(this); │ │ │ │ + } │ │ │ │ + return backBuffer; │ │ │ │ + } │ │ │ │ +}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/PointGrid.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/Layer/Vector.js │ │ │ │ - * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ + * @requires OpenLayers/Renderer.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.PointGrid │ │ │ │ - * A point grid layer dynamically generates a regularly spaced grid of point │ │ │ │ - * features. This is a specialty layer for cases where an application needs │ │ │ │ - * a regular grid of points. It can be used, for example, in an editing │ │ │ │ - * environment to snap to a grid. │ │ │ │ - * │ │ │ │ - * Create a new vector layer with the <OpenLayers.Layer.PointGrid> constructor. │ │ │ │ - * (code) │ │ │ │ - * // create a grid with points spaced at 10 map units │ │ │ │ - * var points = new OpenLayers.Layer.PointGrid({dx: 10, dy: 10}); │ │ │ │ - * │ │ │ │ - * // create a grid with different x/y spacing rotated 15 degrees clockwise. │ │ │ │ - * var points = new OpenLayers.Layer.PointGrid({dx: 5, dy: 10, rotation: 15}); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Vector> │ │ │ │ + * Class: OpenLayers.Renderer.Canvas │ │ │ │ + * A renderer based on the 2D 'canvas' drawing element. │ │ │ │ + * │ │ │ │ + * Inherits: │ │ │ │ + * - <OpenLayers.Renderer> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: dx │ │ │ │ - * {Number} Point grid spacing in the x-axis direction (map units). │ │ │ │ - * Read-only. Use the <setSpacing> method to modify this value. │ │ │ │ - */ │ │ │ │ - dx: null, │ │ │ │ +OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: dy │ │ │ │ - * {Number} Point grid spacing in the y-axis direction (map units). │ │ │ │ - * Read-only. Use the <setSpacing> method to modify this value. │ │ │ │ + * APIProperty: hitDetection │ │ │ │ + * {Boolean} Allow for hit detection of features. Default is true. │ │ │ │ */ │ │ │ │ - dy: null, │ │ │ │ + hitDetection: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: ratio │ │ │ │ - * {Number} Ratio of the desired grid size to the map viewport size. │ │ │ │ - * Default is 1.5. Larger ratios mean the grid is recalculated less often │ │ │ │ - * while panning. The <maxFeatures> setting has precedence when determining │ │ │ │ - * grid size. Read-only. Use the <setRatio> method to modify this value. │ │ │ │ + * 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). │ │ │ │ */ │ │ │ │ - ratio: 1.5, │ │ │ │ + hitOverflow: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: maxFeatures │ │ │ │ - * {Number} The maximum number of points to generate in the grid. Default │ │ │ │ - * is 250. Read-only. Use the <setMaxFeatures> method to modify this value. │ │ │ │ + * Property: canvas │ │ │ │ + * {Canvas} The canvas context object. │ │ │ │ */ │ │ │ │ - maxFeatures: 250, │ │ │ │ + canvas: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: rotation │ │ │ │ - * {Number} Grid rotation (in degrees clockwise from the positive x-axis). │ │ │ │ - * Default is 0. Read-only. Use the <setRotation> method to modify this │ │ │ │ - * value. │ │ │ │ + * Property: features │ │ │ │ + * {Object} Internal object of feature/style pairs for use in redrawing the layer. │ │ │ │ */ │ │ │ │ - rotation: 0, │ │ │ │ + features: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: origin │ │ │ │ - * {<OpenLayers.LonLat>} Grid origin. The grid lattice will be aligned with │ │ │ │ - * the origin. If not set at construction, the center of the map's maximum │ │ │ │ - * extent is used. Read-only. Use the <setOrigin> method to modify this │ │ │ │ - * value. │ │ │ │ + * Property: pendingRedraw │ │ │ │ + * {Boolean} The renderer needs a redraw call to render features added while │ │ │ │ + * the renderer was locked. │ │ │ │ */ │ │ │ │ - origin: null, │ │ │ │ + pendingRedraw: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: gridBounds │ │ │ │ - * {<OpenLayers.Bounds>} Internally cached grid bounds (with optional │ │ │ │ - * rotation applied). │ │ │ │ + * Property: cachedSymbolBounds │ │ │ │ + * {Object} Internal cache of calculated symbol extents. │ │ │ │ */ │ │ │ │ - gridBounds: null, │ │ │ │ + cachedSymbolBounds: {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.PointGrid │ │ │ │ - * Creates a new point grid layer. │ │ │ │ + * Constructor: OpenLayers.Renderer.Canvas │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * config - {Object} An object containing all configuration properties for │ │ │ │ - * the layer. The <dx> and <dy> properties are required to be set at │ │ │ │ - * construction. Any other layer properties may be set in this object. │ │ │ │ - */ │ │ │ │ - initialize: function(config) { │ │ │ │ - config = config || {}; │ │ │ │ - OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config]); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * The layer has been added to the map. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * containerID - {<String>} │ │ │ │ + * options - {Object} Optional properties to be set on the renderer. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); │ │ │ │ - map.events.register("moveend", this, this.onMoveEnd); │ │ │ │ + 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: removeMap │ │ │ │ - * The layer has been removed from the map. │ │ │ │ + * Method: setExtent │ │ │ │ + * Set the visible part of the layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * extent - {<OpenLayers.Bounds>} │ │ │ │ + * resolutionChanged - {Boolean} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true to notify the layer that the new extent does not exceed │ │ │ │ + * the coordinate range, and the features will not need to be redrawn. │ │ │ │ + * False otherwise. │ │ │ │ */ │ │ │ │ - removeMap: function(map) { │ │ │ │ - map.events.unregister("moveend", this, this.onMoveEnd); │ │ │ │ - OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments); │ │ │ │ + setExtent: function() { │ │ │ │ + OpenLayers.Renderer.prototype.setExtent.apply(this, arguments); │ │ │ │ + // always redraw features │ │ │ │ + return false; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: setRatio │ │ │ │ - * Set the grid <ratio> property and update the grid. Can only be called │ │ │ │ - * after the layer has been added to a map with a center/extent. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * 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: │ │ │ │ - * ratio - {Number} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * featureId - {String} │ │ │ │ */ │ │ │ │ - setRatio: function(ratio) { │ │ │ │ - this.ratio = ratio; │ │ │ │ - this.updateGrid(true); │ │ │ │ + eraseGeometry: function(geometry, featureId) { │ │ │ │ + this.eraseFeatures(this.features[featureId][0]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setMaxFeatures │ │ │ │ - * Set the grid <maxFeatures> property and update the grid. Can only be │ │ │ │ - * called after the layer has been added to a map with a center/extent. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * maxFeatures - {Number} │ │ │ │ + * APIMethod: supported │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Whether or not the browser supports the renderer class │ │ │ │ */ │ │ │ │ - setMaxFeatures: function(maxFeatures) { │ │ │ │ - this.maxFeatures = maxFeatures; │ │ │ │ - this.updateGrid(true); │ │ │ │ + supported: function() { │ │ │ │ + return OpenLayers.CANVAS_SUPPORTED; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setSpacing │ │ │ │ - * Set the grid <dx> and <dy> properties and update the grid. If only one │ │ │ │ - * argument is provided, it will be set as <dx> and <dy>. Can only be │ │ │ │ - * called after the layer has been added to a map with a center/extent. │ │ │ │ + * Method: setSize │ │ │ │ + * Sets the size of the drawing surface. │ │ │ │ + * │ │ │ │ + * Once the size is updated, redraw the canvas. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * dx - {Number} │ │ │ │ - * dy - {Number} │ │ │ │ + * size - {<OpenLayers.Size>} │ │ │ │ */ │ │ │ │ - setSpacing: function(dx, dy) { │ │ │ │ - this.dx = dx; │ │ │ │ - this.dy = dy || dx; │ │ │ │ - this.updateGrid(true); │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setOrigin │ │ │ │ - * Set the grid <origin> property and update the grid. Can only be called │ │ │ │ - * after the layer has been added to a map with a center/extent. │ │ │ │ + * Method: drawFeature │ │ │ │ + * Draw the feature. Stores the feature in the features list, │ │ │ │ + * then redraws the layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * origin - {<OpenLayers.LonLat>} │ │ │ │ - */ │ │ │ │ - setOrigin: function(origin) { │ │ │ │ - this.origin = origin; │ │ │ │ - this.updateGrid(true); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getOrigin │ │ │ │ - * Get the grid <origin> property. │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * style - {<Object>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} The grid origin. │ │ │ │ + * {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. │ │ │ │ */ │ │ │ │ - getOrigin: function() { │ │ │ │ - if (!this.origin) { │ │ │ │ - this.origin = this.map.getExtent().getCenterLonLat(); │ │ │ │ - } │ │ │ │ - return this.origin; │ │ │ │ - }, │ │ │ │ + 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(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: setRotation │ │ │ │ - * Set the grid <rotation> property and update the grid. Rotation values │ │ │ │ - * are in degrees clockwise from the positive x-axis (negative values │ │ │ │ - * for counter-clockwise rotation). Can only be called after the layer │ │ │ │ - * has been added to a map with a center/extent. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * rotation - {Number} Degrees clockwise from the positive x-axis. │ │ │ │ - */ │ │ │ │ - setRotation: function(rotation) { │ │ │ │ - this.rotation = rotation; │ │ │ │ - this.updateGrid(true); │ │ │ │ - }, │ │ │ │ + var worldBounds; │ │ │ │ + if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) { │ │ │ │ + worldBounds = this.map.getMaxExtent(); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: onMoveEnd │ │ │ │ - * Listener for map "moveend" events. │ │ │ │ - */ │ │ │ │ - onMoveEnd: function() { │ │ │ │ - this.updateGrid(); │ │ │ │ - }, │ │ │ │ + var intersects = bounds && bounds.intersectsBounds(this.extent, { │ │ │ │ + worldBounds: worldBounds │ │ │ │ + }); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getViewBounds │ │ │ │ - * Gets the (potentially rotated) view bounds for grid calculations. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} │ │ │ │ - */ │ │ │ │ - getViewBounds: function() { │ │ │ │ - var bounds = this.map.getExtent(); │ │ │ │ - if (this.rotation) { │ │ │ │ - var origin = this.getOrigin(); │ │ │ │ - var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat); │ │ │ │ - var rect = bounds.toGeometry(); │ │ │ │ - rect.rotate(-this.rotation, rotationOrigin); │ │ │ │ - bounds = rect.getBounds(); │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - return bounds; │ │ │ │ + if (this.pendingRedraw && !this.locked) { │ │ │ │ + this.redraw(); │ │ │ │ + this.pendingRedraw = false; │ │ │ │ + } │ │ │ │ + return rendered; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: updateGrid │ │ │ │ - * Update the grid. │ │ │ │ + /** │ │ │ │ + * Method: drawGeometry │ │ │ │ + * Used when looping (in redraw) over the features; draws │ │ │ │ + * the canvas. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * force - {Boolean} Update the grid even if the previous bounds are still │ │ │ │ - * valid. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ */ │ │ │ │ - updateGrid: function(force) { │ │ │ │ - if (force || this.invalidBounds()) { │ │ │ │ - var viewBounds = this.getViewBounds(); │ │ │ │ - var origin = this.getOrigin(); │ │ │ │ - var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat); │ │ │ │ - var viewBoundsWidth = viewBounds.getWidth(); │ │ │ │ - var viewBoundsHeight = viewBounds.getHeight(); │ │ │ │ - var aspectRatio = viewBoundsWidth / viewBoundsHeight; │ │ │ │ - var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio); │ │ │ │ - var maxWidth = maxHeight * aspectRatio; │ │ │ │ - var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth); │ │ │ │ - var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight); │ │ │ │ - var center = viewBounds.getCenterLonLat(); │ │ │ │ - this.gridBounds = new OpenLayers.Bounds( │ │ │ │ - center.lon - (gridWidth / 2), │ │ │ │ - center.lat - (gridHeight / 2), │ │ │ │ - center.lon + (gridWidth / 2), │ │ │ │ - center.lat + (gridHeight / 2) │ │ │ │ - ); │ │ │ │ - var rows = Math.floor(gridHeight / this.dy); │ │ │ │ - var cols = Math.floor(gridWidth / this.dx); │ │ │ │ - var gridLeft = origin.lon + (this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx)); │ │ │ │ - var gridBottom = origin.lat + (this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy)); │ │ │ │ - var features = new Array(rows * cols); │ │ │ │ - var x, y, point; │ │ │ │ - for (var i = 0; i < cols; ++i) { │ │ │ │ - x = gridLeft + (i * this.dx); │ │ │ │ - for (var j = 0; j < rows; ++j) { │ │ │ │ - y = gridBottom + (j * this.dy); │ │ │ │ - point = new OpenLayers.Geometry.Point(x, y); │ │ │ │ - if (this.rotation) { │ │ │ │ - point.rotate(this.rotation, rotationOrigin); │ │ │ │ - } │ │ │ │ - features[(i * rows) + j] = new OpenLayers.Feature.Vector(point); │ │ │ │ - } │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - this.destroyFeatures(this.features, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.addFeatures(features, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ + 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: invalidBounds │ │ │ │ - * Determine whether the previously generated point grid is invalid. │ │ │ │ - * This occurs when the map bounds extends beyond the previously │ │ │ │ - * generated grid bounds. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} │ │ │ │ + * Method: drawExternalGraphic │ │ │ │ + * Called to draw External graphics. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {String} │ │ │ │ */ │ │ │ │ - invalidBounds: function() { │ │ │ │ - return !this.gridBounds || !this.gridBounds.containsBounds(this.getViewBounds()); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.PointGrid" │ │ │ │ - │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/Zoomify.js │ │ │ │ - ====================================================================== */ │ │ │ │ + drawExternalGraphic: function(geometry, style, featureId) { │ │ │ │ + var img = new Image(); │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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 title = style.title || style.graphicTitle; │ │ │ │ + if (title) { │ │ │ │ + img.title = title; │ │ │ │ + } │ │ │ │ │ │ │ │ -/* │ │ │ │ - * Development supported by a R&D grant DC08P02OUK006 - Old Maps Online │ │ │ │ - * (www.oldmapsonline.org) from Ministry of Culture of the Czech Republic. │ │ │ │ - */ │ │ │ │ + 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; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/Grid.js │ │ │ │ - */ │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.Zoomify │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + img.onload = OpenLayers.Function.bind(onLoad, this); │ │ │ │ + img.src = style.externalGraphic; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: size │ │ │ │ - * {<OpenLayers.Size>} The Zoomify image size in pixels. │ │ │ │ + * Method: drawNamedSymbol │ │ │ │ + * Called to draw Well Known Graphic Symbol Name. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {String} │ │ │ │ */ │ │ │ │ - size: null, │ │ │ │ + drawNamedSymbol: function(geometry, style, featureId) { │ │ │ │ + var x, y, cx, cy, i, symbolBounds, scaling, angle; │ │ │ │ + var unscaledStrokeWidth; │ │ │ │ + var deg2rad = Math.PI / 180.0; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} │ │ │ │ - */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + var symbol = OpenLayers.Renderer.symbol[style.graphicName]; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: standardTileSize │ │ │ │ - * {Integer} The size of a standard (non-border) square tile in pixels. │ │ │ │ - */ │ │ │ │ - standardTileSize: 256, │ │ │ │ + if (!symbol) { │ │ │ │ + throw new Error(style.graphicName + ' is not a valid symbol name'); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: tileOriginCorner │ │ │ │ - * {String} This layer uses top-left as tile origin │ │ │ │ - **/ │ │ │ │ - tileOriginCorner: "tl", │ │ │ │ + if (!symbol.length || symbol.length < 2) return; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: numberOfTiers │ │ │ │ - * {Integer} Depth of the Zoomify pyramid, number of tiers (zoom levels) │ │ │ │ - * - filled during Zoomify pyramid initialization. │ │ │ │ - */ │ │ │ │ - numberOfTiers: 0, │ │ │ │ + var pt = this.getLocalXY(geometry); │ │ │ │ + var p0 = pt[0]; │ │ │ │ + var p1 = pt[1]; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: tileCountUpToTier │ │ │ │ - * {Array(Integer)} Number of tiles up to the given tier of pyramid. │ │ │ │ - * - filled during Zoomify pyramid initialization. │ │ │ │ - */ │ │ │ │ - tileCountUpToTier: null, │ │ │ │ + if (isNaN(p0) || isNaN(p1)) return; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: tierSizeInTiles │ │ │ │ - * {Array(<OpenLayers.Size>)} Size (in tiles) for each tier of pyramid. │ │ │ │ - * - filled during Zoomify pyramid initialization. │ │ │ │ - */ │ │ │ │ - tierSizeInTiles: null, │ │ │ │ + // Use rounded line caps │ │ │ │ + this.canvas.lineCap = "round"; │ │ │ │ + this.canvas.lineJoin = "round"; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: tierImageSize │ │ │ │ - * {Array(<OpenLayers.Size>)} Image size in pixels for each pyramid tier. │ │ │ │ - * - filled during Zoomify pyramid initialization. │ │ │ │ - */ │ │ │ │ - tierImageSize: null, │ │ │ │ + if (this.hitDetection) { │ │ │ │ + this.hitContext.lineCap = "round"; │ │ │ │ + this.hitContext.lineJoin = "round"; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Layer.Zoomify │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} A name for the layer. │ │ │ │ - * url - {String} - Relative or absolute path to the image or more │ │ │ │ - * precisly to the TileGroup[X] directories root. │ │ │ │ - * Flash plugin use the variable name "zoomifyImagePath" for this. │ │ │ │ - * size - {<OpenLayers.Size>} The size (in pixels) of the image. │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ - */ │ │ │ │ - initialize: function(name, url, size, options) { │ │ │ │ + // 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; │ │ │ │ + } │ │ │ │ │ │ │ │ - // initilize the Zoomify pyramid for given size │ │ │ │ - this.initializeZoomify(size); │ │ │ │ + // 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(); │ │ │ │ + } │ │ │ │ │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, [ │ │ │ │ - name, url, size, {}, │ │ │ │ - options │ │ │ │ - ]); │ │ │ │ - }, │ │ │ │ + // Step 3: place symbol at the desired location │ │ │ │ + this.canvas.translate(p0, p1); │ │ │ │ + if (this.hitDetection) { │ │ │ │ + this.hitContext.translate(p0, p1); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: initializeZoomify │ │ │ │ - * It generates constants for all tiers of the Zoomify pyramid │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * size - {<OpenLayers.Size>} The size of the image in pixels │ │ │ │ - * │ │ │ │ - */ │ │ │ │ - initializeZoomify: function(size) { │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - var imageSize = size.clone(); │ │ │ │ - this.size = size.clone(); │ │ │ │ - var tiles = new OpenLayers.Size( │ │ │ │ - Math.ceil(imageSize.w / this.standardTileSize), │ │ │ │ - Math.ceil(imageSize.h / this.standardTileSize) │ │ │ │ - ); │ │ │ │ + // // 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); │ │ │ │ + } │ │ │ │ │ │ │ │ - this.tierSizeInTiles = [tiles]; │ │ │ │ - this.tierImageSize = [imageSize]; │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ │ │ │ │ - while (imageSize.w > this.standardTileSize || │ │ │ │ - imageSize.h > this.standardTileSize) { │ │ │ │ + // 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; │ │ │ │ │ │ │ │ - imageSize = new OpenLayers.Size( │ │ │ │ - Math.floor(imageSize.w / 2), │ │ │ │ - Math.floor(imageSize.h / 2) │ │ │ │ - ); │ │ │ │ - tiles = new OpenLayers.Size( │ │ │ │ - Math.ceil(imageSize.w / this.standardTileSize), │ │ │ │ - Math.ceil(imageSize.h / this.standardTileSize) │ │ │ │ - ); │ │ │ │ - this.tierSizeInTiles.push(tiles); │ │ │ │ - this.tierImageSize.push(imageSize); │ │ │ │ + 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(); │ │ │ │ + } │ │ │ │ } │ │ │ │ │ │ │ │ - this.tierSizeInTiles.reverse(); │ │ │ │ - this.tierImageSize.reverse(); │ │ │ │ + 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(); │ │ │ │ │ │ │ │ - this.numberOfTiers = this.tierSizeInTiles.length; │ │ │ │ - var resolutions = [1]; │ │ │ │ - this.tileCountUpToTier = [0]; │ │ │ │ - for (var i = 1; i < this.numberOfTiers; i++) { │ │ │ │ - resolutions.unshift(Math.pow(2, i)); │ │ │ │ - this.tileCountUpToTier.push( │ │ │ │ - this.tierSizeInTiles[i - 1].w * this.tierSizeInTiles[i - 1].h + │ │ │ │ - this.tileCountUpToTier[i - 1] │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (!this.serverResolutions) { │ │ │ │ - this.serverResolutions = resolutions; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod:destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - // for now, nothing special to do here. │ │ │ │ - OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments); │ │ │ │ + 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(); │ │ │ │ + } │ │ │ │ │ │ │ │ - // Remove from memory the Zoomify pyramid - is that enough? │ │ │ │ - this.tileCountUpToTier.length = 0; │ │ │ │ - this.tierSizeInTiles.length = 0; │ │ │ │ - this.tierImageSize.length = 0; │ │ │ │ + } │ │ │ │ │ │ │ │ + style.strokeWidth = unscaledStrokeWidth; │ │ │ │ + this.canvas.restore(); │ │ │ │ + if (this.hitDetection) { │ │ │ │ + this.hitContext.restore(); │ │ │ │ + } │ │ │ │ + this.setCanvasStyle("reset"); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ + * Method: setCanvasStyle │ │ │ │ + * Prepare the canvas for drawing by setting various global settings. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * obj - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.Zoomify>} An exact clone of this <OpenLayers.Layer.Zoomify> │ │ │ │ + * type - {String} one of 'stroke', 'fill', or 'reset' │ │ │ │ + * style - {Object} Symbolizer hash │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Zoomify(this.name, │ │ │ │ - this.url, │ │ │ │ - this.size, │ │ │ │ - this.options); │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - │ │ │ │ - return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ + * Method: featureIdToHex │ │ │ │ + * Convert a feature ID string into an RGB hex string. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * featureId - {String} Feature id │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A string with the layer's url and parameters and also the │ │ │ │ - * passed-in bounds and appropriate tile size specified as │ │ │ │ - * parameters │ │ │ │ + * {String} RGB hex string. │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); │ │ │ │ - var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h)); │ │ │ │ - var z = this.getZoomForResolution(res); │ │ │ │ - │ │ │ │ - var tileIndex = x + y * this.tierSizeInTiles[z].w + this.tileCountUpToTier[z]; │ │ │ │ - var path = "TileGroup" + Math.floor((tileIndex) / 256) + │ │ │ │ - "/" + z + "-" + x + "-" + y + ".jpg"; │ │ │ │ - var url = this.url; │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(path, url); │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - return url + path; │ │ │ │ + var hex = "000000" + id.toString(16); │ │ │ │ + var len = hex.length; │ │ │ │ + hex = "#" + hex.substring(len - 6, len); │ │ │ │ + return hex; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getImageSize │ │ │ │ - * getImageSize returns size for a particular tile. If bounds are given as │ │ │ │ - * first argument, size is calculated (bottom-right tiles are non square). │ │ │ │ + * Method: setHitContextStyle │ │ │ │ + * Prepare the hit canvas for drawing by setting various global settings. │ │ │ │ * │ │ │ │ + * Parameters: │ │ │ │ + * type - {String} one of 'stroke', 'fill', or 'reset' │ │ │ │ + * featureId - {String} The feature id. │ │ │ │ + * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer. │ │ │ │ */ │ │ │ │ - getImageSize: function() { │ │ │ │ - if (arguments.length > 0) { │ │ │ │ - var bounds = this.adjustBounds(arguments[0]); │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); │ │ │ │ - var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h)); │ │ │ │ - var z = this.getZoomForResolution(res); │ │ │ │ - var w = this.standardTileSize; │ │ │ │ - var h = this.standardTileSize; │ │ │ │ - if (x == this.tierSizeInTiles[z].w - 1) { │ │ │ │ - var w = this.tierImageSize[z].w % this.standardTileSize; │ │ │ │ - } │ │ │ │ - if (y == this.tierSizeInTiles[z].h - 1) { │ │ │ │ - var h = this.tierImageSize[z].h % this.standardTileSize; │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ } │ │ │ │ - return (new OpenLayers.Size(w, h)); │ │ │ │ } else { │ │ │ │ - return this.tileSize; │ │ │ │ + this.hitContext.globalAlpha = 0; │ │ │ │ + this.hitContext.lineWidth = 1; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setMap │ │ │ │ - * When the layer is added to a map, then we can fetch our origin │ │ │ │ - * (if we don't have one.) │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * Method: drawPoint │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {String} │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ - this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, │ │ │ │ - this.map.maxExtent.top); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Zoomify" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/TMS.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/Grid.js │ │ │ │ - */ │ │ │ │ + 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(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.TMS │ │ │ │ - * Create a layer for accessing tiles from services that conform with the │ │ │ │ - * Tile Map Service Specification │ │ │ │ - * (http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification). │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * var layer = new OpenLayers.Layer.TMS( │ │ │ │ - * "My Layer", // name for display in LayerSwitcher │ │ │ │ - * "http://tilecache.osgeo.org/wms-c/Basic.py/", // service endpoint │ │ │ │ - * {layername: "basic", type: "png"} // required properties │ │ │ │ - * ); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + 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"); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: serviceVersion │ │ │ │ - * {String} Service version for tile requests. Default is "1.0.0". │ │ │ │ + * Method: drawLineString │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {String} │ │ │ │ */ │ │ │ │ - serviceVersion: "1.0.0", │ │ │ │ + drawLineString: function(geometry, style, featureId) { │ │ │ │ + style = OpenLayers.Util.applyDefaults({ │ │ │ │ + fill: false │ │ │ │ + }, style); │ │ │ │ + this.drawLinearRing(geometry, style, featureId); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: layername │ │ │ │ - * {String} The identifier for the <TileMap> as advertised by the service. │ │ │ │ - * For example, if the service advertises a <TileMap> with │ │ │ │ - * 'href="http://tms.osgeo.org/1.0.0/vmap0"', the <layername> property │ │ │ │ - * would be set to "vmap0". │ │ │ │ + * Method: drawLinearRing │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {String} │ │ │ │ */ │ │ │ │ - layername: null, │ │ │ │ + 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"); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: type │ │ │ │ - * {String} The format extension corresponding to the requested tile image │ │ │ │ - * type. This is advertised in a <TileFormat> element as the │ │ │ │ - * "extension" attribute. For example, if the service advertises a │ │ │ │ - * <TileMap> with <TileFormat width="256" height="256" mime-type="image/jpeg" extension="jpg" />, │ │ │ │ - * the <type> property would be set to "jpg". │ │ │ │ + * Method: renderPath │ │ │ │ + * Render a path with stroke and optional fill. │ │ │ │ */ │ │ │ │ - type: null, │ │ │ │ + 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(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} Make this layer a base layer. Default is true. Set false to │ │ │ │ - * use the layer as an overlay. │ │ │ │ + * Method: drawPolygon │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {String} │ │ │ │ */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + 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 │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: tileOrigin │ │ │ │ - * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles. │ │ │ │ - * If provided, requests for tiles at all resolutions will be aligned │ │ │ │ - * with this location (no tiles shall overlap this location). If │ │ │ │ - * not provided, the grid of tiles will be aligned with the bottom-left │ │ │ │ - * corner of the map's <maxExtent>. Default is ``null``. │ │ │ │ + * Method: drawText │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * var layer = new OpenLayers.Layer.TMS( │ │ │ │ - * "My Layer", │ │ │ │ - * "http://tilecache.osgeo.org/wms-c/Basic.py/", │ │ │ │ - * { │ │ │ │ - * layername: "basic", │ │ │ │ - * type: "png", │ │ │ │ - * // set if different than the bottom left of map.maxExtent │ │ │ │ - * tileOrigin: new OpenLayers.LonLat(-180, -90) │ │ │ │ - * } │ │ │ │ - * ); │ │ │ │ - * (end) │ │ │ │ + * Parameters: │ │ │ │ + * location - {<OpenLayers.Point>} │ │ │ │ + * style - {Object} │ │ │ │ */ │ │ │ │ - tileOrigin: null, │ │ │ │ + drawText: function(location, style) { │ │ │ │ + var pt = this.getLocalXY(location); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: serverResolutions │ │ │ │ - * {Array} A list of all resolutions available on the server. Only set this │ │ │ │ - * property if the map resolutions differ from the server. This │ │ │ │ - * property serves two purposes. (a) <serverResolutions> can include │ │ │ │ - * resolutions that the server supports and that you don't want to │ │ │ │ - * provide with this layer; you can also look at <zoomOffset>, which is │ │ │ │ - * an alternative to <serverResolutions> for that specific purpose. │ │ │ │ - * (b) The map can work with resolutions that aren't supported by │ │ │ │ - * the server, i.e. that aren't in <serverResolutions>. When the │ │ │ │ - * map is displayed in such a resolution data for the closest │ │ │ │ - * server-supported resolution is loaded and the layer div is │ │ │ │ - * stretched as necessary. │ │ │ │ - */ │ │ │ │ - serverResolutions: null, │ │ │ │ + this.setCanvasStyle("reset"); │ │ │ │ + this.canvas.fillStyle = style.fontColor; │ │ │ │ + this.canvas.globalAlpha = style.fontOpacity || 1.0; │ │ │ │ + var fontStyle = [style.fontStyle ? style.fontStyle : "normal", │ │ │ │ + "normal", // "font-variant" not supported │ │ │ │ + style.fontWeight ? style.fontWeight : "normal", │ │ │ │ + style.fontSize ? style.fontSize : "1em", │ │ │ │ + style.fontFamily ? style.fontFamily : "sans-serif" │ │ │ │ + ].join(" "); │ │ │ │ + var labelRows = style.label.split('\n'); │ │ │ │ + var numRows = labelRows.length; │ │ │ │ + if (this.canvas.fillText) { │ │ │ │ + // HTML5 │ │ │ │ + this.canvas.font = fontStyle; │ │ │ │ + this.canvas.textAlign = │ │ │ │ + OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] || │ │ │ │ + "center"; │ │ │ │ + this.canvas.textBaseline = │ │ │ │ + OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] || │ │ │ │ + "middle"; │ │ │ │ + var vfactor = │ │ │ │ + OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]]; │ │ │ │ + if (vfactor == null) { │ │ │ │ + vfactor = -.5; │ │ │ │ + } │ │ │ │ + var lineHeight = │ │ │ │ + this.canvas.measureText('Mg').height || │ │ │ │ + this.canvas.measureText('xx').width; │ │ │ │ + pt[1] += lineHeight * vfactor * (numRows - 1); │ │ │ │ + for (var i = 0; i < numRows; i++) { │ │ │ │ + if (style.labelOutlineWidth) { │ │ │ │ + this.canvas.save(); │ │ │ │ + this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0; │ │ │ │ + this.canvas.strokeStyle = style.labelOutlineColor; │ │ │ │ + this.canvas.lineWidth = style.labelOutlineWidth; │ │ │ │ + this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight * i) + 1); │ │ │ │ + this.canvas.restore(); │ │ │ │ + } │ │ │ │ + this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight * i)); │ │ │ │ + } │ │ │ │ + } else if (this.canvas.mozDrawText) { │ │ │ │ + // Mozilla pre-Gecko1.9.1 (<FF3.1) │ │ │ │ + this.canvas.mozTextStyle = fontStyle; │ │ │ │ + // No built-in text alignment, so we measure and adjust the position │ │ │ │ + var hfactor = │ │ │ │ + OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]]; │ │ │ │ + if (hfactor == null) { │ │ │ │ + hfactor = -.5; │ │ │ │ + } │ │ │ │ + var vfactor = │ │ │ │ + OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]]; │ │ │ │ + if (vfactor == null) { │ │ │ │ + vfactor = -.5; │ │ │ │ + } │ │ │ │ + var lineHeight = this.canvas.mozMeasureText('xx'); │ │ │ │ + pt[1] += lineHeight * (1 + (vfactor * numRows)); │ │ │ │ + for (var i = 0; i < numRows; i++) { │ │ │ │ + var x = pt[0] + (hfactor * this.canvas.mozMeasureText(labelRows[i])); │ │ │ │ + var y = pt[1] + (i * lineHeight); │ │ │ │ + this.canvas.translate(x, y); │ │ │ │ + this.canvas.mozDrawText(labelRows[i]); │ │ │ │ + this.canvas.translate(-x, -y); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.setCanvasStyle("reset"); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: zoomOffset │ │ │ │ - * {Number} If your cache has more zoom levels than you want to provide │ │ │ │ - * access to with this layer, supply a zoomOffset. This zoom offset │ │ │ │ - * is added to the current map zoom level to determine the level │ │ │ │ - * for a requested tile. For example, if you supply a zoomOffset │ │ │ │ - * of 3, when the map is at the zoom 0, tiles will be requested from │ │ │ │ - * level 3 of your cache. Default is 0 (assumes cache level and map │ │ │ │ - * zoom are equivalent). Using <zoomOffset> is an alternative to │ │ │ │ - * setting <serverResolutions> if you only want to expose a subset │ │ │ │ - * of the server resolutions. │ │ │ │ + * Method: getLocalXY │ │ │ │ + * transform geographic xy into pixel xy │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} │ │ │ │ */ │ │ │ │ - zoomOffset: 0, │ │ │ │ + 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]; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.TMS │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} Title to be displayed in a <OpenLayers.Control.LayerSwitcher> │ │ │ │ - * url - {String} Service endpoint (without the version number). E.g. │ │ │ │ - * "http://tms.osgeo.org/". │ │ │ │ - * options - {Object} Additional properties to be set on the layer. The │ │ │ │ - * <layername> and <type> properties must be set here. │ │ │ │ + * Method: clear │ │ │ │ + * Clear all vectors from the renderer. │ │ │ │ */ │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - var newArguments = []; │ │ │ │ - newArguments.push(name, url, {}, options); │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Create a complete copy of this layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {Object} Should only be provided by subclasses that call this │ │ │ │ - * method. │ │ │ │ + * Method: getFeatureIdFromEvent │ │ │ │ + * Returns a feature id from an event on the renderer. │ │ │ │ * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer.TMS>} An exact clone of this <OpenLayers.Layer.TMS> │ │ │ │ + * {<OpenLayers.Feature.Vector} A feature or undefined. This method returns a │ │ │ │ + * feature instead of a feature id to avoid an unnecessary lookup on the │ │ │ │ + * layer. │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ + getFeatureIdFromEvent: function(evt) { │ │ │ │ + var featureId, feature; │ │ │ │ │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.TMS(this.name, │ │ │ │ - this.url, │ │ │ │ - this.getOptions()); │ │ │ │ + if (this.hitDetection && this.root.style.display !== "none") { │ │ │ │ + // this dragging check should go in the feature handler │ │ │ │ + if (!this.map.dragging) { │ │ │ │ + var xy = evt.xy; │ │ │ │ + var x = xy.x | 0; │ │ │ │ + var y = xy.y | 0; │ │ │ │ + var data = this.hitContext.getImageData(x, y, 1, 1).data; │ │ │ │ + if (data[3] === 255) { // antialiased │ │ │ │ + var id = data[2] + (256 * (data[1] + (256 * data[0]))); │ │ │ │ + if (id) { │ │ │ │ + featureId = "OpenLayers_Feature_Vector_" + (id - 1 + this.hitOverflow); │ │ │ │ + try { │ │ │ │ + feature = this.features[featureId][0]; │ │ │ │ + } catch (err) { │ │ │ │ + // Because of antialiasing on the canvas, when the hit location is at a point where the edge of │ │ │ │ + // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results. │ │ │ │ + // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it. │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - │ │ │ │ - return obj; │ │ │ │ + return feature; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ + * Method: eraseFeatures │ │ │ │ + * This is called by the layer to erase features; removes the feature from │ │ │ │ + * the list, then redraws the layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A string with the layer's url and parameters and also the │ │ │ │ - * passed-in bounds and appropriate tile size specified as │ │ │ │ - * parameters │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); │ │ │ │ - var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h)); │ │ │ │ - var z = this.getServerZoom(); │ │ │ │ - var path = this.serviceVersion + "/" + this.layername + "/" + z + "/" + x + "/" + y + "." + this.type; │ │ │ │ - var url = this.url; │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(path, url); │ │ │ │ + eraseFeatures: function(features) { │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ + features = [features]; │ │ │ │ } │ │ │ │ - return url + path; │ │ │ │ + for (var i = 0; i < features.length; ++i) { │ │ │ │ + delete this.features[features[i].id]; │ │ │ │ + } │ │ │ │ + this.redraw(); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * When the layer is added to a map, then we can fetch our origin │ │ │ │ - * (if we don't have one.) │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ - if (!this.tileOrigin) { │ │ │ │ - this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, │ │ │ │ - this.map.maxExtent.bottom); │ │ │ │ + redraw: function() { │ │ │ │ + if (!this.locked) { │ │ │ │ + var height = this.root.height; │ │ │ │ + var width = this.root.width; │ │ │ │ + this.canvas.clearRect(0, 0, width, height); │ │ │ │ + if (this.hitDetection) { │ │ │ │ + this.hitContext.clearRect(0, 0, width, height); │ │ │ │ + } │ │ │ │ + var labelMap = []; │ │ │ │ + var feature, geometry, style; │ │ │ │ + var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent(); │ │ │ │ + for (var id in this.features) { │ │ │ │ + if (!this.features.hasOwnProperty(id)) { │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ + feature = this.features[id][0]; │ │ │ │ + geometry = feature.geometry; │ │ │ │ + this.calculateFeatureDx(geometry.getBounds(), worldBounds); │ │ │ │ + style = this.features[id][1]; │ │ │ │ + this.drawGeometry(geometry, style, feature.id); │ │ │ │ + if (style.label) { │ │ │ │ + labelMap.push([feature, style]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var item; │ │ │ │ + for (var i = 0, len = labelMap.length; i < len; ++i) { │ │ │ │ + item = labelMap[i]; │ │ │ │ + this.drawText(item[0].geometry.getCentroid(), item[1]); │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.TMS" │ │ │ │ + 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/Layer/WMS.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/Layer/Grid.js │ │ │ │ + * @requires OpenLayers/Renderer.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.WMS │ │ │ │ - * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web │ │ │ │ - * Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS> │ │ │ │ - * constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ + * 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.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ +OpenLayers.ElementsIndexer = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: DEFAULT_PARAMS │ │ │ │ - * {Object} Hashtable of default parameter key/value pairs │ │ │ │ + * Property: maxZIndex │ │ │ │ + * {Integer} This is the largest-most z-index value for a node │ │ │ │ + * contained within the indexer. │ │ │ │ */ │ │ │ │ - DEFAULT_PARAMS: { │ │ │ │ - service: "WMS", │ │ │ │ - version: "1.1.1", │ │ │ │ - request: "GetMap", │ │ │ │ - styles: "", │ │ │ │ - format: "image/jpeg" │ │ │ │ - }, │ │ │ │ + maxZIndex: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} Default is true for WMS layer │ │ │ │ + * Property: order │ │ │ │ + * {Array<String>} This is an array of node id's stored in the │ │ │ │ + * order that they should show up on screen. Id's higher up in the │ │ │ │ + * array (higher array index) represent nodes with higher z-indeces. │ │ │ │ */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + order: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: encodeBBOX │ │ │ │ - * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', │ │ │ │ - * but some services want it that way. Default false. │ │ │ │ + * 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). │ │ │ │ */ │ │ │ │ - encodeBBOX: false, │ │ │ │ + indices: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: noMagic │ │ │ │ - * {Boolean} If true, the image format will not be automagicaly switched │ │ │ │ - * from image/jpeg to image/png or image/gif when using │ │ │ │ - * TRANSPARENT=TRUE. Also isBaseLayer will not changed by the │ │ │ │ - * constructor. Default false. │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - noMagic: false, │ │ │ │ + compare: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: yx │ │ │ │ - * {Object} Keys in this object are EPSG codes for which the axis order │ │ │ │ - * is to be reversed (yx instead of xy, LatLon instead of LonLat), with │ │ │ │ - * true as value. This is only relevant for WMS versions >= 1.3.0, and │ │ │ │ - * only if yx is not set in <OpenLayers.Projection.defaults> for the │ │ │ │ - * used projection. │ │ │ │ + * APIMethod: initialize │ │ │ │ + * Create a new indexer with │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * yOrdering - {Boolean} Whether to use y-ordering. │ │ │ │ */ │ │ │ │ - yx: {}, │ │ │ │ + initialize: function(yOrdering) { │ │ │ │ + │ │ │ │ + this.compare = yOrdering ? │ │ │ │ + OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : │ │ │ │ + OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER; │ │ │ │ + │ │ │ │ + this.clear(); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.WMS │ │ │ │ - * Create a new WMS layer object │ │ │ │ - * │ │ │ │ - * Examples: │ │ │ │ - * │ │ │ │ - * The code below creates a simple WMS layer using the image/jpeg format. │ │ │ │ - * (code) │ │ │ │ - * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic", │ │ │ │ - * "http://wms.jpl.nasa.gov/wms.cgi", │ │ │ │ - * {layers: "modis,global_mosaic"}); │ │ │ │ - * (end) │ │ │ │ - * Note the 3rd argument (params). Properties added to this object will be │ │ │ │ - * added to the WMS GetMap requests used for this layer's tiles. The only │ │ │ │ - * mandatory parameter is "layers". Other common WMS params include │ │ │ │ - * "transparent", "styles" and "format". Note that the "srs" param will │ │ │ │ - * always be ignored. Instead, it will be derived from the baseLayer's or │ │ │ │ - * map's projection. │ │ │ │ - * │ │ │ │ - * The code below creates a transparent WMS layer with additional options. │ │ │ │ - * (code) │ │ │ │ - * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic", │ │ │ │ - * "http://wms.jpl.nasa.gov/wms.cgi", │ │ │ │ - * { │ │ │ │ - * layers: "modis,global_mosaic", │ │ │ │ - * transparent: true │ │ │ │ - * }, { │ │ │ │ - * opacity: 0.5, │ │ │ │ - * singleTile: true │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ - * Note that by default, a WMS layer is configured as baseLayer. Setting │ │ │ │ - * the "transparent" param to true will apply some magic (see <noMagic>). │ │ │ │ - * The default image format changes from image/jpeg to image/png, and the │ │ │ │ - * layer is not configured as baseLayer. │ │ │ │ - * │ │ │ │ + * 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: │ │ │ │ - * name - {String} A name for the layer │ │ │ │ - * url - {String} Base url for the WMS │ │ │ │ - * (e.g. http://wms.jpl.nasa.gov/wms.cgi) │ │ │ │ - * params - {Object} An object with key/value pairs representing the │ │ │ │ - * GetMap query string parameters and parameter values. │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer. │ │ │ │ - * These options include all properties listed above, plus the ones │ │ │ │ - * inherited from superclasses. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - var newArguments = []; │ │ │ │ - //uppercase params │ │ │ │ - params = OpenLayers.Util.upperCaseObject(params); │ │ │ │ - if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) { │ │ │ │ - params.EXCEPTIONS = "INIMAGE"; │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - newArguments.push(name, url, params, options); │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ - OpenLayers.Util.applyDefaults( │ │ │ │ - this.params, │ │ │ │ - OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS) │ │ │ │ - ); │ │ │ │ │ │ │ │ + var nodeId = newNode.id; │ │ │ │ │ │ │ │ - //layer is transparent │ │ │ │ - if (!this.noMagic && this.params.TRANSPARENT && │ │ │ │ - this.params.TRANSPARENT.toString().toLowerCase() == "true") { │ │ │ │ + this.determineZIndex(newNode); │ │ │ │ │ │ │ │ - // unless explicitly set in options, make layer an overlay │ │ │ │ - if ((options == null) || (!options.isBaseLayer)) { │ │ │ │ - this.isBaseLayer = false; │ │ │ │ - } │ │ │ │ + var leftIndex = -1; │ │ │ │ + var rightIndex = this.order.length; │ │ │ │ + var middle; │ │ │ │ │ │ │ │ - // jpegs can never be transparent, so intelligently switch the │ │ │ │ - // format, depending on the browser's capabilities │ │ │ │ - if (this.params.FORMAT == "image/jpeg") { │ │ │ │ - this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif" : │ │ │ │ - "image/png"; │ │ │ │ + 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); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.WMS>} An exact clone of this layer │ │ │ │ + * APIMethod: remove │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} The node to be removed. │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ + 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]; │ │ │ │ │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.WMS(this.name, │ │ │ │ - this.url, │ │ │ │ - this.params, │ │ │ │ - this.getOptions()); │ │ │ │ + // 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; │ │ │ │ + } │ │ │ │ } │ │ │ │ - │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - │ │ │ │ - return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: reverseAxisOrder │ │ │ │ - * Returns true if the axis order is reversed for the WMS version and │ │ │ │ - * projection of the layer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} true if the axis order is reversed, false otherwise. │ │ │ │ + * APIMethod: clear │ │ │ │ */ │ │ │ │ - reverseAxisOrder: function() { │ │ │ │ - var projCode = this.projection.getCode(); │ │ │ │ - return parseFloat(this.params.VERSION) >= 1.3 && │ │ │ │ - !!(this.yx[projCode] || (OpenLayers.Projection.defaults[projCode] && │ │ │ │ - OpenLayers.Projection.defaults[projCode].yx)); │ │ │ │ + clear: function() { │ │ │ │ + this.order = []; │ │ │ │ + this.indices = {}; │ │ │ │ + this.maxZIndex = 0; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ - * Return a GetMap query string for this layer │ │ │ │ + * APIMethod: exists │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the │ │ │ │ - * request. │ │ │ │ + * node - {DOMElement} The node to test for existence. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A string with the layer's url and parameters and also the │ │ │ │ - * passed-in bounds and appropriate tile size specified as │ │ │ │ - * parameters. │ │ │ │ + * {Boolean} Whether or not the node exists in the indexer? │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - │ │ │ │ - var imageSize = this.getImageSize(); │ │ │ │ - var newParams = {}; │ │ │ │ - // WMS 1.3 introduced axis order │ │ │ │ - var reverseAxisOrder = this.reverseAxisOrder(); │ │ │ │ - newParams.BBOX = this.encodeBBOX ? │ │ │ │ - bounds.toBBOX(null, reverseAxisOrder) : │ │ │ │ - bounds.toArray(reverseAxisOrder); │ │ │ │ - newParams.WIDTH = imageSize.w; │ │ │ │ - newParams.HEIGHT = imageSize.h; │ │ │ │ - var requestString = this.getFullRequestString(newParams); │ │ │ │ - return requestString; │ │ │ │ + exists: function(node) { │ │ │ │ + return (this.indices[node.id] != null); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: mergeNewParams │ │ │ │ - * Catch changeParams and uppercase the new params to be merged in │ │ │ │ - * before calling changeParams on the super class. │ │ │ │ + * APIMethod: getZIndex │ │ │ │ + * Get the z-index value for the current node from the node data itself. │ │ │ │ * │ │ │ │ - * Once params have been changed, the tiles will be reloaded with │ │ │ │ - * the new parameters. │ │ │ │ + * 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: │ │ │ │ - * newParams - {Object} Hashtable of new params to use │ │ │ │ + * node - {DOMElement} │ │ │ │ */ │ │ │ │ - mergeNewParams: function(newParams) { │ │ │ │ - var upperParams = OpenLayers.Util.upperCaseObject(newParams); │ │ │ │ - var newArguments = [upperParams]; │ │ │ │ - return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, │ │ │ │ - newArguments); │ │ │ │ + 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: getFullRequestString │ │ │ │ - * Combine the layer's url with its params and these newParams. │ │ │ │ - * │ │ │ │ - * Add the SRS parameter from projection -- this is probably │ │ │ │ - * more eloquently done via a setProjection() method, but this │ │ │ │ - * works for now and always. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * APIMethod: getNextElement │ │ │ │ + * Get the next element in the order stack. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * newParams - {Object} │ │ │ │ - * altUrl - {String} Use this as the url instead of the layer's url │ │ │ │ + * index - {Integer} The index of the current node in this.order. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} │ │ │ │ + * {DOMElement} the node following the index passed in, or │ │ │ │ + * null. │ │ │ │ */ │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ - var mapProjection = this.map.getProjectionObject(); │ │ │ │ - var projectionCode = this.projection && this.projection.equals(mapProjection) ? │ │ │ │ - this.projection.getCode() : │ │ │ │ - mapProjection.getCode(); │ │ │ │ - var value = (projectionCode == "none") ? null : projectionCode; │ │ │ │ - if (parseFloat(this.params.VERSION) >= 1.3) { │ │ │ │ - this.params.CRS = value; │ │ │ │ + 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 { │ │ │ │ - this.params.SRS = value; │ │ │ │ + return null; │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (typeof this.params.TRANSPARENT == "boolean") { │ │ │ │ - newParams.TRANSPARENT = this.params.TRANSPARENT ? "TRUE" : "FALSE"; │ │ │ │ + CLASS_NAME: "OpenLayers.ElementsIndexer" │ │ │ │ +}); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Namespace: OpenLayers.ElementsIndexer.IndexingMethods │ │ │ │ + * These are the compare methods for figuring out where a new node should be │ │ │ │ + * placed within the indexer. These methods are very similar to general │ │ │ │ + * sorting methods in that they return -1, 0, and 1 to specify the │ │ │ │ + * direction in which new nodes fall in the ordering. │ │ │ │ + */ │ │ │ │ +OpenLayers.ElementsIndexer.IndexingMethods = { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: Z_ORDER │ │ │ │ + * This compare method is used by other comparison methods. │ │ │ │ + * It can be used individually for ordering, but is not recommended, │ │ │ │ + * because it doesn't subscribe to drawing order. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * indexer - {<OpenLayers.ElementsIndexer>} │ │ │ │ + * newNode - {DOMElement} │ │ │ │ + * nextNode - {DOMElement} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} │ │ │ │ + */ │ │ │ │ + Z_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ + var newZIndex = indexer.getZIndex(newNode); │ │ │ │ + │ │ │ │ + var returnVal = 0; │ │ │ │ + if (nextNode) { │ │ │ │ + var nextZIndex = indexer.getZIndex(nextNode); │ │ │ │ + returnVal = newZIndex - nextZIndex; │ │ │ │ } │ │ │ │ │ │ │ │ - return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply( │ │ │ │ - this, arguments); │ │ │ │ + return returnVal; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.WMS" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/ArcXML.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * APIMethod: Z_ORDER_DRAWING_ORDER │ │ │ │ + * This method orders nodes by their z-index, but does so in a way │ │ │ │ + * that, if there are other nodes with the same z-index, the newest │ │ │ │ + * drawn will be the front most within that z-index. This is the │ │ │ │ + * default indexing method. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * indexer - {<OpenLayers.ElementsIndexer>} │ │ │ │ + * newNode - {DOMElement} │ │ │ │ + * nextNode - {DOMElement} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} │ │ │ │ + */ │ │ │ │ + Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ + var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER( │ │ │ │ + indexer, │ │ │ │ + newNode, │ │ │ │ + nextNode │ │ │ │ + ); │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + // 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; │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ - * @requires OpenLayers/Geometry/Point.js │ │ │ │ - * @requires OpenLayers/Geometry/MultiPolygon.js │ │ │ │ - * @requires OpenLayers/Geometry/LinearRing.js │ │ │ │ - */ │ │ │ │ + return returnVal; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: Z_ORDER_Y_ORDER │ │ │ │ + * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it │ │ │ │ + * best describes which ordering methods have precedence (though, the │ │ │ │ + * name would be too long). This method orders nodes by their z-index, │ │ │ │ + * but does so in a way that, if there are other nodes with the same │ │ │ │ + * z-index, the nodes with the lower y position will be "closer" than │ │ │ │ + * those with a higher y position. If two nodes have the exact same y │ │ │ │ + * position, however, then this method will revert to using drawing │ │ │ │ + * order to decide placement. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * indexer - {<OpenLayers.ElementsIndexer>} │ │ │ │ + * newNode - {DOMElement} │ │ │ │ + * nextNode - {DOMElement} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} │ │ │ │ + */ │ │ │ │ + Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ + var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER( │ │ │ │ + indexer, │ │ │ │ + newNode, │ │ │ │ + nextNode │ │ │ │ + ); │ │ │ │ + │ │ │ │ + if (nextNode && returnVal === 0) { │ │ │ │ + var result = nextNode._boundsBottom - newNode._boundsBottom; │ │ │ │ + returnVal = (result === 0) ? 1 : result; │ │ │ │ + } │ │ │ │ + │ │ │ │ + return returnVal; │ │ │ │ + } │ │ │ │ +}; │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.ArcXML │ │ │ │ - * Read/Write ArcXML. Create a new instance with the <OpenLayers.Format.ArcXML> │ │ │ │ - * constructor. │ │ │ │ + * 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. │ │ │ │ * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * The highlevel bits of code that are implemented here are the adding and │ │ │ │ + * removing of geometries, which is essentially the same for any │ │ │ │ + * element-based renderer. The details of creating each node and drawing the │ │ │ │ + * paths are of course different, but the machinery is the same. │ │ │ │ + * │ │ │ │ + * Inherits: │ │ │ │ + * - <OpenLayers.Renderer> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.ArcXML = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ +OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: fontStyleKeys │ │ │ │ - * {Array} List of keys used in font styling. │ │ │ │ + * Property: rendererRoot │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - fontStyleKeys: [ │ │ │ │ - 'antialiasing', 'blockout', 'font', 'fontcolor', 'fontsize', 'fontstyle', │ │ │ │ - 'glowing', 'interval', 'outline', 'printmode', 'shadow', 'transparency' │ │ │ │ - ], │ │ │ │ + rendererRoot: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: request │ │ │ │ - * A get_image request destined for an ArcIMS server. │ │ │ │ + * Property: root │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - request: null, │ │ │ │ + root: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: response │ │ │ │ - * A parsed response from an ArcIMS server. │ │ │ │ + * Property: vectorRoot │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - response: null, │ │ │ │ + vectorRoot: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.ArcXML │ │ │ │ - * Create a new parser/writer for ArcXML. Create an instance of this class │ │ │ │ - * to begin authoring a request to an ArcIMS service. This is used │ │ │ │ - * primarily by the ArcIMS layer, but could be used to do other wild │ │ │ │ - * stuff, like geocoding. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * Property: textRoot │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - this.request = new OpenLayers.Format.ArcXML.Request(); │ │ │ │ - this.response = new OpenLayers.Format.ArcXML.Response(); │ │ │ │ + textRoot: null, │ │ │ │ │ │ │ │ - if (options) { │ │ │ │ - if (options.requesttype == "feature") { │ │ │ │ - this.request.get_image = null; │ │ │ │ + /** │ │ │ │ + * Property: xmlns │ │ │ │ + * {String} │ │ │ │ + */ │ │ │ │ + xmlns: null, │ │ │ │ │ │ │ │ - var qry = this.request.get_feature.query; │ │ │ │ - this.addCoordSys(qry.featurecoordsys, options.featureCoordSys); │ │ │ │ - this.addCoordSys(qry.filtercoordsys, options.filterCoordSys); │ │ │ │ + /** │ │ │ │ + * 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, │ │ │ │ │ │ │ │ - if (options.polygon) { │ │ │ │ - qry.isspatial = true; │ │ │ │ - qry.spatialfilter.polygon = options.polygon; │ │ │ │ - } else if (options.envelope) { │ │ │ │ - qry.isspatial = true; │ │ │ │ - qry.spatialfilter.envelope = { │ │ │ │ - minx: 0, │ │ │ │ - miny: 0, │ │ │ │ - maxx: 0, │ │ │ │ - maxy: 0 │ │ │ │ - }; │ │ │ │ - this.parseEnvelope(qry.spatialfilter.envelope, options.envelope); │ │ │ │ - } │ │ │ │ - } else if (options.requesttype == "image") { │ │ │ │ - this.request.get_feature = null; │ │ │ │ + /** │ │ │ │ + * Property: rightOfDateLine │ │ │ │ + * {Boolean} Keeps track of the location of the map extent relative to the │ │ │ │ + * date line. The <setExtent> method compares this value (which is the one │ │ │ │ + * from the previous <setExtent> call) with the current position of the map │ │ │ │ + * extent relative to the date line and updates the xOffset when the extent │ │ │ │ + * has moved from one side of the date line to the other. │ │ │ │ + */ │ │ │ │ │ │ │ │ - var props = this.request.get_image.properties; │ │ │ │ - this.parseEnvelope(props.envelope, options.envelope); │ │ │ │ + /** │ │ │ │ + * Property: Indexer │ │ │ │ + * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer │ │ │ │ + * created upon initialization if the zIndexing or yOrdering options │ │ │ │ + * passed to this renderer's constructor are set to true. │ │ │ │ + */ │ │ │ │ + indexer: null, │ │ │ │ │ │ │ │ - this.addLayers(props.layerlist, options.layers); │ │ │ │ - this.addImageSize(props.imagesize, options.tileSize); │ │ │ │ - this.addCoordSys(props.featurecoordsys, options.featureCoordSys); │ │ │ │ - this.addCoordSys(props.filtercoordsys, options.filterCoordSys); │ │ │ │ - } else { │ │ │ │ - // if an arcxml object is being created with no request type, it is │ │ │ │ - // probably going to consume a response, so do not throw an error if │ │ │ │ - // the requesttype is not defined │ │ │ │ - this.request = null; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Constant: BACKGROUND_ID_SUFFIX │ │ │ │ + * {String} │ │ │ │ + */ │ │ │ │ + BACKGROUND_ID_SUFFIX: "_background", │ │ │ │ │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Constant: LABEL_ID_SUFFIX │ │ │ │ + * {String} │ │ │ │ + */ │ │ │ │ + LABEL_ID_SUFFIX: "_label", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseEnvelope │ │ │ │ - * Parse an array of coordinates into an ArcXML envelope structure. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * env - {Object} An envelope object that will contain the parsed coordinates. │ │ │ │ - * arr - {Array(double)} An array of coordinates in the order: [ minx, miny, maxx, maxy ] │ │ │ │ + * Constant: LABEL_OUTLINE_SUFFIX │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - parseEnvelope: function(env, arr) { │ │ │ │ - if (arr && arr.length == 4) { │ │ │ │ - env.minx = arr[0]; │ │ │ │ - env.miny = arr[1]; │ │ │ │ - env.maxx = arr[2]; │ │ │ │ - env.maxy = arr[3]; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + LABEL_OUTLINE_SUFFIX: "_outline", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: addLayers │ │ │ │ - * Add a collection of layers to another collection of layers. Each layer in the list is tuple of │ │ │ │ - * { id, visible }. These layer collections represent the │ │ │ │ - * /ARCXML/REQUEST/get_image/PROPERTIES/LAYERLIST/LAYERDEF items in ArcXML │ │ │ │ - * │ │ │ │ - * TODO: Add support for dynamic layer rendering. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Renderer.Elements │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * ll - {Array({id,visible})} A list of layer definitions. │ │ │ │ - * lyrs - {Array({id,visible})} A list of layer definitions. │ │ │ │ + * containerID - {String} │ │ │ │ + * options - {Object} options for this renderer. │ │ │ │ + * │ │ │ │ + * Supported options are: │ │ │ │ + * yOrdering - {Boolean} Whether to use y-ordering │ │ │ │ + * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored │ │ │ │ + * if yOrdering is set to true. │ │ │ │ */ │ │ │ │ - addLayers: function(ll, lyrs) { │ │ │ │ - for (var lind = 0, len = lyrs.length; lind < len; lind++) { │ │ │ │ - ll.push(lyrs[lind]); │ │ │ │ + initialize: function(containerID, options) { │ │ │ │ + OpenLayers.Renderer.prototype.initialize.apply(this, arguments); │ │ │ │ + │ │ │ │ + this.rendererRoot = this.createRenderRoot(); │ │ │ │ + this.root = this.createRoot("_root"); │ │ │ │ + this.vectorRoot = this.createRoot("_vroot"); │ │ │ │ + this.textRoot = this.createRoot("_troot"); │ │ │ │ + │ │ │ │ + this.root.appendChild(this.vectorRoot); │ │ │ │ + this.root.appendChild(this.textRoot); │ │ │ │ + │ │ │ │ + this.rendererRoot.appendChild(this.root); │ │ │ │ + this.container.appendChild(this.rendererRoot); │ │ │ │ + │ │ │ │ + if (options && (options.zIndexing || options.yOrdering)) { │ │ │ │ + this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: addImageSize │ │ │ │ - * Set the size of the requested image. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * imsize - {Object} An ArcXML imagesize object. │ │ │ │ - * olsize - {<OpenLayers.Size>} The image size to set. │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - addImageSize: function(imsize, olsize) { │ │ │ │ - if (olsize !== null) { │ │ │ │ - imsize.width = olsize.w; │ │ │ │ - imsize.height = olsize.h; │ │ │ │ - imsize.printwidth = olsize.w; │ │ │ │ - imsize.printheight = olsize.h; │ │ │ │ - } │ │ │ │ + destroy: function() { │ │ │ │ + │ │ │ │ + this.clear(); │ │ │ │ + │ │ │ │ + this.rendererRoot = null; │ │ │ │ + this.root = null; │ │ │ │ + this.xmlns = null; │ │ │ │ + │ │ │ │ + OpenLayers.Renderer.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: addCoordSys │ │ │ │ - * Add the coordinate system information to an object. The object may be │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * featOrFilt - {Object} A featurecoordsys or filtercoordsys ArcXML structure. │ │ │ │ - * fsys - {String} or {<OpenLayers.Projection>} or {filtercoordsys} or │ │ │ │ - * {featurecoordsys} A projection representation. If it's a {String}, │ │ │ │ - * the value is assumed to be the SRID. If it's a {OpenLayers.Projection} │ │ │ │ - * AND Proj4js is available, the projection number and name are extracted │ │ │ │ - * from there. If it's a filter or feature ArcXML structure, it is copied. │ │ │ │ + * Method: clear │ │ │ │ + * Remove all the elements from the root │ │ │ │ */ │ │ │ │ - addCoordSys: function(featOrFilt, fsys) { │ │ │ │ - if (typeof fsys == "string") { │ │ │ │ - featOrFilt.id = parseInt(fsys); │ │ │ │ - featOrFilt.string = fsys; │ │ │ │ + clear: function() { │ │ │ │ + var child; │ │ │ │ + var root = this.vectorRoot; │ │ │ │ + if (root) { │ │ │ │ + while (child = root.firstChild) { │ │ │ │ + root.removeChild(child); │ │ │ │ + } │ │ │ │ } │ │ │ │ - // is this a proj4js instance? │ │ │ │ - else if (typeof fsys == "object" && fsys.proj !== null) { │ │ │ │ - featOrFilt.id = fsys.proj.srsProjNumber; │ │ │ │ - featOrFilt.string = fsys.proj.srsCode; │ │ │ │ - } else { │ │ │ │ - featOrFilt = fsys; │ │ │ │ + root = this.textRoot; │ │ │ │ + if (root) { │ │ │ │ + while (child = root.firstChild) { │ │ │ │ + root.removeChild(child); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (this.indexer) { │ │ │ │ + this.indexer.clear(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: iserror │ │ │ │ - * Check to see if the response from the server was an error. │ │ │ │ + * Method: setExtent │ │ │ │ + * Set the visible part of the layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. If nothing is supplied, │ │ │ │ - * the current response is examined. │ │ │ │ + * extent - {<OpenLayers.Bounds>} │ │ │ │ + * resolutionChanged - {Boolean} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} true if the response was an error. │ │ │ │ + * {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. │ │ │ │ */ │ │ │ │ - iserror: function(data) { │ │ │ │ - var ret = null; │ │ │ │ - │ │ │ │ - if (!data) { │ │ │ │ - ret = (this.response.error !== ''); │ │ │ │ - } else { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - var errorNodes = data.documentElement.getElementsByTagName("ERROR"); │ │ │ │ - ret = (errorNodes !== null && errorNodes.length > 0); │ │ │ │ + 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 ret; │ │ │ │ + return coordSysUnchanged; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read data from a string, and return an response. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * 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: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The corresponding node type for the specified geometry │ │ │ │ + */ │ │ │ │ + getNodeType: function(geometry, style) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: drawGeometry │ │ │ │ + * Draw the geometry, creating new nodes, setting paths, setting style, │ │ │ │ + * setting featureId on the node. This method should only be called │ │ │ │ + * by the renderer itself. │ │ │ │ * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {String} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Format.ArcXML.Response>} An ArcXML response. Note that this response │ │ │ │ - * data may change in the future. │ │ │ │ + * {Boolean} true if the geometry has been drawn completely; null if │ │ │ │ + * incomplete; false otherwise │ │ │ │ */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + 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; │ │ │ │ } │ │ │ │ │ │ │ │ - var arcNode = null; │ │ │ │ - if (data && data.documentElement) { │ │ │ │ - if (data.documentElement.nodeName == "ARCXML") { │ │ │ │ - arcNode = data.documentElement; │ │ │ │ + rendered = false; │ │ │ │ + var removeBackground = false; │ │ │ │ + if (style.display != "none") { │ │ │ │ + if (style.backgroundGraphic) { │ │ │ │ + this.redrawBackgroundNode(geometry.id, geometry, style, │ │ │ │ + featureId); │ │ │ │ } else { │ │ │ │ - arcNode = data.documentElement.getElementsByTagName("ARCXML")[0]; │ │ │ │ + removeBackground = true; │ │ │ │ } │ │ │ │ + rendered = this.redrawNode(geometry.id, geometry, style, │ │ │ │ + featureId); │ │ │ │ } │ │ │ │ - │ │ │ │ - // in Safari, arcNode will be there but will have a child named │ │ │ │ - // parsererror │ │ │ │ - if (!arcNode || arcNode.firstChild.nodeName === 'parsererror') { │ │ │ │ - var error, source; │ │ │ │ - try { │ │ │ │ - error = data.firstChild.nodeValue; │ │ │ │ - source = data.firstChild.childNodes[1].firstChild.nodeValue; │ │ │ │ - } catch (err) { │ │ │ │ - // pass │ │ │ │ + if (rendered == false) { │ │ │ │ + var node = document.getElementById(geometry.id); │ │ │ │ + if (node) { │ │ │ │ + if (node._style.backgroundGraphic) { │ │ │ │ + removeBackground = true; │ │ │ │ + } │ │ │ │ + node.parentNode.removeChild(node); │ │ │ │ } │ │ │ │ - throw { │ │ │ │ - message: "Error parsing the ArcXML request", │ │ │ │ - error: error, │ │ │ │ - source: source │ │ │ │ - }; │ │ │ │ } │ │ │ │ - │ │ │ │ - var response = this.parseResponse(arcNode); │ │ │ │ - return response; │ │ │ │ + if (removeBackground) { │ │ │ │ + var node = document.getElementById( │ │ │ │ + geometry.id + this.BACKGROUND_ID_SUFFIX); │ │ │ │ + if (node) { │ │ │ │ + node.parentNode.removeChild(node); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return rendered; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Generate an ArcXml document string for sending to an ArcIMS server. │ │ │ │ + * Method: redrawNode │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * id - {String} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {String} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A string representing the ArcXML document request. │ │ │ │ + * {Boolean} true if the complete geometry could be drawn, null if parts of │ │ │ │ + * the geometry could not be drawn, false otherwise │ │ │ │ */ │ │ │ │ - write: function(request) { │ │ │ │ - if (!request) { │ │ │ │ - request = this.request; │ │ │ │ - } │ │ │ │ - var root = this.createElementNS("", "ARCXML"); │ │ │ │ - root.setAttribute("version", "1.1"); │ │ │ │ - │ │ │ │ - var reqElem = this.createElementNS("", "REQUEST"); │ │ │ │ - │ │ │ │ - if (request.get_image != null) { │ │ │ │ - var getElem = this.createElementNS("", "GET_IMAGE"); │ │ │ │ - reqElem.appendChild(getElem); │ │ │ │ - │ │ │ │ - var propElem = this.createElementNS("", "PROPERTIES"); │ │ │ │ - getElem.appendChild(propElem); │ │ │ │ + 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)); │ │ │ │ │ │ │ │ - var props = request.get_image.properties; │ │ │ │ - if (props.featurecoordsys != null) { │ │ │ │ - var feat = this.createElementNS("", "FEATURECOORDSYS"); │ │ │ │ - propElem.appendChild(feat); │ │ │ │ + // 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; │ │ │ │ │ │ │ │ - if (props.featurecoordsys.id === 0) { │ │ │ │ - feat.setAttribute("string", props.featurecoordsys['string']); │ │ │ │ - } else { │ │ │ │ - feat.setAttribute("id", props.featurecoordsys.id); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + var drawResult = this.drawGeometryNode(node, geometry, style); │ │ │ │ + if (drawResult === false) { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ │ │ │ │ - if (props.filtercoordsys != null) { │ │ │ │ - var filt = this.createElementNS("", "FILTERCOORDSYS"); │ │ │ │ - propElem.appendChild(filt); │ │ │ │ + node = drawResult.node; │ │ │ │ │ │ │ │ - if (props.filtercoordsys.id === 0) { │ │ │ │ - filt.setAttribute("string", props.filtercoordsys.string); │ │ │ │ - } else { │ │ │ │ - filt.setAttribute("id", props.filtercoordsys.id); │ │ │ │ - } │ │ │ │ + // 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); │ │ │ │ } │ │ │ │ - │ │ │ │ - if (props.envelope != null) { │ │ │ │ - var env = this.createElementNS("", "ENVELOPE"); │ │ │ │ - propElem.appendChild(env); │ │ │ │ - │ │ │ │ - env.setAttribute("minx", props.envelope.minx); │ │ │ │ - env.setAttribute("miny", props.envelope.miny); │ │ │ │ - env.setAttribute("maxx", props.envelope.maxx); │ │ │ │ - env.setAttribute("maxy", props.envelope.maxy); │ │ │ │ + } 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); │ │ │ │ } │ │ │ │ + } │ │ │ │ │ │ │ │ - var imagesz = this.createElementNS("", "IMAGESIZE"); │ │ │ │ - propElem.appendChild(imagesz); │ │ │ │ + this.postDraw(node); │ │ │ │ │ │ │ │ - imagesz.setAttribute("height", props.imagesize.height); │ │ │ │ - imagesz.setAttribute("width", props.imagesize.width); │ │ │ │ + return drawResult.complete; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (props.imagesize.height != props.imagesize.printheight || │ │ │ │ - props.imagesize.width != props.imagesize.printwidth) { │ │ │ │ - imagesz.setAttribute("printheight", props.imagesize.printheight); │ │ │ │ - imagesz.setArrtibute("printwidth", props.imagesize.printwidth); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (props.background != null) { │ │ │ │ - var backgrnd = this.createElementNS("", "BACKGROUND"); │ │ │ │ - propElem.appendChild(backgrnd); │ │ │ │ - │ │ │ │ - backgrnd.setAttribute("color", │ │ │ │ - props.background.color.r + "," + │ │ │ │ - props.background.color.g + "," + │ │ │ │ - props.background.color.b); │ │ │ │ - │ │ │ │ - if (props.background.transcolor !== null) { │ │ │ │ - backgrnd.setAttribute("transcolor", │ │ │ │ - props.background.transcolor.r + "," + │ │ │ │ - props.background.transcolor.g + "," + │ │ │ │ - props.background.transcolor.b); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (props.layerlist != null && props.layerlist.length > 0) { │ │ │ │ - var layerlst = this.createElementNS("", "LAYERLIST"); │ │ │ │ - propElem.appendChild(layerlst); │ │ │ │ - │ │ │ │ - for (var ld = 0; ld < props.layerlist.length; ld++) { │ │ │ │ - var ldef = this.createElementNS("", "LAYERDEF"); │ │ │ │ - layerlst.appendChild(ldef); │ │ │ │ - │ │ │ │ - ldef.setAttribute("id", props.layerlist[ld].id); │ │ │ │ - ldef.setAttribute("visible", props.layerlist[ld].visible); │ │ │ │ - │ │ │ │ - if (typeof props.layerlist[ld].query == "object") { │ │ │ │ - var query = props.layerlist[ld].query; │ │ │ │ - │ │ │ │ - if (query.where.length < 0) { │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var queryElem = null; │ │ │ │ - if (typeof query.spatialfilter == "boolean" && query.spatialfilter) { │ │ │ │ - // handle spatial filter madness │ │ │ │ - queryElem = this.createElementNS("", "SPATIALQUERY"); │ │ │ │ - } else { │ │ │ │ - queryElem = this.createElementNS("", "QUERY"); │ │ │ │ - } │ │ │ │ - │ │ │ │ - queryElem.setAttribute("where", query.where); │ │ │ │ - │ │ │ │ - if (typeof query.accuracy == "number" && query.accuracy > 0) { │ │ │ │ - queryElem.setAttribute("accuracy", query.accuracy); │ │ │ │ - } │ │ │ │ - if (typeof query.featurelimit == "number" && query.featurelimit < 2000) { │ │ │ │ - queryElem.setAttribute("featurelimit", query.featurelimit); │ │ │ │ - } │ │ │ │ - if (typeof query.subfields == "string" && query.subfields != "#ALL#") { │ │ │ │ - queryElem.setAttribute("subfields", query.subfields); │ │ │ │ - } │ │ │ │ - if (typeof query.joinexpression == "string" && query.joinexpression.length > 0) { │ │ │ │ - queryElem.setAttribute("joinexpression", query.joinexpression); │ │ │ │ - } │ │ │ │ - if (typeof query.jointables == "string" && query.jointables.length > 0) { │ │ │ │ - queryElem.setAttribute("jointables", query.jointables); │ │ │ │ - } │ │ │ │ - │ │ │ │ - ldef.appendChild(queryElem); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (typeof props.layerlist[ld].renderer == "object") { │ │ │ │ - this.addRenderer(ldef, props.layerlist[ld].renderer); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else if (request.get_feature != null) { │ │ │ │ - var getElem = this.createElementNS("", "GET_FEATURES"); │ │ │ │ - getElem.setAttribute("outputmode", "newxml"); │ │ │ │ - getElem.setAttribute("checkesc", "true"); │ │ │ │ - │ │ │ │ - if (request.get_feature.geometry) { │ │ │ │ - getElem.setAttribute("geometry", request.get_feature.geometry); │ │ │ │ - } else { │ │ │ │ - getElem.setAttribute("geometry", "false"); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (request.get_feature.compact) { │ │ │ │ - getElem.setAttribute("compact", request.get_feature.compact); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (request.get_feature.featurelimit == "number") { │ │ │ │ - getElem.setAttribute("featurelimit", request.get_feature.featurelimit); │ │ │ │ - } │ │ │ │ - │ │ │ │ - getElem.setAttribute("globalenvelope", "true"); │ │ │ │ - reqElem.appendChild(getElem); │ │ │ │ - │ │ │ │ - if (request.get_feature.layer != null && request.get_feature.layer.length > 0) { │ │ │ │ - var lyrElem = this.createElementNS("", "LAYER"); │ │ │ │ - lyrElem.setAttribute("id", request.get_feature.layer); │ │ │ │ - getElem.appendChild(lyrElem); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var fquery = request.get_feature.query; │ │ │ │ - if (fquery != null) { │ │ │ │ - var qElem = null; │ │ │ │ - if (fquery.isspatial) { │ │ │ │ - qElem = this.createElementNS("", "SPATIALQUERY"); │ │ │ │ - } else { │ │ │ │ - qElem = this.createElementNS("", "QUERY"); │ │ │ │ - } │ │ │ │ - getElem.appendChild(qElem); │ │ │ │ - │ │ │ │ - if (typeof fquery.accuracy == "number") { │ │ │ │ - qElem.setAttribute("accuracy", fquery.accuracy); │ │ │ │ - } │ │ │ │ - //qElem.setAttribute("featurelimit", "5"); │ │ │ │ - │ │ │ │ - if (fquery.featurecoordsys != null) { │ │ │ │ - var fcsElem1 = this.createElementNS("", "FEATURECOORDSYS"); │ │ │ │ - │ │ │ │ - if (fquery.featurecoordsys.id == 0) { │ │ │ │ - fcsElem1.setAttribute("string", fquery.featurecoordsys.string); │ │ │ │ - } else { │ │ │ │ - fcsElem1.setAttribute("id", fquery.featurecoordsys.id); │ │ │ │ - } │ │ │ │ - qElem.appendChild(fcsElem1); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (fquery.filtercoordsys != null) { │ │ │ │ - var fcsElem2 = this.createElementNS("", "FILTERCOORDSYS"); │ │ │ │ - │ │ │ │ - if (fquery.filtercoordsys.id === 0) { │ │ │ │ - fcsElem2.setAttribute("string", fquery.filtercoordsys.string); │ │ │ │ - } else { │ │ │ │ - fcsElem2.setAttribute("id", fquery.filtercoordsys.id); │ │ │ │ - } │ │ │ │ - qElem.appendChild(fcsElem2); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (fquery.buffer > 0) { │ │ │ │ - var bufElem = this.createElementNS("", "BUFFER"); │ │ │ │ - bufElem.setAttribute("distance", fquery.buffer); │ │ │ │ - qElem.appendChild(bufElem); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (fquery.isspatial) { │ │ │ │ - var spfElem = this.createElementNS("", "SPATIALFILTER"); │ │ │ │ - spfElem.setAttribute("relation", fquery.spatialfilter.relation); │ │ │ │ - qElem.appendChild(spfElem); │ │ │ │ - │ │ │ │ - if (fquery.spatialfilter.envelope) { │ │ │ │ - var envElem = this.createElementNS("", "ENVELOPE"); │ │ │ │ - envElem.setAttribute("minx", fquery.spatialfilter.envelope.minx); │ │ │ │ - envElem.setAttribute("miny", fquery.spatialfilter.envelope.miny); │ │ │ │ - envElem.setAttribute("maxx", fquery.spatialfilter.envelope.maxx); │ │ │ │ - envElem.setAttribute("maxy", fquery.spatialfilter.envelope.maxy); │ │ │ │ - spfElem.appendChild(envElem); │ │ │ │ - } else if (typeof fquery.spatialfilter.polygon == "object") { │ │ │ │ - spfElem.appendChild(this.writePolygonGeometry(fquery.spatialfilter.polygon)); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: redrawBackgroundNode │ │ │ │ + * Redraws the node using special 'background' style properties. Basically │ │ │ │ + * just calls redrawNode(), but instead of directly using the │ │ │ │ + * 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and │ │ │ │ + * 'graphicZIndex' properties directly from the specified 'style' │ │ │ │ + * parameter, we create a new style object and set those properties │ │ │ │ + * from the corresponding 'background'-prefixed properties from │ │ │ │ + * specified 'style' parameter. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * id - {String} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {String} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true if the complete geometry could be drawn, null if parts of │ │ │ │ + * the geometry could not be drawn, false otherwise │ │ │ │ + */ │ │ │ │ + redrawBackgroundNode: function(id, geometry, style, featureId) { │ │ │ │ + var backgroundStyle = OpenLayers.Util.extend({}, style); │ │ │ │ │ │ │ │ - if (fquery.where != null && fquery.where.length > 0) { │ │ │ │ - qElem.setAttribute("where", fquery.where); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + // 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; │ │ │ │ │ │ │ │ - root.appendChild(reqElem); │ │ │ │ + // Erase background styles. │ │ │ │ + backgroundStyle.backgroundGraphic = null; │ │ │ │ + backgroundStyle.backgroundXOffset = null; │ │ │ │ + backgroundStyle.backgroundYOffset = null; │ │ │ │ + backgroundStyle.backgroundGraphicZIndex = null; │ │ │ │ │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [root]); │ │ │ │ + return this.redrawNode( │ │ │ │ + id + this.BACKGROUND_ID_SUFFIX, │ │ │ │ + geometry, │ │ │ │ + backgroundStyle, │ │ │ │ + null │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Method: drawGeometryNode │ │ │ │ + * Given a node, draw a geometry on the specified layer. │ │ │ │ + * node and geometry are required arguments, style is optional. │ │ │ │ + * This method is only called by the render itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} a hash with properties "node" (the drawn node) and "complete" │ │ │ │ + * (null if parts of the geometry could not be drawn, false if nothing │ │ │ │ + * could be drawn) │ │ │ │ + */ │ │ │ │ + drawGeometryNode: function(node, geometry, style) { │ │ │ │ + style = style || node._style; │ │ │ │ │ │ │ │ - addGroupRenderer: function(ldef, toprenderer) { │ │ │ │ - var topRelem = this.createElementNS("", "GROUPRENDERER"); │ │ │ │ - ldef.appendChild(topRelem); │ │ │ │ - │ │ │ │ - for (var rind = 0; rind < toprenderer.length; rind++) { │ │ │ │ - var renderer = toprenderer[rind]; │ │ │ │ - this.addRenderer(topRelem, renderer); │ │ │ │ + var options = { │ │ │ │ + 'isFilled': style.fill === undefined ? │ │ │ │ + true : style.fill, │ │ │ │ + 'isStroked': style.stroke === undefined ? │ │ │ │ + !!style.strokeWidth : style.stroke │ │ │ │ + }; │ │ │ │ + var drawn; │ │ │ │ + switch (geometry.CLASS_NAME) { │ │ │ │ + case "OpenLayers.Geometry.Point": │ │ │ │ + if (style.graphic === false) { │ │ │ │ + options.isFilled = false; │ │ │ │ + options.isStroked = false; │ │ │ │ + } │ │ │ │ + drawn = this.drawPoint(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LineString": │ │ │ │ + options.isFilled = false; │ │ │ │ + drawn = this.drawLineString(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LinearRing": │ │ │ │ + drawn = this.drawLinearRing(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Polygon": │ │ │ │ + drawn = this.drawPolygon(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Rectangle": │ │ │ │ + drawn = this.drawRectangle(node, geometry); │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + break; │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ + node._options = options; │ │ │ │ │ │ │ │ - addRenderer: function(topRelem, renderer) { │ │ │ │ - if (OpenLayers.Util.isArray(renderer)) { │ │ │ │ - this.addGroupRenderer(topRelem, renderer); │ │ │ │ + //set style │ │ │ │ + //TBD simplify this │ │ │ │ + if (drawn != false) { │ │ │ │ + return { │ │ │ │ + node: this.setStyle(node, style, options, geometry), │ │ │ │ + complete: drawn │ │ │ │ + }; │ │ │ │ } else { │ │ │ │ - var renderElem = this.createElementNS("", renderer.type.toUpperCase() + "RENDERER"); │ │ │ │ - topRelem.appendChild(renderElem); │ │ │ │ - │ │ │ │ - if (renderElem.tagName == "VALUEMAPRENDERER") { │ │ │ │ - this.addValueMapRenderer(renderElem, renderer); │ │ │ │ - } else if (renderElem.tagName == "VALUEMAPLABELRENDERER") { │ │ │ │ - this.addValueMapLabelRenderer(renderElem, renderer); │ │ │ │ - } else if (renderElem.tagName == "SIMPLELABELRENDERER") { │ │ │ │ - this.addSimpleLabelRenderer(renderElem, renderer); │ │ │ │ - } else if (renderElem.tagName == "SCALEDEPENDENTRENDERER") { │ │ │ │ - this.addScaleDependentRenderer(renderElem, renderer); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - addScaleDependentRenderer: function(renderElem, renderer) { │ │ │ │ - if (typeof renderer.lower == "string" || typeof renderer.lower == "number") { │ │ │ │ - renderElem.setAttribute("lower", renderer.lower); │ │ │ │ - } │ │ │ │ - if (typeof renderer.upper == "string" || typeof renderer.upper == "number") { │ │ │ │ - renderElem.setAttribute("upper", renderer.upper); │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.addRenderer(renderElem, renderer.renderer); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - addValueMapLabelRenderer: function(renderElem, renderer) { │ │ │ │ - renderElem.setAttribute("lookupfield", renderer.lookupfield); │ │ │ │ - renderElem.setAttribute("labelfield", renderer.labelfield); │ │ │ │ - │ │ │ │ - if (typeof renderer.exacts == "object") { │ │ │ │ - for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) { │ │ │ │ - var exact = renderer.exacts[ext]; │ │ │ │ - │ │ │ │ - var eelem = this.createElementNS("", "EXACT"); │ │ │ │ - │ │ │ │ - if (typeof exact.value == "string") { │ │ │ │ - eelem.setAttribute("value", exact.value); │ │ │ │ - } │ │ │ │ - if (typeof exact.label == "string") { │ │ │ │ - eelem.setAttribute("label", exact.label); │ │ │ │ - } │ │ │ │ - if (typeof exact.method == "string") { │ │ │ │ - eelem.setAttribute("method", exact.method); │ │ │ │ - } │ │ │ │ - │ │ │ │ - renderElem.appendChild(eelem); │ │ │ │ - │ │ │ │ - if (typeof exact.symbol == "object") { │ │ │ │ - var selem = null; │ │ │ │ - │ │ │ │ - if (exact.symbol.type == "text") { │ │ │ │ - selem = this.createElementNS("", "TEXTSYMBOL"); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (selem != null) { │ │ │ │ - var keys = this.fontStyleKeys; │ │ │ │ - for (var i = 0, len = keys.length; i < len; i++) { │ │ │ │ - var key = keys[i]; │ │ │ │ - if (exact.symbol[key]) { │ │ │ │ - selem.setAttribute(key, exact.symbol[key]); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - eelem.appendChild(selem); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } // for each exact │ │ │ │ + return false; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - addValueMapRenderer: function(renderElem, renderer) { │ │ │ │ - renderElem.setAttribute("lookupfield", renderer.lookupfield); │ │ │ │ - │ │ │ │ - if (typeof renderer.ranges == "object") { │ │ │ │ - for (var rng = 0, rnglen = renderer.ranges.length; rng < rnglen; rng++) { │ │ │ │ - var range = renderer.ranges[rng]; │ │ │ │ - │ │ │ │ - var relem = this.createElementNS("", "RANGE"); │ │ │ │ - relem.setAttribute("lower", range.lower); │ │ │ │ - relem.setAttribute("upper", range.upper); │ │ │ │ - │ │ │ │ - renderElem.appendChild(relem); │ │ │ │ - │ │ │ │ - if (typeof range.symbol == "object") { │ │ │ │ - var selem = null; │ │ │ │ - │ │ │ │ - if (range.symbol.type == "simplepolygon") { │ │ │ │ - selem = this.createElementNS("", "SIMPLEPOLYGONSYMBOL"); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (selem != null) { │ │ │ │ - if (typeof range.symbol.boundarycolor == "string") { │ │ │ │ - selem.setAttribute("boundarycolor", range.symbol.boundarycolor); │ │ │ │ - } │ │ │ │ - if (typeof range.symbol.fillcolor == "string") { │ │ │ │ - selem.setAttribute("fillcolor", range.symbol.fillcolor); │ │ │ │ - } │ │ │ │ - if (typeof range.symbol.filltransparency == "number") { │ │ │ │ - selem.setAttribute("filltransparency", range.symbol.filltransparency); │ │ │ │ - } │ │ │ │ - relem.appendChild(selem); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } // for each range │ │ │ │ - } else if (typeof renderer.exacts == "object") { │ │ │ │ - for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) { │ │ │ │ - var exact = renderer.exacts[ext]; │ │ │ │ - │ │ │ │ - var eelem = this.createElementNS("", "EXACT"); │ │ │ │ - if (typeof exact.value == "string") { │ │ │ │ - eelem.setAttribute("value", exact.value); │ │ │ │ - } │ │ │ │ - if (typeof exact.label == "string") { │ │ │ │ - eelem.setAttribute("label", exact.label); │ │ │ │ - } │ │ │ │ - if (typeof exact.method == "string") { │ │ │ │ - eelem.setAttribute("method", exact.method); │ │ │ │ - } │ │ │ │ - │ │ │ │ - renderElem.appendChild(eelem); │ │ │ │ - │ │ │ │ - if (typeof exact.symbol == "object") { │ │ │ │ - var selem = null; │ │ │ │ - │ │ │ │ - if (exact.symbol.type == "simplemarker") { │ │ │ │ - selem = this.createElementNS("", "SIMPLEMARKERSYMBOL"); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: postDraw │ │ │ │ + * Things that have do be done after the geometry node is appended │ │ │ │ + * to its parent node. To be overridden by subclasses. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + */ │ │ │ │ + postDraw: function(node) {}, │ │ │ │ │ │ │ │ - if (selem != null) { │ │ │ │ - if (typeof exact.symbol.antialiasing == "string") { │ │ │ │ - selem.setAttribute("antialiasing", exact.symbol.antialiasing); │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.color == "string") { │ │ │ │ - selem.setAttribute("color", exact.symbol.color); │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.outline == "string") { │ │ │ │ - selem.setAttribute("outline", exact.symbol.outline); │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.overlap == "string") { │ │ │ │ - selem.setAttribute("overlap", exact.symbol.overlap); │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.shadow == "string") { │ │ │ │ - selem.setAttribute("shadow", exact.symbol.shadow); │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.transparency == "number") { │ │ │ │ - selem.setAttribute("transparency", exact.symbol.transparency); │ │ │ │ - } │ │ │ │ - //if (typeof exact.symbol.type == "string") │ │ │ │ - // selem.setAttribute("type", exact.symbol.type); │ │ │ │ - if (typeof exact.symbol.usecentroid == "string") { │ │ │ │ - selem.setAttribute("usecentroid", exact.symbol.usecentroid); │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.width == "number") { │ │ │ │ - selem.setAttribute("width", exact.symbol.width); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: drawPoint │ │ │ │ + * Virtual function for drawing Point Geometry. │ │ │ │ + * Should be implemented by subclasses. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the renderer could not draw the point │ │ │ │ + */ │ │ │ │ + drawPoint: function(node, geometry) {}, │ │ │ │ │ │ │ │ - eelem.appendChild(selem); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } // for each exact │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: drawLineString │ │ │ │ + * Virtual function for drawing LineString Geometry. │ │ │ │ + * Should be implemented by subclasses. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components of │ │ │ │ + * the linestring, or false if nothing could be drawn │ │ │ │ + */ │ │ │ │ + drawLineString: function(node, geometry) {}, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Method: drawLinearRing │ │ │ │ + * Virtual function for drawing LinearRing Geometry. │ │ │ │ + * Should be implemented by subclasses. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components │ │ │ │ + * of the linear ring, or false if nothing could be drawn │ │ │ │ + */ │ │ │ │ + drawLinearRing: function(node, geometry) {}, │ │ │ │ │ │ │ │ - addSimpleLabelRenderer: function(renderElem, renderer) { │ │ │ │ - renderElem.setAttribute("field", renderer.field); │ │ │ │ - var keys = ['featureweight', 'howmanylabels', 'labelbufferratio', │ │ │ │ - 'labelpriorities', 'labelweight', 'linelabelposition', │ │ │ │ - 'rotationalangles' │ │ │ │ - ]; │ │ │ │ - for (var i = 0, len = keys.length; i < len; i++) { │ │ │ │ - var key = keys[i]; │ │ │ │ - if (renderer[key]) { │ │ │ │ - renderElem.setAttribute(key, renderer[key]); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: drawPolygon │ │ │ │ + * Virtual function for drawing Polygon Geometry. │ │ │ │ + * Should be implemented by subclasses. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components │ │ │ │ + * of the polygon, or false if nothing could be drawn │ │ │ │ + */ │ │ │ │ + drawPolygon: function(node, geometry) {}, │ │ │ │ │ │ │ │ - if (renderer.symbol.type == "text") { │ │ │ │ - var symbol = renderer.symbol; │ │ │ │ - var selem = this.createElementNS("", "TEXTSYMBOL"); │ │ │ │ - renderElem.appendChild(selem); │ │ │ │ + /** │ │ │ │ + * Method: drawRectangle │ │ │ │ + * Virtual function for drawing Rectangle Geometry. │ │ │ │ + * Should be implemented by subclasses. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the renderer could not draw the rectangle │ │ │ │ + */ │ │ │ │ + drawRectangle: function(node, geometry) {}, │ │ │ │ │ │ │ │ - var keys = this.fontStyleKeys; │ │ │ │ - for (var i = 0, len = keys.length; i < len; i++) { │ │ │ │ - var key = keys[i]; │ │ │ │ - if (symbol[key]) { │ │ │ │ - selem.setAttribute(key, renderer[key]); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: drawCircle │ │ │ │ + * Virtual function for drawing Circle Geometry. │ │ │ │ + * Should be implemented by subclasses. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the renderer could not draw the circle │ │ │ │ + */ │ │ │ │ + drawCircle: function(node, geometry) {}, │ │ │ │ │ │ │ │ - writePolygonGeometry: function(polygon) { │ │ │ │ - if (!(polygon instanceof OpenLayers.Geometry.Polygon)) { │ │ │ │ - throw { │ │ │ │ - message: 'Cannot write polygon geometry to ArcXML with an ' + │ │ │ │ - polygon.CLASS_NAME + ' object.', │ │ │ │ - geometry: polygon │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Method: removeText │ │ │ │ + * Removes a label │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * featureId - {String} │ │ │ │ + */ │ │ │ │ + removeText: function(featureId) { │ │ │ │ + var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX); │ │ │ │ + if (label) { │ │ │ │ + this.textRoot.removeChild(label); │ │ │ │ } │ │ │ │ - │ │ │ │ - var polyElem = this.createElementNS("", "POLYGON"); │ │ │ │ - │ │ │ │ - for (var ln = 0, lnlen = polygon.components.length; ln < lnlen; ln++) { │ │ │ │ - var ring = polygon.components[ln]; │ │ │ │ - var ringElem = this.createElementNS("", "RING"); │ │ │ │ - │ │ │ │ - for (var rn = 0, rnlen = ring.components.length; rn < rnlen; rn++) { │ │ │ │ - var point = ring.components[rn]; │ │ │ │ - var pointElem = this.createElementNS("", "POINT"); │ │ │ │ - │ │ │ │ - pointElem.setAttribute("x", point.x); │ │ │ │ - pointElem.setAttribute("y", point.y); │ │ │ │ - │ │ │ │ - ringElem.appendChild(pointElem); │ │ │ │ - } │ │ │ │ - │ │ │ │ - polyElem.appendChild(ringElem); │ │ │ │ + var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX); │ │ │ │ + if (outline) { │ │ │ │ + this.textRoot.removeChild(outline); │ │ │ │ } │ │ │ │ - │ │ │ │ - return polyElem; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseResponse │ │ │ │ - * Take an ArcXML response, and parse in into this object's internal properties. │ │ │ │ - * │ │ │ │ + * Method: getFeatureIdFromEvent │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * data - {String} or {DOMElement} The ArcXML response, as either a string or the │ │ │ │ - * top level DOMElement of the response. │ │ │ │ + * evt - {Object} An <OpenLayers.Event> object │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A feature id or undefined. │ │ │ │ */ │ │ │ │ - parseResponse: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - var newData = new OpenLayers.Format.XML(); │ │ │ │ - data = newData.read(data); │ │ │ │ - } │ │ │ │ - var response = new OpenLayers.Format.ArcXML.Response(); │ │ │ │ - │ │ │ │ - var errorNode = data.getElementsByTagName("ERROR"); │ │ │ │ - │ │ │ │ - if (errorNode != null && errorNode.length > 0) { │ │ │ │ - response.error = this.getChildValue(errorNode, "Unknown error."); │ │ │ │ - } else { │ │ │ │ - var responseNode = data.getElementsByTagName("RESPONSE"); │ │ │ │ - │ │ │ │ - if (responseNode == null || responseNode.length == 0) { │ │ │ │ - response.error = "No RESPONSE tag found in ArcXML response."; │ │ │ │ - return response; │ │ │ │ - } │ │ │ │ + getFeatureIdFromEvent: function(evt) { │ │ │ │ + var target = evt.target; │ │ │ │ + var useElement = target && target.correspondingUseElement; │ │ │ │ + var node = useElement ? useElement : (target || evt.srcElement); │ │ │ │ + return node._featureId; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var rtype = responseNode[0].firstChild.nodeName; │ │ │ │ - if (rtype == "#text") { │ │ │ │ - rtype = responseNode[0].firstChild.nextSibling.nodeName; │ │ │ │ + /** │ │ │ │ + * Method: eraseGeometry │ │ │ │ + * Erase a geometry from the renderer. In the case of a multi-geometry, │ │ │ │ + * we cycle through and recurse on ourselves. Otherwise, we look for a │ │ │ │ + * node with the geometry.id, destroy its geometry, and remove it from │ │ │ │ + * the DOM. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * featureId - {String} │ │ │ │ + */ │ │ │ │ + eraseGeometry: function(geometry, featureId) { │ │ │ │ + if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") || │ │ │ │ + (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") || │ │ │ │ + (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") || │ │ │ │ + (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) { │ │ │ │ + for (var i = 0, len = geometry.components.length; i < len; i++) { │ │ │ │ + this.eraseGeometry(geometry.components[i], featureId); │ │ │ │ } │ │ │ │ - │ │ │ │ - if (rtype == "IMAGE") { │ │ │ │ - var envelopeNode = data.getElementsByTagName("ENVELOPE"); │ │ │ │ - var outputNode = data.getElementsByTagName("OUTPUT"); │ │ │ │ - │ │ │ │ - if (envelopeNode == null || envelopeNode.length == 0) { │ │ │ │ - response.error = "No ENVELOPE tag found in ArcXML response."; │ │ │ │ - } else if (outputNode == null || outputNode.length == 0) { │ │ │ │ - response.error = "No OUTPUT tag found in ArcXML response."; │ │ │ │ - } else { │ │ │ │ - var envAttr = this.parseAttributes(envelopeNode[0]); │ │ │ │ - var outputAttr = this.parseAttributes(outputNode[0]); │ │ │ │ - │ │ │ │ - if (typeof outputAttr.type == "string") { │ │ │ │ - response.image = { │ │ │ │ - envelope: envAttr, │ │ │ │ - output: { │ │ │ │ - type: outputAttr.type, │ │ │ │ - data: this.getChildValue(outputNode[0]) │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - } else { │ │ │ │ - response.image = { │ │ │ │ - envelope: envAttr, │ │ │ │ - output: outputAttr │ │ │ │ - }; │ │ │ │ - } │ │ │ │ + } else { │ │ │ │ + var element = OpenLayers.Util.getElement(geometry.id); │ │ │ │ + if (element && element.parentNode) { │ │ │ │ + if (element.geometry) { │ │ │ │ + element.geometry.destroy(); │ │ │ │ + element.geometry = null; │ │ │ │ } │ │ │ │ - } else if (rtype == "FEATURES") { │ │ │ │ - var features = responseNode[0].getElementsByTagName("FEATURES"); │ │ │ │ - │ │ │ │ - // get the feature count │ │ │ │ - var featureCount = features[0].getElementsByTagName("FEATURECOUNT"); │ │ │ │ - response.features.featurecount = featureCount[0].getAttribute("count"); │ │ │ │ - │ │ │ │ - if (response.features.featurecount > 0) { │ │ │ │ - // get the feature envelope │ │ │ │ - var envelope = features[0].getElementsByTagName("ENVELOPE"); │ │ │ │ - response.features.envelope = this.parseAttributes(envelope[0], typeof(0)); │ │ │ │ - │ │ │ │ - // get the field values per feature │ │ │ │ - var featureList = features[0].getElementsByTagName("FEATURE"); │ │ │ │ - for (var fn = 0; fn < featureList.length; fn++) { │ │ │ │ - var feature = new OpenLayers.Feature.Vector(); │ │ │ │ - var fields = featureList[fn].getElementsByTagName("FIELD"); │ │ │ │ - │ │ │ │ - for (var fdn = 0; fdn < fields.length; fdn++) { │ │ │ │ - var fieldName = fields[fdn].getAttribute("name"); │ │ │ │ - var fieldValue = fields[fdn].getAttribute("value"); │ │ │ │ - feature.attributes[fieldName] = fieldValue; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var geom = featureList[fn].getElementsByTagName("POLYGON"); │ │ │ │ - │ │ │ │ - if (geom.length > 0) { │ │ │ │ - // if there is a polygon, create an openlayers polygon, and assign │ │ │ │ - // it to the .geometry property of the feature │ │ │ │ - var ring = geom[0].getElementsByTagName("RING"); │ │ │ │ - │ │ │ │ - var polys = []; │ │ │ │ - for (var rn = 0; rn < ring.length; rn++) { │ │ │ │ - var linearRings = []; │ │ │ │ - linearRings.push(this.parsePointGeometry(ring[rn])); │ │ │ │ - │ │ │ │ - var holes = ring[rn].getElementsByTagName("HOLE"); │ │ │ │ - for (var hn = 0; hn < holes.length; hn++) { │ │ │ │ - linearRings.push(this.parsePointGeometry(holes[hn])); │ │ │ │ - } │ │ │ │ - holes = null; │ │ │ │ - polys.push(new OpenLayers.Geometry.Polygon(linearRings)); │ │ │ │ - linearRings = null; │ │ │ │ - } │ │ │ │ - ring = null; │ │ │ │ + element.parentNode.removeChild(element); │ │ │ │ │ │ │ │ - if (polys.length == 1) { │ │ │ │ - feature.geometry = polys[0]; │ │ │ │ - } else { │ │ │ │ - feature.geometry = new OpenLayers.Geometry.MultiPolygon(polys); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + if (this.indexer) { │ │ │ │ + this.indexer.remove(element); │ │ │ │ + } │ │ │ │ │ │ │ │ - response.features.feature.push(feature); │ │ │ │ + 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); │ │ │ │ } │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - response.error = "Unidentified response type."; │ │ │ │ } │ │ │ │ } │ │ │ │ - return response; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: parseAttributes │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * 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: │ │ │ │ - * node - {<DOMElement>} An element to parse attributes from. │ │ │ │ - * │ │ │ │ + * id - {String} │ │ │ │ + * type - {String} type Kind of node to draw. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Object} An attributes object, with properties set to attribute values. │ │ │ │ + * {DOMElement} A new node of the given type and id. │ │ │ │ */ │ │ │ │ - parseAttributes: function(node, type) { │ │ │ │ - var attributes = {}; │ │ │ │ - for (var attr = 0; attr < node.attributes.length; attr++) { │ │ │ │ - if (type == "number") { │ │ │ │ - attributes[node.attributes[attr].nodeName] = parseFloat(node.attributes[attr].nodeValue); │ │ │ │ - } else { │ │ │ │ - attributes[node.attributes[attr].nodeName] = node.attributes[attr].nodeValue; │ │ │ │ + nodeFactory: function(id, type) { │ │ │ │ + var node = OpenLayers.Util.getElement(id); │ │ │ │ + if (node) { │ │ │ │ + if (!this.nodeTypeCompare(node, type)) { │ │ │ │ + node.parentNode.removeChild(node); │ │ │ │ + node = this.nodeFactory(id, type); │ │ │ │ } │ │ │ │ + } else { │ │ │ │ + node = this.createNode(type, id); │ │ │ │ } │ │ │ │ - return attributes; │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Method: nodeTypeCompare │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * type - {String} Kind of node │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ + * This function must be overridden by subclasses. │ │ │ │ + */ │ │ │ │ + nodeTypeCompare: function(node, type) {}, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: parsePointGeometry │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: createNode │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {<DOMElement>} An element to parse <COORDS> or <POINT> arcxml data from. │ │ │ │ - * │ │ │ │ + * type - {String} Kind of node to draw. │ │ │ │ + * id - {String} Id for node. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Geometry.LinearRing>} A linear ring represented by the node's points. │ │ │ │ + * {DOMElement} A new node of the given type and id. │ │ │ │ + * This function must be overridden by subclasses. │ │ │ │ */ │ │ │ │ - parsePointGeometry: function(node) { │ │ │ │ - var ringPoints = []; │ │ │ │ - var coords = node.getElementsByTagName("COORDS"); │ │ │ │ + createNode: function(type, id) {}, │ │ │ │ │ │ │ │ - if (coords.length > 0) { │ │ │ │ - // if coords is present, it's the only coords item │ │ │ │ - var coordArr = this.getChildValue(coords[0]); │ │ │ │ - coordArr = coordArr.split(/;/); │ │ │ │ - for (var cn = 0; cn < coordArr.length; cn++) { │ │ │ │ - var coordItems = coordArr[cn].split(/ /); │ │ │ │ - ringPoints.push(new OpenLayers.Geometry.Point(coordItems[0], coordItems[1])); │ │ │ │ - } │ │ │ │ - coords = null; │ │ │ │ - } else { │ │ │ │ - var point = node.getElementsByTagName("POINT"); │ │ │ │ - if (point.length > 0) { │ │ │ │ - for (var pn = 0; pn < point.length; pn++) { │ │ │ │ - ringPoints.push( │ │ │ │ - new OpenLayers.Geometry.Point( │ │ │ │ - parseFloat(point[pn].getAttribute("x")), │ │ │ │ - parseFloat(point[pn].getAttribute("y")) │ │ │ │ - ) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - point = null; │ │ │ │ + /** │ │ │ │ + * Method: moveRoot │ │ │ │ + * moves this renderer's root to a different renderer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * renderer - {<OpenLayers.Renderer>} target renderer for the moved root │ │ │ │ + */ │ │ │ │ + moveRoot: function(renderer) { │ │ │ │ + var root = this.root; │ │ │ │ + if (renderer.root.parentNode == this.rendererRoot) { │ │ │ │ + root = renderer.root; │ │ │ │ } │ │ │ │ - │ │ │ │ - return new OpenLayers.Geometry.LinearRing(ringPoints); │ │ │ │ + root.parentNode.removeChild(root); │ │ │ │ + renderer.rendererRoot.appendChild(root); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.ArcXML" │ │ │ │ -}); │ │ │ │ - │ │ │ │ -OpenLayers.Format.ArcXML.Request = OpenLayers.Class({ │ │ │ │ - initialize: function(params) { │ │ │ │ - var defaults = { │ │ │ │ - get_image: { │ │ │ │ - properties: { │ │ │ │ - background: null, │ │ │ │ - /*{ │ │ │ │ - color: { r:255, g:255, b:255 }, │ │ │ │ - transcolor: null │ │ │ │ - },*/ │ │ │ │ - draw: true, │ │ │ │ - envelope: { │ │ │ │ - minx: 0, │ │ │ │ - miny: 0, │ │ │ │ - maxx: 0, │ │ │ │ - maxy: 0 │ │ │ │ - }, │ │ │ │ - featurecoordsys: { │ │ │ │ - id: 0, │ │ │ │ - string: "", │ │ │ │ - datumtransformid: 0, │ │ │ │ - datumtransformstring: "" │ │ │ │ - }, │ │ │ │ - filtercoordsys: { │ │ │ │ - id: 0, │ │ │ │ - string: "", │ │ │ │ - datumtransformid: 0, │ │ │ │ - datumtransformstring: "" │ │ │ │ - }, │ │ │ │ - imagesize: { │ │ │ │ - height: 0, │ │ │ │ - width: 0, │ │ │ │ - dpi: 96, │ │ │ │ - printheight: 0, │ │ │ │ - printwidth: 0, │ │ │ │ - scalesymbols: false │ │ │ │ - }, │ │ │ │ - layerlist: [], │ │ │ │ - /* no support for legends */ │ │ │ │ - output: { │ │ │ │ - baseurl: "", │ │ │ │ - legendbaseurl: "", │ │ │ │ - legendname: "", │ │ │ │ - legendpath: "", │ │ │ │ - legendurl: "", │ │ │ │ - name: "", │ │ │ │ - path: "", │ │ │ │ - type: "jpg", │ │ │ │ - url: "" │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - get_feature: { │ │ │ │ - layer: "", │ │ │ │ - query: { │ │ │ │ - isspatial: false, │ │ │ │ - featurecoordsys: { │ │ │ │ - id: 0, │ │ │ │ - string: "", │ │ │ │ - datumtransformid: 0, │ │ │ │ - datumtransformstring: "" │ │ │ │ - }, │ │ │ │ - filtercoordsys: { │ │ │ │ - id: 0, │ │ │ │ - string: "", │ │ │ │ - datumtransformid: 0, │ │ │ │ - datumtransformstring: "" │ │ │ │ - }, │ │ │ │ - buffer: 0, │ │ │ │ - where: "", │ │ │ │ - spatialfilter: { │ │ │ │ - relation: "envelope_intersection", │ │ │ │ - envelope: null │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - environment: { │ │ │ │ - separators: { │ │ │ │ - cs: " ", │ │ │ │ - ts: ";" │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - layer: [], │ │ │ │ - workspaces: [] │ │ │ │ - }; │ │ │ │ - │ │ │ │ - return OpenLayers.Util.extend(this, defaults); │ │ │ │ + /** │ │ │ │ + * Method: getRenderLayerId │ │ │ │ + * Gets the layer that this renderer's output appears on. If moveRoot was │ │ │ │ + * used, this will be different from the id of the layer containing the │ │ │ │ + * features rendered by this renderer. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} the id of the output layer. │ │ │ │ + */ │ │ │ │ + getRenderLayerId: function() { │ │ │ │ + return this.root.parentNode.parentNode.id; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.ArcXML.Request" │ │ │ │ -}); │ │ │ │ - │ │ │ │ -OpenLayers.Format.ArcXML.Response = OpenLayers.Class({ │ │ │ │ - initialize: function(params) { │ │ │ │ - var defaults = { │ │ │ │ - image: { │ │ │ │ - envelope: null, │ │ │ │ - output: '' │ │ │ │ - }, │ │ │ │ - │ │ │ │ - features: { │ │ │ │ - featurecount: 0, │ │ │ │ - envelope: null, │ │ │ │ - feature: [] │ │ │ │ - }, │ │ │ │ - │ │ │ │ - error: '' │ │ │ │ - }; │ │ │ │ - │ │ │ │ - return OpenLayers.Util.extend(this, defaults); │ │ │ │ + /** │ │ │ │ + * Method: isComplexSymbol │ │ │ │ + * Determines if a symbol cannot be rendered using drawCircle │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * graphicName - {String} │ │ │ │ + * │ │ │ │ + * Returns │ │ │ │ + * {Boolean} true if the symbol is complex, false if not │ │ │ │ + */ │ │ │ │ + isComplexSymbol: function(graphicName) { │ │ │ │ + return (graphicName != "circle") && !!graphicName; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.ArcXML.Response" │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.Elements" │ │ │ │ }); │ │ │ │ + │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/ArcIMS.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/Layer/Grid.js │ │ │ │ - * @requires OpenLayers/Format/ArcXML.js │ │ │ │ - * @requires OpenLayers/Request.js │ │ │ │ + * @requires OpenLayers/Renderer/Elements.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.ArcIMS │ │ │ │ - * Instances of OpenLayers.Layer.ArcIMS are used to display data from ESRI ArcIMS │ │ │ │ - * Mapping Services. Create a new ArcIMS layer with the <OpenLayers.Layer.ArcIMS> │ │ │ │ - * constructor. │ │ │ │ + * Class: OpenLayers.Renderer.VML │ │ │ │ + * Render vector features in browsers with VML capability. Construct a new │ │ │ │ + * VML renderer with the <OpenLayers.Renderer.VML> constructor. │ │ │ │ * │ │ │ │ + * Note that for all calculations in this class, we use (num | 0) to truncate a │ │ │ │ + * float value to an integer. This is done because it seems that VML doesn't │ │ │ │ + * support float values. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ + * - <OpenLayers.Renderer.Elements> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.ArcIMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: DEFAULT_PARAMS │ │ │ │ - * {Object} Default query string parameters. │ │ │ │ - */ │ │ │ │ - DEFAULT_PARAMS: { │ │ │ │ - ClientVersion: "9.2", │ │ │ │ - ServiceName: '' │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: featureCoordSys │ │ │ │ - * {String} Code for feature coordinate system. Default is "4326". │ │ │ │ - */ │ │ │ │ - featureCoordSys: "4326", │ │ │ │ +OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: filterCoordSys │ │ │ │ - * {String} Code for filter coordinate system. Default is "4326". │ │ │ │ + * Property: xmlns │ │ │ │ + * {String} XML Namespace URN │ │ │ │ */ │ │ │ │ - filterCoordSys: "4326", │ │ │ │ + xmlns: "urn:schemas-microsoft-com:vml", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: layers │ │ │ │ - * {Array} An array of objects with layer properties. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - layers: null, │ │ │ │ + symbolCache: {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: async │ │ │ │ - * {Boolean} Request images asynchronously. Default is true. │ │ │ │ + * Property: offset │ │ │ │ + * {Object} Hash with "x" and "y" properties │ │ │ │ */ │ │ │ │ - async: true, │ │ │ │ + offset: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: name │ │ │ │ - * {String} Layer name. Default is "ArcIMS". │ │ │ │ + * Constructor: OpenLayers.Renderer.VML │ │ │ │ + * Create a new VML renderer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * containerID - {String} The id for the element that contains the renderer │ │ │ │ */ │ │ │ │ - name: "ArcIMS", │ │ │ │ + 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++) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} The layer is a base layer. Default is true. │ │ │ │ - */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " + │ │ │ │ + "position: absolute; display: inline-block;"); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: DEFAULT_OPTIONS │ │ │ │ - * {Object} Default layers properties. │ │ │ │ - */ │ │ │ │ - DEFAULT_OPTIONS: { │ │ │ │ - tileSize: new OpenLayers.Size(512, 512), │ │ │ │ - featureCoordSys: "4326", │ │ │ │ - filterCoordSys: "4326", │ │ │ │ - layers: null, │ │ │ │ - isBaseLayer: true, │ │ │ │ - async: true, │ │ │ │ - name: "ArcIMS" │ │ │ │ + OpenLayers.Renderer.Elements.prototype.initialize.apply(this, │ │ │ │ + arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.ArcIMS │ │ │ │ - * Create a new ArcIMS layer object. │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * var arcims = new OpenLayers.Layer.ArcIMS( │ │ │ │ - * "Global Sample", │ │ │ │ - * "http://sample.avencia.com/servlet/com.esri.esrimap.Esrimap", │ │ │ │ - * { │ │ │ │ - * service: "OpenLayers_Sample", │ │ │ │ - * layers: [ │ │ │ │ - * // layers to manipulate │ │ │ │ - * {id: "1", visible: true} │ │ │ │ - * ] │ │ │ │ - * } │ │ │ │ - * ); │ │ │ │ - * (end) │ │ │ │ + * APIMethod: supported │ │ │ │ + * Determine whether a browser supports this renderer. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} A name for the layer │ │ │ │ - * url - {String} Base url for the ArcIMS server │ │ │ │ - * options - {Object} Optional object with properties to be set on the │ │ │ │ - * layer. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The browser supports the VML renderer │ │ │ │ */ │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - │ │ │ │ - this.tileSize = new OpenLayers.Size(512, 512); │ │ │ │ - │ │ │ │ - // parameters │ │ │ │ - this.params = OpenLayers.Util.applyDefaults({ │ │ │ │ - ServiceName: options.serviceName │ │ │ │ - }, │ │ │ │ - this.DEFAULT_PARAMS │ │ │ │ - ); │ │ │ │ - this.options = OpenLayers.Util.applyDefaults( │ │ │ │ - options, this.DEFAULT_OPTIONS │ │ │ │ - ); │ │ │ │ - │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply( │ │ │ │ - this, [name, url, this.params, options] │ │ │ │ - ); │ │ │ │ - │ │ │ │ - //layer is transparent │ │ │ │ - if (this.transparent) { │ │ │ │ - │ │ │ │ - // unless explicitly set in options, make layer an overlay │ │ │ │ - if (!this.isBaseLayer) { │ │ │ │ - this.isBaseLayer = false; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // jpegs can never be transparent, so intelligently switch the │ │ │ │ - // format, depending on the browser's capabilities │ │ │ │ - if (this.format == "image/jpeg") { │ │ │ │ - this.format = OpenLayers.Util.alphaHack() ? "image/gif" : "image/png"; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - // create an empty layer list if no layers specified in the options │ │ │ │ - if (this.options.layers === null) { │ │ │ │ - this.options.layers = []; │ │ │ │ - } │ │ │ │ + supported: function() { │ │ │ │ + return !!(document.namespaces); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ - * Return an image url this layer. │ │ │ │ + * Method: setExtent │ │ │ │ + * Set the renderer's extent │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the │ │ │ │ - * request. │ │ │ │ - * │ │ │ │ + * extent - {<OpenLayers.Bounds>} │ │ │ │ + * resolutionChanged - {Boolean} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} A string with the map image's url. │ │ │ │ + * {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. │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - var url = ""; │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ + setExtent: function(extent, resolutionChanged) { │ │ │ │ + var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ - // create an arcxml request to generate the image │ │ │ │ - var axlReq = new OpenLayers.Format.ArcXML( │ │ │ │ - OpenLayers.Util.extend(this.options, { │ │ │ │ - requesttype: "image", │ │ │ │ - envelope: bounds.toArray(), │ │ │ │ - tileSize: this.tileSize │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ │ │ │ │ - // create a synchronous ajax request to get an arcims image │ │ │ │ - var req = new OpenLayers.Request.POST({ │ │ │ │ - url: this.getFullRequestString(), │ │ │ │ - data: axlReq.write(), │ │ │ │ - async: false │ │ │ │ - }); │ │ │ │ │ │ │ │ - // if the response exists │ │ │ │ - if (req != null) { │ │ │ │ - var doc = req.responseXML; │ │ │ │ + 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]; │ │ │ │ │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = req.responseText; │ │ │ │ - } │ │ │ │ + var size = this.size.w + " " + this.size.h; │ │ │ │ + root.coordsize = size; │ │ │ │ │ │ │ │ - // create a new arcxml format to read the response │ │ │ │ - var axlResp = new OpenLayers.Format.ArcXML(); │ │ │ │ - var arcxml = axlResp.read(doc); │ │ │ │ - url = this.getUrlOrImage(arcxml.image.output); │ │ │ │ } │ │ │ │ + // flip the VML display Y axis upside down so it │ │ │ │ + // matches the display Y axis of the map │ │ │ │ + this.root.style.flip = "y"; │ │ │ │ │ │ │ │ - return url; │ │ │ │ + return coordSysUnchanged; │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURLasync │ │ │ │ - * Get an image url this layer asynchronously, and execute a callback │ │ │ │ - * when the image url is generated. │ │ │ │ + * Method: setSize │ │ │ │ + * Set the size of the drawing surface │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the │ │ │ │ - * request. │ │ │ │ - * callback - {Function} Function to call when image url is retrieved. │ │ │ │ - * scope - {Object} The scope of the callback method. │ │ │ │ + * size - {<OpenLayers.Size>} the size of the drawing surface │ │ │ │ */ │ │ │ │ - getURLasync: function(bounds, callback, scope) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - │ │ │ │ - // create an arcxml request to generate the image │ │ │ │ - var axlReq = new OpenLayers.Format.ArcXML( │ │ │ │ - OpenLayers.Util.extend(this.options, { │ │ │ │ - requesttype: "image", │ │ │ │ - envelope: bounds.toArray(), │ │ │ │ - tileSize: this.tileSize │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ - │ │ │ │ - // create an asynchronous ajax request to get an arcims image │ │ │ │ - OpenLayers.Request.POST({ │ │ │ │ - url: this.getFullRequestString(), │ │ │ │ - async: true, │ │ │ │ - data: axlReq.write(), │ │ │ │ - callback: function(req) { │ │ │ │ - // process the response from ArcIMS, and call the callback function │ │ │ │ - // to set the image URL │ │ │ │ - var doc = req.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = req.responseText; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // create a new arcxml format to read the response │ │ │ │ - var axlResp = new OpenLayers.Format.ArcXML(); │ │ │ │ - var arcxml = axlResp.read(doc); │ │ │ │ + setSize: function(size) { │ │ │ │ + OpenLayers.Renderer.prototype.setSize.apply(this, arguments); │ │ │ │ │ │ │ │ - callback.call(scope, this.getUrlOrImage(arcxml.image.output)); │ │ │ │ - }, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + // setting width and height on all roots to avoid flicker which we │ │ │ │ + // would get with 100% width and height on child roots │ │ │ │ + var roots = [ │ │ │ │ + this.rendererRoot, │ │ │ │ + this.root, │ │ │ │ + this.vectorRoot, │ │ │ │ + this.textRoot │ │ │ │ + ]; │ │ │ │ + var w = this.size.w + "px"; │ │ │ │ + var h = this.size.h + "px"; │ │ │ │ + var root; │ │ │ │ + for (var i = 0, len = roots.length; i < len; ++i) { │ │ │ │ + root = roots[i]; │ │ │ │ + root.style.width = w; │ │ │ │ + root.style.height = h; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getUrlOrImage │ │ │ │ - * Extract a url or image from the ArcXML image output. │ │ │ │ + * Method: getNodeType │ │ │ │ + * Get the node type for a geometry and style │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * output - {Object} The image.output property of the object returned from │ │ │ │ - * the ArcXML format read method. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A URL for an image (potentially with the data protocol). │ │ │ │ + * {String} The corresponding node type for the specified geometry │ │ │ │ */ │ │ │ │ - getUrlOrImage: function(output) { │ │ │ │ - var ret = ""; │ │ │ │ - if (output.url) { │ │ │ │ - // If the image response output url is a string, then the image │ │ │ │ - // data is not inline. │ │ │ │ - ret = output.url; │ │ │ │ - } else if (output.data) { │ │ │ │ - // The image data is inline and base64 encoded, create a data │ │ │ │ - // url for the image. This will only work for small images, │ │ │ │ - // due to browser url length limits. │ │ │ │ - ret = "data:image/" + output.type + │ │ │ │ - ";base64," + output.data; │ │ │ │ + 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 ret; │ │ │ │ + return nodeType; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setLayerQuery │ │ │ │ - * Set the query definition on this layer. Query definitions are used to │ │ │ │ - * render parts of the spatial data in an image, and can be used to │ │ │ │ - * filter features or layers in the ArcIMS service. │ │ │ │ + * Method: setStyle │ │ │ │ + * Use to set all the style attributes to a VML node. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * id - {String} The ArcIMS layer ID. │ │ │ │ - * querydef - {Object} The query definition to apply to this layer. │ │ │ │ + * node - {DOMElement} An VML element to decorate │ │ │ │ + * style - {Object} │ │ │ │ + * options - {Object} Currently supported options include │ │ │ │ + * 'isFilled' {Boolean} and │ │ │ │ + * 'isStroked' {Boolean} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ */ │ │ │ │ - setLayerQuery: function(id, querydef) { │ │ │ │ - // find the matching layer, if it exists │ │ │ │ - for (var lyr = 0; lyr < this.options.layers.length; lyr++) { │ │ │ │ - if (id == this.options.layers[lyr].id) { │ │ │ │ - // replace this layer definition │ │ │ │ - this.options.layers[lyr].query = querydef; │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + setStyle: function(node, style, options, geometry) { │ │ │ │ + style = style || node._style; │ │ │ │ + options = options || node._options; │ │ │ │ + var fillColor = style.fillColor; │ │ │ │ │ │ │ │ - // no layer found, create a new definition │ │ │ │ - this.options.layers.push({ │ │ │ │ - id: id, │ │ │ │ - visible: true, │ │ │ │ - query: querydef │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ + var title = style.title || style.graphicTitle; │ │ │ │ + if (title) { │ │ │ │ + node.title = title; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getFeatureInfo │ │ │ │ - * Get feature information from ArcIMS. Using the applied geometry, apply │ │ │ │ - * the options to the query (buffer, area/envelope intersection), and │ │ │ │ - * query the ArcIMS service. │ │ │ │ - * │ │ │ │ - * A note about accuracy: │ │ │ │ - * ArcIMS interprets the accuracy attribute in feature requests to be │ │ │ │ - * something like the 'modulus' operator on feature coordinates, │ │ │ │ - * applied to the database geometry of the feature. It doesn't round, │ │ │ │ - * so your feature coordinates may be up to (1 x accuracy) offset from │ │ │ │ - * the actual feature coordinates. If the accuracy of the layer is not │ │ │ │ - * specified, the accuracy will be computed to be approximately 1 │ │ │ │ - * feature coordinate per screen pixel. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.LonLat>} or {<OpenLayers.Geometry.Polygon>} The │ │ │ │ - * geometry to use when making the query. This should be a closed │ │ │ │ - * polygon for behavior approximating a free selection. │ │ │ │ - * layer - {Object} The ArcIMS layer definition. This is an anonymous object │ │ │ │ - * that looks like: │ │ │ │ - * (code) │ │ │ │ - * { │ │ │ │ - * id: "ArcXML layer ID", // the ArcXML layer ID │ │ │ │ - * query: { │ │ │ │ - * where: "STATE = 'PA'", // the where clause of the query │ │ │ │ - * accuracy: 100 // the accuracy of the returned feature │ │ │ │ - * } │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ - * options - {Object} Object with non-default properties to set on the layer. │ │ │ │ - * Supported properties are buffer, callback, scope, and any other │ │ │ │ - * properties applicable to the ArcXML format. Set the 'callback' and │ │ │ │ - * 'scope' for an object and function to recieve the parsed features │ │ │ │ - * from ArcIMS. │ │ │ │ - */ │ │ │ │ - getFeatureInfo: function(geometry, layer, options) { │ │ │ │ - // set the buffer to 1 unit (dd/m/ft?) by default │ │ │ │ - var buffer = options.buffer || 1; │ │ │ │ - // empty callback by default │ │ │ │ - var callback = options.callback || function() {}; │ │ │ │ - // default scope is window (global) │ │ │ │ - var scope = options.scope || window; │ │ │ │ + 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; │ │ │ │ │ │ │ │ - // apply these option to the request options │ │ │ │ - var requestOptions = {}; │ │ │ │ - OpenLayers.Util.extend(requestOptions, this.options); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var xOffset = (style.graphicXOffset != undefined) ? │ │ │ │ + style.graphicXOffset : -(0.5 * width); │ │ │ │ + var yOffset = (style.graphicYOffset != undefined) ? │ │ │ │ + style.graphicYOffset : -(0.5 * height); │ │ │ │ │ │ │ │ - // this is a feature request │ │ │ │ - requestOptions.requesttype = "feature"; │ │ │ │ + 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 (geometry instanceof OpenLayers.LonLat) { │ │ │ │ - // create an envelope if the geometry is really a lon/lat │ │ │ │ - requestOptions.polygon = null; │ │ │ │ - requestOptions.envelope = [ │ │ │ │ - geometry.lon - buffer, │ │ │ │ - geometry.lat - buffer, │ │ │ │ - geometry.lon + buffer, │ │ │ │ - geometry.lat + buffer │ │ │ │ - ]; │ │ │ │ - } else if (geometry instanceof OpenLayers.Geometry.Polygon) { │ │ │ │ - // use the polygon assigned, and empty the envelope │ │ │ │ - requestOptions.envelope = null; │ │ │ │ - requestOptions.polygon = geometry; │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ } │ │ │ │ │ │ │ │ - // create an arcxml request to get feature requests │ │ │ │ - var arcxml = new OpenLayers.Format.ArcXML(requestOptions); │ │ │ │ - │ │ │ │ - // apply any get feature options to the arcxml request │ │ │ │ - OpenLayers.Util.extend(arcxml.request.get_feature, options); │ │ │ │ - │ │ │ │ - arcxml.request.get_feature.layer = layer.id; │ │ │ │ - if (typeof layer.query.accuracy == "number") { │ │ │ │ - // set the accuracy if it was specified │ │ │ │ - arcxml.request.get_feature.query.accuracy = layer.query.accuracy; │ │ │ │ + // fill │ │ │ │ + if (options.isFilled) { │ │ │ │ + node.fillcolor = fillColor; │ │ │ │ } else { │ │ │ │ - // guess that the accuracy is 1 per screen pixel │ │ │ │ - var mapCenter = this.map.getCenter(); │ │ │ │ - var viewPx = this.map.getViewPortPxFromLonLat(mapCenter); │ │ │ │ - viewPx.x++; │ │ │ │ - var mapOffCenter = this.map.getLonLatFromPixel(viewPx); │ │ │ │ - arcxml.request.get_feature.query.accuracy = mapOffCenter.lon - mapCenter.lon; │ │ │ │ + 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; │ │ │ │ │ │ │ │ - // set the get_feature query to be the same as the layer passed in │ │ │ │ - arcxml.request.get_feature.query.where = layer.query.where; │ │ │ │ + if (node._geometryClass === "OpenLayers.Geometry.Point" && │ │ │ │ + style.externalGraphic) { │ │ │ │ │ │ │ │ - // use area_intersection │ │ │ │ - arcxml.request.get_feature.query.spatialfilter.relation = "area_intersection"; │ │ │ │ + // override fillOpacity │ │ │ │ + if (style.graphicOpacity) { │ │ │ │ + fill.opacity = style.graphicOpacity; │ │ │ │ + } │ │ │ │ │ │ │ │ - // create a new asynchronous request to get the feature info │ │ │ │ - OpenLayers.Request.POST({ │ │ │ │ - url: this.getFullRequestString({ │ │ │ │ - 'CustomService': 'Query' │ │ │ │ - }), │ │ │ │ - data: arcxml.write(), │ │ │ │ - callback: function(request) { │ │ │ │ - // parse the arcxml response │ │ │ │ - var response = arcxml.parseResponse(request.responseText); │ │ │ │ + fill.src = style.externalGraphic; │ │ │ │ + fill.type = "frame"; │ │ │ │ │ │ │ │ - if (!arcxml.iserror()) { │ │ │ │ - // if the arcxml is not an error, call the callback with the features parsed │ │ │ │ - callback.call(scope, response.features); │ │ │ │ - } else { │ │ │ │ - // if the arcxml is an error, return null features selected │ │ │ │ - callback.call(scope, null); │ │ │ │ + if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ + fill.aspect = "atmost"; │ │ │ │ } │ │ │ │ } │ │ │ │ - }); │ │ │ │ + if (fill.parentNode != node) { │ │ │ │ + node.appendChild(fill); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // additional rendering for rotated graphics or symbols │ │ │ │ + var rotation = style.rotation; │ │ │ │ + if ((rotation !== undefined || node._rotation !== undefined)) { │ │ │ │ + node._rotation = rotation; │ │ │ │ + if (style.externalGraphic) { │ │ │ │ + this.graphicRotate(node, xOffset, yOffset, style); │ │ │ │ + // make the fill fully transparent, because we now have │ │ │ │ + // the graphic as imagedata element. We cannot just remove │ │ │ │ + // the fill, because this is part of the hack described │ │ │ │ + // in graphicRotate │ │ │ │ + fill.opacity = 0; │ │ │ │ + } else if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ + node.style.rotation = rotation || 0; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // stroke │ │ │ │ + var strokes = node.getElementsByTagName("stroke"); │ │ │ │ + var stroke = (strokes.length == 0) ? null : strokes[0]; │ │ │ │ + if (!options.isStroked) { │ │ │ │ + node.stroked = false; │ │ │ │ + if (stroke) { │ │ │ │ + stroke.on = false; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + if (!stroke) { │ │ │ │ + stroke = this.createNode('olv:stroke', node.id + "_stroke"); │ │ │ │ + node.appendChild(stroke); │ │ │ │ + } │ │ │ │ + stroke.on = true; │ │ │ │ + stroke.color = style.strokeColor; │ │ │ │ + stroke.weight = style.strokeWidth + "px"; │ │ │ │ + stroke.opacity = style.strokeOpacity; │ │ │ │ + stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' : │ │ │ │ + (style.strokeLinecap || 'round'); │ │ │ │ + if (style.strokeDashstyle) { │ │ │ │ + stroke.dashstyle = this.dashStyle(style); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ + node.style.cursor = style.cursor; │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.ArcIMS>} An exact clone of this layer │ │ │ │ + * Method: graphicRotate │ │ │ │ + * If a point is to be styled with externalGraphic and rotation, VML fills │ │ │ │ + * cannot be used to display the graphic, because rotation of graphic │ │ │ │ + * fills is not supported by the VML implementation of Internet Explorer. │ │ │ │ + * This method creates a olv:imagedata element inside the VML node, │ │ │ │ + * DXImageTransform.Matrix and BasicImage filters for rotation and │ │ │ │ + * opacity, and a 3-step hack to remove rendering artefacts from the │ │ │ │ + * graphic and preserve the ability of graphics to trigger events. │ │ │ │ + * Finally, OpenLayers methods are used to determine the correct │ │ │ │ + * insertion point of the rotated image, because DXImageTransform.Matrix │ │ │ │ + * does the rotation without the ability to specify a rotation center │ │ │ │ + * point. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * xOffset - {Number} rotation center relative to image, x coordinate │ │ │ │ + * yOffset - {Number} rotation center relative to image, y coordinate │ │ │ │ + * style - {Object} │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ + graphicRotate: function(node, xOffset, yOffset, style) { │ │ │ │ + var style = style || node._style; │ │ │ │ + var rotation = style.rotation || 0; │ │ │ │ │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.ArcIMS(this.name, │ │ │ │ - this.url, │ │ │ │ - this.getOptions()); │ │ │ │ - } │ │ │ │ + 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; │ │ │ │ │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + // will be called again by the onreadystate handler │ │ │ │ + return; │ │ │ │ + } else { │ │ │ │ + size = Math.max(style.graphicWidth, style.graphicHeight); │ │ │ │ + aspectRatio = style.graphicWidth / style.graphicHeight; │ │ │ │ + } │ │ │ │ │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ + 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"; │ │ │ │ │ │ │ │ - return obj; │ │ │ │ - }, │ │ │ │ + // 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')"; │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.ArcIMS" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/ArcGIS93Rest.js │ │ │ │ - ====================================================================== */ │ │ │ │ + var rot = rotation * Math.PI / 180; │ │ │ │ + var sintheta = Math.sin(rot); │ │ │ │ + var costheta = Math.cos(rot); │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + // do the rotation on the image │ │ │ │ + var filter = │ │ │ │ + "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta + │ │ │ │ + ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta + │ │ │ │ + ",SizingMethod='auto expand')\n"; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/Grid.js │ │ │ │ - */ │ │ │ │ + // 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; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.ArcGIS93Rest │ │ │ │ - * Instances of OpenLayers.Layer.ArcGIS93Rest are used to display data from │ │ │ │ - * ESRI ArcGIS Server 9.3 (and up?) Mapping Services using the REST API. │ │ │ │ - * Create a new ArcGIS93Rest layer with the <OpenLayers.Layer.ArcGIS93Rest> │ │ │ │ - * constructor. More detail on the REST API is available at │ │ │ │ - * http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/index.html ; │ │ │ │ - * specifically, the URL provided to this layer should be an export service │ │ │ │ - * URL: http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.ArcGIS93Rest = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + // 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(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: DEFAULT_PARAMS │ │ │ │ - * {Object} Hashtable of default parameter key/value pairs │ │ │ │ - */ │ │ │ │ - DEFAULT_PARAMS: { │ │ │ │ - format: "png" │ │ │ │ + node.style.left = Math.round( │ │ │ │ + parseInt(node.style.left) + imgBounds.left) + "px"; │ │ │ │ + node.style.top = Math.round( │ │ │ │ + parseInt(node.style.top) - imgBounds.bottom) + "px"; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} Default is true for ArcGIS93Rest layer │ │ │ │ + * Method: postDraw │ │ │ │ + * Does some node postprocessing to work around browser issues: │ │ │ │ + * - Some versions of Internet Explorer seem to be unable to set fillcolor │ │ │ │ + * and strokecolor to "none" correctly before the fill node is appended │ │ │ │ + * to a visible vml node. This method takes care of that and sets │ │ │ │ + * fillcolor and strokecolor again if needed. │ │ │ │ + * - In some cases, a node won't become visible after being drawn. Setting │ │ │ │ + * style.visibility to "visible" works around that. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.ArcGIS93Rest │ │ │ │ - * Create a new ArcGIS93Rest layer object. │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * var arcims = new OpenLayers.Layer.ArcGIS93Rest("MyName", │ │ │ │ - * "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer/export", │ │ │ │ - * { │ │ │ │ - * layers: "0,1,2" │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ + * 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: │ │ │ │ - * name - {String} A name for the layer │ │ │ │ - * url - {String} Base url for the ArcGIS server REST service │ │ │ │ - * options - {Object} An object with key/value pairs representing the │ │ │ │ - * options and option values. │ │ │ │ - * │ │ │ │ - * Valid Options: │ │ │ │ - * format - {String} MIME type of desired image type. │ │ │ │ - * layers - {String} Comma-separated list of layers to display. │ │ │ │ - * srs - {String} Projection ID. │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ */ │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - var newArguments = []; │ │ │ │ - //uppercase params │ │ │ │ - params = OpenLayers.Util.upperCaseObject(params); │ │ │ │ - newArguments.push(name, url, params, options); │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ - OpenLayers.Util.applyDefaults( │ │ │ │ - this.params, │ │ │ │ - OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS) │ │ │ │ - ); │ │ │ │ + setNodeDimension: function(node, geometry) { │ │ │ │ │ │ │ │ - //layer is transparent │ │ │ │ - if (this.params.TRANSPARENT && │ │ │ │ - this.params.TRANSPARENT.toString().toLowerCase() == "true") { │ │ │ │ + var bbox = geometry.getBounds(); │ │ │ │ + if (bbox) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ - // unless explicitly set in options, make layer an overlay │ │ │ │ - if ((options == null) || (!options.isBaseLayer)) { │ │ │ │ - this.isBaseLayer = false; │ │ │ │ - } │ │ │ │ + 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); │ │ │ │ │ │ │ │ - // jpegs can never be transparent, so intelligently switch the │ │ │ │ - // format, depending on the browser's capabilities │ │ │ │ - if (this.params.FORMAT == "jpg") { │ │ │ │ - this.params.FORMAT = OpenLayers.Util.alphaHack() ? "gif" : │ │ │ │ - "png"; │ │ │ │ - } │ │ │ │ + // 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: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: dashStyle │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * style - {Object} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer.ArcGIS93Rest>} An exact clone of this layer │ │ │ │ + * {String} A VML compliant 'stroke-dasharray' value │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.ArcGIS93Rest(this.name, │ │ │ │ - this.url, │ │ │ │ - this.params, │ │ │ │ - this.getOptions()); │ │ │ │ + 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"; │ │ │ │ } │ │ │ │ - │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - │ │ │ │ - return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ - * Return an image url this layer. │ │ │ │ + * Method: createNode │ │ │ │ + * Create a new node │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the │ │ │ │ - * request. │ │ │ │ + * type - {String} Kind of node to draw │ │ │ │ + * id - {String} Id for node │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A string with the map image's url. │ │ │ │ + * {DOMElement} A new node of the given type and id │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - │ │ │ │ - // ArcGIS Server only wants the numeric portion of the projection ID. │ │ │ │ - var projWords = this.projection.getCode().split(":"); │ │ │ │ - var srid = projWords[projWords.length - 1]; │ │ │ │ + createNode: function(type, id) { │ │ │ │ + var node = document.createElement(type); │ │ │ │ + if (id) { │ │ │ │ + node.id = id; │ │ │ │ + } │ │ │ │ │ │ │ │ - var imageSize = this.getImageSize(); │ │ │ │ - var newParams = { │ │ │ │ - 'BBOX': bounds.toBBOX(), │ │ │ │ - 'SIZE': imageSize.w + "," + imageSize.h, │ │ │ │ - // We always want image, the other options were json, image with a whole lotta html around it, etc. │ │ │ │ - 'F': "image", │ │ │ │ - 'BBOXSR': srid, │ │ │ │ - 'IMAGESR': srid │ │ │ │ - }; │ │ │ │ + // IE hack to make elements unselectable, to prevent 'blue flash' │ │ │ │ + // while dragging vectors; #1410 │ │ │ │ + node.unselectable = 'on'; │ │ │ │ + node.onselectstart = OpenLayers.Function.False; │ │ │ │ │ │ │ │ - // Now add the filter parameters. │ │ │ │ - if (this.layerDefs) { │ │ │ │ - var layerDefStrList = []; │ │ │ │ - var layerID; │ │ │ │ - for (layerID in this.layerDefs) { │ │ │ │ - if (this.layerDefs.hasOwnProperty(layerID)) { │ │ │ │ - if (this.layerDefs[layerID]) { │ │ │ │ - layerDefStrList.push(layerID); │ │ │ │ - layerDefStrList.push(":"); │ │ │ │ - layerDefStrList.push(this.layerDefs[layerID]); │ │ │ │ - layerDefStrList.push(";"); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (layerDefStrList.length > 0) { │ │ │ │ - newParams['LAYERDEFS'] = layerDefStrList.join(""); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var requestString = this.getFullRequestString(newParams); │ │ │ │ - return requestString; │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setLayerFilter │ │ │ │ - * addTile creates a tile, initializes it, and adds it to the layer div. │ │ │ │ + * Method: nodeTypeCompare │ │ │ │ + * Determine whether a node is of a given type │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * id - {String} The id of the layer to which the filter applies. │ │ │ │ - * queryDef - {String} A sql-ish query filter, for more detail see the ESRI │ │ │ │ - * documentation at http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html │ │ │ │ + * node - {DOMElement} An VML element │ │ │ │ + * type - {String} Kind of node │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ */ │ │ │ │ - setLayerFilter: function(id, queryDef) { │ │ │ │ - if (!this.layerDefs) { │ │ │ │ - this.layerDefs = {}; │ │ │ │ + nodeTypeCompare: function(node, type) { │ │ │ │ + │ │ │ │ + //split type │ │ │ │ + var subType = type; │ │ │ │ + var splitIndex = subType.indexOf(":"); │ │ │ │ + if (splitIndex != -1) { │ │ │ │ + subType = subType.substr(splitIndex + 1); │ │ │ │ } │ │ │ │ - if (queryDef) { │ │ │ │ - this.layerDefs[id] = queryDef; │ │ │ │ - } else { │ │ │ │ - delete this.layerDefs[id]; │ │ │ │ + │ │ │ │ + //split nodeName │ │ │ │ + var nodeName = node.nodeName; │ │ │ │ + splitIndex = nodeName.indexOf(":"); │ │ │ │ + if (splitIndex != -1) { │ │ │ │ + nodeName = nodeName.substr(splitIndex + 1); │ │ │ │ } │ │ │ │ + │ │ │ │ + return (subType == nodeName); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clearLayerFilter │ │ │ │ - * Clears layer filters, either from a specific layer, │ │ │ │ - * or all of them. │ │ │ │ + * Method: createRenderRoot │ │ │ │ + * Create the renderer root │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * id - {String} The id of the layer from which to remove any │ │ │ │ - * filter. If unspecified/blank, all filters │ │ │ │ - * will be removed. │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The specific render engine's root element │ │ │ │ */ │ │ │ │ - clearLayerFilter: function(id) { │ │ │ │ - if (id) { │ │ │ │ - delete this.layerDefs[id]; │ │ │ │ - } else { │ │ │ │ - delete this.layerDefs; │ │ │ │ - } │ │ │ │ + createRenderRoot: function() { │ │ │ │ + return this.nodeFactory(this.container.id + "_vmlRoot", "div"); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: mergeNewParams │ │ │ │ - * Catch changeParams and uppercase the new params to be merged in │ │ │ │ - * before calling changeParams on the super class. │ │ │ │ - * │ │ │ │ - * Once params have been changed, the tiles will be reloaded with │ │ │ │ - * the new parameters. │ │ │ │ + * Method: createRoot │ │ │ │ + * Create the main root element │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * newParams - {Object} Hashtable of new params to use │ │ │ │ + * suffix - {String} suffix to append to the id │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - mergeNewParams: function(newParams) { │ │ │ │ - var upperParams = OpenLayers.Util.upperCaseObject(newParams); │ │ │ │ - var newArguments = [upperParams]; │ │ │ │ - return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, │ │ │ │ - newArguments); │ │ │ │ + createRoot: function(suffix) { │ │ │ │ + return this.nodeFactory(this.container.id + suffix, "olv:group"); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.ArcGIS93Rest" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/OSM.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/XYZ.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.OSM │ │ │ │ - * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap │ │ │ │ - * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use │ │ │ │ - * a different layer instead, you need to provide a different │ │ │ │ - * URL to the constructor. Here's an example for using OpenCycleMap: │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * new OpenLayers.Layer.OSM("OpenCycleMap", │ │ │ │ - * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ - * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ - * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.XYZ> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ + /************************************** │ │ │ │ + * * │ │ │ │ + * GEOMETRY DRAWING FUNCTIONS * │ │ │ │ + * * │ │ │ │ + **************************************/ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: name │ │ │ │ - * {String} The layer name. Defaults to "OpenStreetMap" if the first │ │ │ │ - * argument to the constructor is null or undefined. │ │ │ │ + * Method: drawPoint │ │ │ │ + * Render a point │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the point could not be drawn │ │ │ │ */ │ │ │ │ - name: "OpenStreetMap", │ │ │ │ + drawPoint: function(node, geometry) { │ │ │ │ + return this.drawCircle(node, geometry, 1); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: url │ │ │ │ - * {String} The tileset URL scheme. Defaults to │ │ │ │ - * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png │ │ │ │ - * (the official OSM tileset) if the second argument to the constructor │ │ │ │ - * is null or undefined. To use another tileset you can have something │ │ │ │ - * like this: │ │ │ │ - * (code) │ │ │ │ - * new OpenLayers.Layer.OSM("OpenCycleMap", │ │ │ │ - * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ - * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ - * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); │ │ │ │ - * (end) │ │ │ │ + * Method: drawCircle │ │ │ │ + * Render a circle. │ │ │ │ + * Size and Center a circle given geometry (x,y center) and radius │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * radius - {float} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the circle could not ne drawn │ │ │ │ */ │ │ │ │ - url: [ │ │ │ │ - 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png', │ │ │ │ - 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png', │ │ │ │ - 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png' │ │ │ │ - ], │ │ │ │ + drawCircle: function(node, geometry, radius) { │ │ │ │ + if (!isNaN(geometry.x) && !isNaN(geometry.y)) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: attribution │ │ │ │ - * {String} The layer attribution. │ │ │ │ - */ │ │ │ │ - attribution: "© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors", │ │ │ │ + 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"; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: sphericalMercator │ │ │ │ - * {Boolean} │ │ │ │ - */ │ │ │ │ - sphericalMercator: true, │ │ │ │ + var diameter = radius * 2; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: wrapDateLine │ │ │ │ - * {Boolean} │ │ │ │ - */ │ │ │ │ - wrapDateLine: true, │ │ │ │ + node.style.width = diameter + "px"; │ │ │ │ + node.style.height = diameter + "px"; │ │ │ │ + return node; │ │ │ │ + } │ │ │ │ + return false; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** APIProperty: tileOptions │ │ │ │ - * {Object} optional configuration options for <OpenLayers.Tile> instances │ │ │ │ - * created by this Layer. Default is │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * {crossOriginKeyword: 'anonymous'} │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * When using OSM tilesets other than the default ones, it may be │ │ │ │ - * necessary to set this to │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * {crossOriginKeyword: null} │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * if the server does not send Access-Control-Allow-Origin headers. │ │ │ │ - */ │ │ │ │ - tileOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.OSM │ │ │ │ - * │ │ │ │ + * Method: drawLineString │ │ │ │ + * Render a linestring. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} The layer name. │ │ │ │ - * url - {String} The tileset URL scheme. │ │ │ │ - * options - {Object} Configuration options for the layer. Any inherited │ │ │ │ - * layer option can be set in this object (e.g. │ │ │ │ - * <OpenLayers.Layer.Grid.buffer>). │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); │ │ │ │ - this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ - crossOriginKeyword: 'anonymous' │ │ │ │ - }, this.options && this.options.tileOptions); │ │ │ │ + drawLineString: function(node, geometry) { │ │ │ │ + return this.drawLine(node, geometry, false); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clone │ │ │ │ + * Method: drawLinearRing │ │ │ │ + * Render a linearring │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.OSM( │ │ │ │ - this.name, this.url, this.getOptions()); │ │ │ │ - } │ │ │ │ - obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj; │ │ │ │ + drawLinearRing: function(node, geometry) { │ │ │ │ + return this.drawLine(node, geometry, true); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.OSM" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/MapServer.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + /** │ │ │ │ + * Method: DrawLine │ │ │ │ + * Render a line. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * closeLine - {Boolean} Close the line? (make it a ring?) │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + drawLine: function(node, geometry, closeLine) { │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/Grid.js │ │ │ │ - */ │ │ │ │ + this.setNodeDimension(node, geometry); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.MapServer │ │ │ │ - * Instances of OpenLayers.Layer.MapServer are used to display │ │ │ │ - * data from a MapServer CGI instance. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var numComponents = geometry.components.length; │ │ │ │ + var parts = new Array(numComponents); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: DEFAULT_PARAMS │ │ │ │ - * {Object} Hashtable of default parameter key/value pairs │ │ │ │ - */ │ │ │ │ - DEFAULT_PARAMS: { │ │ │ │ - mode: "map", │ │ │ │ - map_imagetype: "png" │ │ │ │ + var comp, x, y; │ │ │ │ + for (var i = 0; i < numComponents; i++) { │ │ │ │ + comp = geometry.components[i]; │ │ │ │ + x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0; │ │ │ │ + y = (comp.y / resolution - this.offset.y) | 0; │ │ │ │ + parts[i] = " " + x + "," + y + " l "; │ │ │ │ + } │ │ │ │ + var end = (closeLine) ? " x e" : " e"; │ │ │ │ + node.path = "m" + parts.join("") + end; │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.MapServer │ │ │ │ - * Create a new MapServer layer object │ │ │ │ - * │ │ │ │ + * Method: drawPolygon │ │ │ │ + * Render a polygon │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} A name for the layer │ │ │ │ - * url - {String} Base url for the MapServer CGI │ │ │ │ - * (e.g. http://www2.dmsolutions.ca/cgi-bin/mapserv) │ │ │ │ - * params - {Object} An object with key/value pairs representing the │ │ │ │ - * GetMap query string parameters and parameter values. │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments); │ │ │ │ + drawPolygon: function(node, geometry) { │ │ │ │ + this.setNodeDimension(node, geometry); │ │ │ │ │ │ │ │ - this.params = OpenLayers.Util.applyDefaults( │ │ │ │ - this.params, this.DEFAULT_PARAMS │ │ │ │ - ); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ - // unless explicitly set in options, if the layer is transparent, │ │ │ │ - // it will be an overlay │ │ │ │ - if (options == null || options.isBaseLayer == null) { │ │ │ │ - this.isBaseLayer = ((this.params.transparent != "true") && │ │ │ │ - (this.params.transparent != true)); │ │ │ │ + var path = []; │ │ │ │ + var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y; │ │ │ │ + for (j = 0, jj = geometry.components.length; j < jj; j++) { │ │ │ │ + path.push("m"); │ │ │ │ + points = geometry.components[j].components; │ │ │ │ + // we only close paths of interior rings with area │ │ │ │ + area = (j === 0); │ │ │ │ + first = null; │ │ │ │ + second = null; │ │ │ │ + for (i = 0, ii = points.length; i < ii; i++) { │ │ │ │ + comp = points[i]; │ │ │ │ + x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0; │ │ │ │ + y = (comp.y / resolution - this.offset.y) | 0; │ │ │ │ + pathComp = " " + x + "," + y; │ │ │ │ + path.push(pathComp); │ │ │ │ + if (i == 0) { │ │ │ │ + path.push(" l"); │ │ │ │ + } │ │ │ │ + if (!area) { │ │ │ │ + // IE improperly renders sub-paths that have no area. │ │ │ │ + // Instead of checking the area of every ring, we confirm │ │ │ │ + // the ring has at least three distinct points. This does │ │ │ │ + // not catch all non-zero area cases, but it greatly improves │ │ │ │ + // interior ring digitizing and is a minor performance hit │ │ │ │ + // when rendering rings with many points. │ │ │ │ + if (!first) { │ │ │ │ + first = pathComp; │ │ │ │ + } else if (first != pathComp) { │ │ │ │ + if (!second) { │ │ │ │ + second = pathComp; │ │ │ │ + } else if (second != pathComp) { │ │ │ │ + // stop looking │ │ │ │ + area = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + path.push(area ? " x " : " "); │ │ │ │ } │ │ │ │ + path.push("e"); │ │ │ │ + node.path = path.join(""); │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ - * │ │ │ │ + * Method: drawRectangle │ │ │ │ + * Render a rectangle │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer.MapServer>} An exact clone of this layer │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.MapServer(this.name, │ │ │ │ - this.url, │ │ │ │ - this.params, │ │ │ │ - this.getOptions()); │ │ │ │ - } │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + drawRectangle: function(node, geometry) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ + node.style.left = (((geometry.x - this.featureDx) / resolution - this.offset.x) | 0) + "px"; │ │ │ │ + node.style.top = ((geometry.y / resolution - this.offset.y) | 0) + "px"; │ │ │ │ + node.style.width = ((geometry.width / resolution) | 0) + "px"; │ │ │ │ + node.style.height = ((geometry.height / resolution) | 0) + "px"; │ │ │ │ │ │ │ │ - return obj; │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ - * Return a query string for this layer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox │ │ │ │ - * for the request │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A string with the layer's url and parameters and also │ │ │ │ - * the passed-in bounds and appropriate tile size specified │ │ │ │ - * as parameters. │ │ │ │ + * Method: drawText │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * featureId - {String} │ │ │ │ + * style - │ │ │ │ + * location - {<OpenLayers.Geometry.Point>} │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - // Make a list, so that getFullRequestString uses literal "," │ │ │ │ - var extent = [bounds.left, bounds.bottom, bounds.right, bounds.top]; │ │ │ │ + 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 imageSize = this.getImageSize(); │ │ │ │ + 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"; │ │ │ │ │ │ │ │ - // make lists, so that literal ','s are used │ │ │ │ - var url = this.getFullRequestString({ │ │ │ │ - mapext: extent, │ │ │ │ - imgext: extent, │ │ │ │ - map_size: [imageSize.w, imageSize.h], │ │ │ │ - imgx: imageSize.w / 2, │ │ │ │ - imgy: imageSize.h / 2, │ │ │ │ - imgxy: [imageSize.w, imageSize.h] │ │ │ │ - }); │ │ │ │ + textbox.innerText = style.label; │ │ │ │ + │ │ │ │ + if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ + textbox.style.cursor = style.cursor; │ │ │ │ + } │ │ │ │ + if (style.fontColor) { │ │ │ │ + textbox.style.color = style.fontColor; │ │ │ │ + } │ │ │ │ + if (style.fontOpacity) { │ │ │ │ + textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')'; │ │ │ │ + } │ │ │ │ + if (style.fontFamily) { │ │ │ │ + textbox.style.fontFamily = style.fontFamily; │ │ │ │ + } │ │ │ │ + if (style.fontSize) { │ │ │ │ + textbox.style.fontSize = style.fontSize; │ │ │ │ + } │ │ │ │ + if (style.fontWeight) { │ │ │ │ + textbox.style.fontWeight = style.fontWeight; │ │ │ │ + } │ │ │ │ + if (style.fontStyle) { │ │ │ │ + textbox.style.fontStyle = style.fontStyle; │ │ │ │ + } │ │ │ │ + if (style.labelSelect === true) { │ │ │ │ + label._featureId = featureId; │ │ │ │ + textbox._featureId = featureId; │ │ │ │ + textbox._geometry = location; │ │ │ │ + textbox._geometryClass = location.CLASS_NAME; │ │ │ │ + } │ │ │ │ + textbox.style.whiteSpace = "nowrap"; │ │ │ │ + // fun with IE: IE7 in standards compliant mode does not display any │ │ │ │ + // text with a left inset of 0. So we set this to 1px and subtract one │ │ │ │ + // pixel later when we set label.style.left │ │ │ │ + textbox.inset = "1px,0px,0px,0px"; │ │ │ │ + │ │ │ │ + if (!label.parentNode) { │ │ │ │ + label.appendChild(textbox); │ │ │ │ + this.textRoot.appendChild(label); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var align = style.labelAlign || "cm"; │ │ │ │ + if (align.length == 1) { │ │ │ │ + align += "m"; │ │ │ │ + } │ │ │ │ + var xshift = textbox.clientWidth * │ │ │ │ + (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0, 1)]); │ │ │ │ + var yshift = textbox.clientHeight * │ │ │ │ + (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1, 1)]); │ │ │ │ + label.style.left = parseInt(label.style.left) - xshift - 1 + "px"; │ │ │ │ + label.style.top = parseInt(label.style.top) + yshift + "px"; │ │ │ │ │ │ │ │ - return url; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getFullRequestString │ │ │ │ - * combine the layer's url with its params and these newParams. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: moveRoot │ │ │ │ + * moves this renderer's root to a different renderer. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * newParams - {Object} New parameters that should be added to the │ │ │ │ - * request string. │ │ │ │ - * altUrl - {String} (optional) Replace the URL in the full request │ │ │ │ - * string with the provided URL. │ │ │ │ + * renderer - {<OpenLayers.Renderer>} target renderer for the moved root │ │ │ │ + * root - {DOMElement} optional root node. To be used when this renderer │ │ │ │ + * holds roots from multiple layers to tell this method which one to │ │ │ │ + * detach │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {String} A string with the layer's url and parameters embedded in it. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true if successful, false otherwise │ │ │ │ */ │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ - // use layer's url unless altUrl passed in │ │ │ │ - var url = (altUrl == null) ? this.url : altUrl; │ │ │ │ + moveRoot: function(renderer) { │ │ │ │ + var layer = this.map.getLayer(renderer.container.id); │ │ │ │ + if (layer instanceof OpenLayers.Layer.Vector.RootContainer) { │ │ │ │ + layer = this.map.getLayer(this.container.id); │ │ │ │ + } │ │ │ │ + layer && layer.renderer.clear(); │ │ │ │ + OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments); │ │ │ │ + layer && layer.redraw(); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // 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: importSymbol │ │ │ │ + * add a new symbol definition from the rendererer's symbol hash │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * graphicName - {String} name of the symbol to import │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} - hash of {DOMElement} "symbol" and {Number} "size" │ │ │ │ + */ │ │ │ │ + importSymbol: function(graphicName) { │ │ │ │ + var id = this.container.id + "-" + graphicName; │ │ │ │ │ │ │ │ - // 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); │ │ │ │ + // check if symbol already exists in the cache │ │ │ │ + var cache = this.symbolCache[id]; │ │ │ │ + if (cache) { │ │ │ │ + return cache; │ │ │ │ } │ │ │ │ │ │ │ │ - // ignore parameters that are already in the url search string │ │ │ │ - var urlParams = OpenLayers.Util.upperCaseObject( │ │ │ │ - OpenLayers.Util.getParameters(url)); │ │ │ │ - for (var key in allParams) { │ │ │ │ - if (key.toUpperCase() in urlParams) { │ │ │ │ - delete allParams[key]; │ │ │ │ - } │ │ │ │ + var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ + if (!symbol) { │ │ │ │ + throw new Error(graphicName + ' is not a valid symbol name'); │ │ │ │ } │ │ │ │ - paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ │ │ │ │ - // requestString always starts with url │ │ │ │ - var requestString = url; │ │ │ │ + var symbolExtent = new OpenLayers.Bounds( │ │ │ │ + Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ │ │ │ │ - // MapServer needs '+' seperating things like bounds/height/width. │ │ │ │ - // Since typically this is URL encoded, we use a slight hack: we │ │ │ │ - // depend on the list-like functionality of getParameterString to │ │ │ │ - // leave ',' only in the case of list items (since otherwise it is │ │ │ │ - // encoded) then do a regular expression replace on the , characters │ │ │ │ - // to '+' │ │ │ │ - // │ │ │ │ - paramsString = paramsString.replace(/,/g, "+"); │ │ │ │ + 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); │ │ │ │ │ │ │ │ - if (paramsString != "") { │ │ │ │ - var lastServerChar = url.charAt(url.length - 1); │ │ │ │ - if ((lastServerChar == "&") || (lastServerChar == "?")) { │ │ │ │ - requestString += paramsString; │ │ │ │ - } else { │ │ │ │ - if (url.indexOf('?') == -1) { │ │ │ │ - //serverPath has no ? -- add one │ │ │ │ - requestString += '?' + paramsString; │ │ │ │ - } else { │ │ │ │ - //serverPath contains ?, so must already have paramsString at the end │ │ │ │ - requestString += '&' + paramsString; │ │ │ │ - } │ │ │ │ + pathitems.push(x); │ │ │ │ + pathitems.push(y); │ │ │ │ + if (i == 0) { │ │ │ │ + pathitems.push("l"); │ │ │ │ } │ │ │ │ } │ │ │ │ - return requestString; │ │ │ │ + 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.Layer.MapServer" │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.VML" │ │ │ │ }); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT │ │ │ │ + * {Object} │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.VML.LABEL_SHIFT = { │ │ │ │ + "l": 0, │ │ │ │ + "c": .5, │ │ │ │ + "r": 1, │ │ │ │ + "t": 0, │ │ │ │ + "m": .5, │ │ │ │ + "b": 1 │ │ │ │ +}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/KaMap.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/Layer/Grid.js │ │ │ │ + * @requires OpenLayers/Renderer/Elements.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.KaMap │ │ │ │ + * Class: OpenLayers.Renderer.SVG │ │ │ │ * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ + * Inherits: │ │ │ │ + * - <OpenLayers.Renderer.Elements> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ +OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} KaMap Layer is always a base layer │ │ │ │ + * Property: xmlns │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + xmlns: "http://www.w3.org/2000/svg", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: DEFAULT_PARAMS │ │ │ │ - * {Object} parameters set by default. The default parameters set │ │ │ │ - * the format via the 'i' parameter to 'jpeg'. │ │ │ │ + * Property: xlinkns │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - DEFAULT_PARAMS: { │ │ │ │ - i: 'jpeg', │ │ │ │ - map: '' │ │ │ │ - }, │ │ │ │ + xlinkns: "http://www.w3.org/1999/xlink", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.KaMap │ │ │ │ + * Constant: MAX_PIXEL │ │ │ │ + * {Integer} Firefox has a limitation where values larger or smaller than │ │ │ │ + * about 15000 in an SVG document lock the browser up. This │ │ │ │ + * works around it. │ │ │ │ + */ │ │ │ │ + MAX_PIXEL: 15000, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: translationParameters │ │ │ │ + * {Object} Hash with "x" and "y" properties │ │ │ │ + */ │ │ │ │ + translationParameters: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: symbolMetrics │ │ │ │ + * {Object} Cache for symbol metrics according to their svg coordinate │ │ │ │ + * space. This is an object keyed by the symbol's id, and values are │ │ │ │ + * an array of [width, centerX, centerY]. │ │ │ │ + */ │ │ │ │ + symbolMetrics: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Renderer.SVG │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * url - {String} │ │ │ │ - * params - {Object} Parameters to be sent to the HTTP server in the │ │ │ │ - * query string for the tile. The format can be set via the 'i' │ │ │ │ - * parameter (defaults to jpg) , and the map should be set via │ │ │ │ - * the 'map' parameter. It has been reported that ka-Map may behave │ │ │ │ - * inconsistently if your format parameter does not match the format │ │ │ │ - * parameter configured in your config.php. (See ticket #327 for more │ │ │ │ - * information.) │ │ │ │ - * options - {Object} Additional options for the layer. Any of the │ │ │ │ - * APIProperties listed on this layer, and any layer types it │ │ │ │ - * extends, can be overridden through the options parameter. │ │ │ │ + * containerID - {String} │ │ │ │ */ │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments); │ │ │ │ - this.params = OpenLayers.Util.applyDefaults( │ │ │ │ - this.params, this.DEFAULT_PARAMS │ │ │ │ - ); │ │ │ │ + initialize: function(containerID) { │ │ │ │ + if (!this.supported()) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + OpenLayers.Renderer.Elements.prototype.initialize.apply(this, │ │ │ │ + arguments); │ │ │ │ + this.translationParameters = { │ │ │ │ + x: 0, │ │ │ │ + y: 0 │ │ │ │ + }; │ │ │ │ + │ │ │ │ + this.symbolMetrics = {}; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ + * APIMethod: supported │ │ │ │ * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Whether or not the browser supports the SVG renderer │ │ │ │ + */ │ │ │ │ + supported: function() { │ │ │ │ + var svgFeature = "http://www.w3.org/TR/SVG11/feature#"; │ │ │ │ + return (document.implementation && │ │ │ │ + (document.implementation.hasFeature("org.w3c.svg", "1.0") || │ │ │ │ + document.implementation.hasFeature(svgFeature + "SVG", "1.1") || │ │ │ │ + document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1"))); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: inValidRange │ │ │ │ + * See #669 for more information │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * 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: │ │ │ │ - * {String} A string with the layer's url and parameters and also the │ │ │ │ - * passed-in bounds and appropriate tile size specified as │ │ │ │ - * parameters │ │ │ │ + * {Boolean} Whether or not the 'x' and 'y' coordinates are in the │ │ │ │ + * valid range. │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var mapRes = this.map.getResolution(); │ │ │ │ - var scale = Math.round((this.map.getScale() * 10000)) / 10000; │ │ │ │ - var pX = Math.round(bounds.left / mapRes); │ │ │ │ - var pY = -Math.round(bounds.top / mapRes); │ │ │ │ - return this.getFullRequestString({ │ │ │ │ - t: pY, │ │ │ │ - l: pX, │ │ │ │ - s: scale │ │ │ │ - }); │ │ │ │ + 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: calculateGridLayout │ │ │ │ - * ka-Map uses the center point of the map as an origin for │ │ │ │ - * its tiles. Override calculateGridLayout to center tiles │ │ │ │ - * correctly for this case. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: setExtent │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bound>} │ │ │ │ - * origin - {<OpenLayers.LonLat>} │ │ │ │ - * resolution - {Number} │ │ │ │ - * │ │ │ │ + * extent - {<OpenLayers.Bounds>} │ │ │ │ + * resolutionChanged - {Boolean} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Object} Object containing properties tilelon, tilelat, startcol, │ │ │ │ - * startrow │ │ │ │ + * {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. │ │ │ │ */ │ │ │ │ - calculateGridLayout: function(bounds, origin, resolution) { │ │ │ │ - var tilelon = resolution * this.tileSize.w; │ │ │ │ - var tilelat = resolution * this.tileSize.h; │ │ │ │ + setExtent: function(extent, resolutionChanged) { │ │ │ │ + var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); │ │ │ │ │ │ │ │ - var offsetlon = bounds.left; │ │ │ │ - var tilecol = Math.floor(offsetlon / tilelon) - this.buffer; │ │ │ │ + var resolution = this.getResolution(), │ │ │ │ + left = -extent.left / resolution, │ │ │ │ + top = extent.top / resolution; │ │ │ │ │ │ │ │ - var offsetlat = bounds.top; │ │ │ │ - var tilerow = Math.floor(offsetlat / tilelat) + this.buffer; │ │ │ │ + // 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; │ │ │ │ │ │ │ │ - return { │ │ │ │ - tilelon: tilelon, │ │ │ │ - tilelat: tilelat, │ │ │ │ - startcol: tilecol, │ │ │ │ - startrow: tilerow │ │ │ │ - }; │ │ │ │ + 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: getTileBoundsForGridIndex │ │ │ │ - * │ │ │ │ + * Method: translate │ │ │ │ + * Transforms the SVG coordinate system │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * row - {Number} The row of the grid │ │ │ │ - * col - {Number} The column of the grid │ │ │ │ - * │ │ │ │ + * x - {Float} │ │ │ │ + * y - {Float} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} The bounds for the tile at (row, col) │ │ │ │ + * {Boolean} true if the translation parameters are in the valid coordinates │ │ │ │ + * range, false otherwise. │ │ │ │ */ │ │ │ │ - getTileBoundsForGridIndex: function(row, col) { │ │ │ │ - var origin = this.getTileOrigin(); │ │ │ │ - var tileLayout = this.gridLayout; │ │ │ │ - var tilelon = tileLayout.tilelon; │ │ │ │ - var tilelat = tileLayout.tilelat; │ │ │ │ - var minX = (tileLayout.startcol + col) * tilelon; │ │ │ │ - var minY = (tileLayout.startrow - row) * tilelat; │ │ │ │ - return new OpenLayers.Bounds( │ │ │ │ - minX, minY, │ │ │ │ - minX + tilelon, minY + tilelat │ │ │ │ - ); │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ + * Method: setSize │ │ │ │ + * Sets the size of the drawing surface. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {Object} │ │ │ │ + * Parameters: │ │ │ │ + * size - {<OpenLayers.Size>} The size of the drawing surface │ │ │ │ + */ │ │ │ │ + setSize: function(size) { │ │ │ │ + OpenLayers.Renderer.prototype.setSize.apply(this, arguments); │ │ │ │ + │ │ │ │ + this.rendererRoot.setAttributeNS(null, "width", this.size.w); │ │ │ │ + this.rendererRoot.setAttributeNS(null, "height", this.size.h); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getNodeType │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer.Kamap>} An exact clone of this OpenLayers.Layer.KaMap │ │ │ │ + * {String} The corresponding node type for the specified geometry │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.KaMap(this.name, │ │ │ │ - this.url, │ │ │ │ - this.params, │ │ │ │ - this.getOptions()); │ │ │ │ + getNodeType: function(geometry, style) { │ │ │ │ + var nodeType = null; │ │ │ │ + switch (geometry.CLASS_NAME) { │ │ │ │ + case "OpenLayers.Geometry.Point": │ │ │ │ + if (style.externalGraphic) { │ │ │ │ + nodeType = "image"; │ │ │ │ + } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ + nodeType = "svg"; │ │ │ │ + } else { │ │ │ │ + nodeType = "circle"; │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Rectangle": │ │ │ │ + nodeType = "rect"; │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LineString": │ │ │ │ + nodeType = "polyline"; │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LinearRing": │ │ │ │ + nodeType = "polygon"; │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Polygon": │ │ │ │ + case "OpenLayers.Geometry.Curve": │ │ │ │ + nodeType = "path"; │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + break; │ │ │ │ } │ │ │ │ + return nodeType; │ │ │ │ + }, │ │ │ │ │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + /** │ │ │ │ + * Method: setStyle │ │ │ │ + * Use to set all the style attributes to a SVG node. │ │ │ │ + * │ │ │ │ + * Takes care to adjust stroke width and point radius to be │ │ │ │ + * resolution-relative │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {SVGDomElement} An SVG element to decorate │ │ │ │ + * style - {Object} │ │ │ │ + * options - {Object} Currently supported options include │ │ │ │ + * 'isFilled' {Boolean} and │ │ │ │ + * 'isStroked' {Boolean} │ │ │ │ + */ │ │ │ │ + setStyle: function(node, style, options) { │ │ │ │ + style = style || node._style; │ │ │ │ + options = options || node._options; │ │ │ │ │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - if (this.tileSize != null) { │ │ │ │ - obj.tileSize = this.tileSize.clone(); │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ } │ │ │ │ │ │ │ │ - // we do not want to copy reference to grid, so we make a new array │ │ │ │ - obj.grid = []; │ │ │ │ + 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); │ │ │ │ │ │ │ │ - return obj; │ │ │ │ - }, │ │ │ │ + var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getTileBounds │ │ │ │ - * Returns The tile bounds for a layer given a pixel location. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location. │ │ │ │ - */ │ │ │ │ - getTileBounds: function(viewPortPx) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var tileMapWidth = resolution * this.tileSize.w; │ │ │ │ - var tileMapHeight = resolution * this.tileSize.h; │ │ │ │ - var mapPoint = this.getLonLatFromViewPortPx(viewPortPx); │ │ │ │ - var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth); │ │ │ │ - var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight); │ │ │ │ - return new OpenLayers.Bounds(tileLeft, tileBottom, │ │ │ │ - tileLeft + tileMapWidth, │ │ │ │ - tileBottom + tileMapHeight); │ │ │ │ - }, │ │ │ │ + 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_NAME: "OpenLayers.Layer.KaMap" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/KaMapCache.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); │ │ │ │ │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/Grid.js │ │ │ │ - * @requires OpenLayers/Layer/KaMap.js │ │ │ │ - */ │ │ │ │ + var rotation = style.rotation; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.KaMapCache │ │ │ │ - * │ │ │ │ - * This class is designed to talk directly to a web-accessible ka-Map │ │ │ │ - * cache generated by the precache2.php script. │ │ │ │ - * │ │ │ │ - * To create a a new KaMapCache layer, you must indicate also the "i" parameter │ │ │ │ - * (that will be used to calculate the file extension), and another special │ │ │ │ - * parameter, object names "metaTileSize", with "h" (height) and "w" (width) │ │ │ │ - * properties. │ │ │ │ - * │ │ │ │ - * // Create a new kaMapCache layer. │ │ │ │ - * var kamap_base = new OpenLayers.Layer.KaMapCache( │ │ │ │ - * "Satellite", │ │ │ │ - * "http://www.example.org/web/acessible/cache", │ │ │ │ - * {g: "satellite", map: "world", i: 'png24', metaTileSize: {w: 5, h: 5} } │ │ │ │ - * ); │ │ │ │ - * │ │ │ │ - * // Create an kaMapCache overlay layer (using "isBaseLayer: false"). │ │ │ │ - * // Forces the output to be a "gif", using the "i" parameter. │ │ │ │ - * var kamap_overlay = new OpenLayers.Layer.KaMapCache( │ │ │ │ - * "Streets", │ │ │ │ - * "http://www.example.org/web/acessible/cache", │ │ │ │ - * {g: "streets", map: "world", i: "gif", metaTileSize: {w: 5, h: 5} }, │ │ │ │ - * {isBaseLayer: false} │ │ │ │ - * ); │ │ │ │ - * │ │ │ │ - * The cache URLs must look like: │ │ │ │ - * var/cache/World/50000/Group_Name/def/t-440320/l20480 │ │ │ │ - * │ │ │ │ - * This means that the cache generated via tile.php will *not* work with │ │ │ │ - * this class, and should instead use the KaMap layer. │ │ │ │ - * │ │ │ │ - * More information is available in Ticket #1518. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.KaMap> │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.KaMapCache = OpenLayers.Class(OpenLayers.Layer.KaMap, { │ │ │ │ + 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] + ")"); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: IMAGE_EXTENSIONS │ │ │ │ - * {Object} Simple hash map to convert format to extension. │ │ │ │ - */ │ │ │ │ - IMAGE_EXTENSIONS: { │ │ │ │ - 'jpeg': 'jpg', │ │ │ │ - 'gif': 'gif', │ │ │ │ - 'png': 'png', │ │ │ │ - 'png8': 'png', │ │ │ │ - 'png24': 'png', │ │ │ │ - 'dithered': 'png' │ │ │ │ - }, │ │ │ │ + if (options.isFilled) { │ │ │ │ + node.setAttributeNS(null, "fill", style.fillColor); │ │ │ │ + node.setAttributeNS(null, "fill-opacity", style.fillOpacity); │ │ │ │ + } else { │ │ │ │ + node.setAttributeNS(null, "fill", "none"); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: DEFAULT_FORMAT │ │ │ │ - * {Object} Simple hash map to convert format to extension. │ │ │ │ - */ │ │ │ │ - DEFAULT_FORMAT: 'jpeg', │ │ │ │ + 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"); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Layer.KaMapCache │ │ │ │ + if (style.pointerEvents) { │ │ │ │ + node.setAttributeNS(null, "pointer-events", style.pointerEvents); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (style.cursor != null) { │ │ │ │ + node.setAttributeNS(null, "cursor", style.cursor); │ │ │ │ + } │ │ │ │ + │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: dashStyle │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * url - {String} │ │ │ │ - * params - {Object} Parameters to be sent to the HTTP server in the │ │ │ │ - * query string for the tile. The format can be set via the 'i' │ │ │ │ - * parameter (defaults to jpg) , and the map should be set via │ │ │ │ - * the 'map' parameter. It has been reported that ka-Map may behave │ │ │ │ - * inconsistently if your format parameter does not match the format │ │ │ │ - * parameter configured in your config.php. (See ticket #327 for more │ │ │ │ - * information.) │ │ │ │ - * options - {Object} Additional options for the layer. Any of the │ │ │ │ - * APIProperties listed on this layer, and any layer types it │ │ │ │ - * extends, can be overridden through the options parameter. │ │ │ │ + * style - {Object} │ │ │ │ + * widthFactor - {Number} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A SVG compliant 'stroke-dasharray' value │ │ │ │ */ │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - OpenLayers.Layer.KaMap.prototype.initialize.apply(this, arguments); │ │ │ │ - this.extension = this.IMAGE_EXTENSIONS[this.params.i.toLowerCase() || this.DEFAULT_FORMAT]; │ │ │ │ + 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: getURL │ │ │ │ + /** │ │ │ │ + * Method: createNode │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * type - {String} Kind of node to draw │ │ │ │ + * id - {String} Id for node │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A string with the layer's url and parameters and also the │ │ │ │ - * passed-in bounds and appropriate tile size specified as │ │ │ │ - * parameters │ │ │ │ + * {DOMElement} A new node of the given type and id │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var mapRes = this.map.getResolution(); │ │ │ │ - var scale = Math.round((this.map.getScale() * 10000)) / 10000; │ │ │ │ - var pX = Math.round(bounds.left / mapRes); │ │ │ │ - var pY = -Math.round(bounds.top / mapRes); │ │ │ │ - │ │ │ │ - var metaX = Math.floor(pX / this.tileSize.w / this.params.metaTileSize.w) * this.tileSize.w * this.params.metaTileSize.w; │ │ │ │ - var metaY = Math.floor(pY / this.tileSize.h / this.params.metaTileSize.h) * this.tileSize.h * this.params.metaTileSize.h; │ │ │ │ - │ │ │ │ - var components = [ │ │ │ │ - "/", │ │ │ │ - this.params.map, │ │ │ │ - "/", │ │ │ │ - scale, │ │ │ │ - "/", │ │ │ │ - this.params.g.replace(/\s/g, '_'), │ │ │ │ - "/def/t", │ │ │ │ - metaY, │ │ │ │ - "/l", │ │ │ │ - metaX, │ │ │ │ - "/t", │ │ │ │ - pY, │ │ │ │ - "l", │ │ │ │ - pX, │ │ │ │ - ".", │ │ │ │ - this.extension │ │ │ │ - ]; │ │ │ │ - │ │ │ │ - var url = this.url; │ │ │ │ - │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(components.join(''), url); │ │ │ │ + createNode: function(type, id) { │ │ │ │ + var node = document.createElementNS(this.xmlns, type); │ │ │ │ + if (id) { │ │ │ │ + node.setAttributeNS(null, "id", id); │ │ │ │ } │ │ │ │ - return url + components.join(""); │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.KaMapCache" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/Image.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer.js │ │ │ │ - * @requires OpenLayers/Tile/Image.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.Image │ │ │ │ - * Instances of OpenLayers.Layer.Image are used to display data from a web │ │ │ │ - * accessible image as a map layer. Create a new image layer with the │ │ │ │ - * <OpenLayers.Layer.Image> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: isBaseLayer │ │ │ │ - * {Boolean} The layer is a base layer. Default is true. Set this property │ │ │ │ - * in the layer options │ │ │ │ + /** │ │ │ │ + * Method: nodeTypeCompare │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {SVGDomElement} An SVG element │ │ │ │ + * type - {String} Kind of node │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + nodeTypeCompare: function(node, type) { │ │ │ │ + return (type == node.nodeName); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: url │ │ │ │ - * {String} URL of the image to use │ │ │ │ + * Method: createRenderRoot │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The specific render engine's root element │ │ │ │ */ │ │ │ │ - url: null, │ │ │ │ + createRenderRoot: function() { │ │ │ │ + var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg"); │ │ │ │ + svg.style.display = "block"; │ │ │ │ + return svg; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: extent │ │ │ │ - * {<OpenLayers.Bounds>} The image bounds in map units. This extent will │ │ │ │ - * also be used as the default maxExtent for the layer. If you wish │ │ │ │ - * to have a maxExtent that is different than the image extent, set the │ │ │ │ - * maxExtent property of the options argument (as with any other layer). │ │ │ │ + * Method: createRoot │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * suffix - {String} suffix to append to the id │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - extent: null, │ │ │ │ + createRoot: function(suffix) { │ │ │ │ + return this.nodeFactory(this.container.id + suffix, "g"); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: size │ │ │ │ - * {<OpenLayers.Size>} The image size in pixels │ │ │ │ + * Method: createDefs │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The element to which we'll add the symbol definitions │ │ │ │ */ │ │ │ │ - size: null, │ │ │ │ + createDefs: function() { │ │ │ │ + var defs = this.nodeFactory(this.container.id + "_defs", "defs"); │ │ │ │ + this.rendererRoot.appendChild(defs); │ │ │ │ + return defs; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: tile │ │ │ │ - * {<OpenLayers.Tile.Image>} │ │ │ │ - */ │ │ │ │ - tile: null, │ │ │ │ + /************************************** │ │ │ │ + * * │ │ │ │ + * GEOMETRY DRAWING FUNCTIONS * │ │ │ │ + * * │ │ │ │ + **************************************/ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: aspectRatio │ │ │ │ - * {Float} The ratio of height/width represented by a single pixel in the │ │ │ │ - * graphic │ │ │ │ + * Method: drawPoint │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the renderer could not draw the point │ │ │ │ */ │ │ │ │ - aspectRatio: null, │ │ │ │ + drawPoint: function(node, geometry) { │ │ │ │ + return this.drawCircle(node, geometry, 1); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.Image │ │ │ │ - * Create a new image layer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} A name for the layer. │ │ │ │ - * url - {String} Relative or absolute path to the image │ │ │ │ - * extent - {<OpenLayers.Bounds>} The extent represented by the image │ │ │ │ - * size - {<OpenLayers.Size>} The size (in pixels) of the image │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * Method: drawCircle │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * radius - {Float} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the renderer could not draw the circle │ │ │ │ */ │ │ │ │ - initialize: function(name, url, extent, size, options) { │ │ │ │ - this.url = url; │ │ │ │ - this.extent = extent; │ │ │ │ - this.maxExtent = extent; │ │ │ │ - this.size = size; │ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, [name, options]); │ │ │ │ + drawCircle: function(node, geometry, radius) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var x = ((geometry.x - this.featureDx) / resolution + this.left); │ │ │ │ + var y = (this.top - geometry.y / resolution); │ │ │ │ + │ │ │ │ + if (this.inValidRange(x, y)) { │ │ │ │ + node.setAttributeNS(null, "cx", x); │ │ │ │ + node.setAttributeNS(null, "cy", y); │ │ │ │ + node.setAttributeNS(null, "r", radius); │ │ │ │ + return node; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ │ │ │ │ - this.aspectRatio = (this.extent.getHeight() / this.size.h) / │ │ │ │ - (this.extent.getWidth() / this.size.w); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ - * Destroy this layer │ │ │ │ + * Method: drawLineString │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components of │ │ │ │ + * the linestring, or false if nothing could be drawn │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.tile) { │ │ │ │ - this.removeTileMonitoringHooks(this.tile); │ │ │ │ - this.tile.destroy(); │ │ │ │ - this.tile = null; │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ - * │ │ │ │ - * Paramters: │ │ │ │ - * obj - {Object} An optional layer (is this ever used?) │ │ │ │ - * │ │ │ │ + * Method: drawLinearRing │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer.Image>} An exact copy of this layer │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components │ │ │ │ + * of the linear ring, or false if nothing could be drawn │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Image(this.name, │ │ │ │ - this.url, │ │ │ │ - this.extent, │ │ │ │ - this.size, │ │ │ │ - this.getOptions()); │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ - │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - │ │ │ │ - return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setMap │ │ │ │ + * Method: drawPolygon │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components │ │ │ │ + * of the polygon, or false if nothing could be drawn │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - /** │ │ │ │ - * If nothing to do with resolutions has been set, assume a single │ │ │ │ - * resolution determined by ratio*extent/size - if an image has a │ │ │ │ - * pixel aspect ratio different than one (as calculated above), the │ │ │ │ - * image will be stretched in one dimension only. │ │ │ │ - */ │ │ │ │ - if (this.options.maxResolution == null) { │ │ │ │ - this.options.maxResolution = this.aspectRatio * │ │ │ │ - this.extent.getWidth() / │ │ │ │ - this.size.w; │ │ │ │ + drawPolygon: function(node, geometry) { │ │ │ │ + var d = ""; │ │ │ │ + var draw = true; │ │ │ │ + var complete = true; │ │ │ │ + var linearRingResult, path; │ │ │ │ + for (var j = 0, len = geometry.components.length; j < len; j++) { │ │ │ │ + d += " M"; │ │ │ │ + linearRingResult = this.getComponentsString( │ │ │ │ + geometry.components[j].components, " "); │ │ │ │ + path = linearRingResult.path; │ │ │ │ + if (path) { │ │ │ │ + d += " " + path; │ │ │ │ + complete = linearRingResult.complete && complete; │ │ │ │ + } else { │ │ │ │ + draw = false; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + d += " z"; │ │ │ │ + if (draw) { │ │ │ │ + node.setAttributeNS(null, "d", d); │ │ │ │ + node.setAttributeNS(null, "fill-rule", "evenodd"); │ │ │ │ + return complete ? node : null; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ } │ │ │ │ - OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * Create the tile for the image or resize it for the new resolution │ │ │ │ + /** │ │ │ │ + * Method: drawRectangle │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the renderer could not draw the rectangle │ │ │ │ + */ │ │ │ │ + drawRectangle: function(node, geometry) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var x = ((geometry.x - this.featureDx) / resolution + this.left); │ │ │ │ + var y = (this.top - geometry.y / resolution); │ │ │ │ + │ │ │ │ + if (this.inValidRange(x, y)) { │ │ │ │ + node.setAttributeNS(null, "x", x); │ │ │ │ + node.setAttributeNS(null, "y", y); │ │ │ │ + node.setAttributeNS(null, "width", geometry.width / resolution); │ │ │ │ + node.setAttributeNS(null, "height", geometry.height / resolution); │ │ │ │ + return node; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: drawText │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * zoomChanged - {Boolean} │ │ │ │ - * dragging - {Boolean} │ │ │ │ + * featureId - {String} │ │ │ │ + * style - │ │ │ │ + * location - {<OpenLayers.Geometry.Point>} │ │ │ │ */ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ + drawText: function(featureId, style, location) { │ │ │ │ + var drawOutline = (!!style.labelOutlineWidth); │ │ │ │ + // First draw text in halo color and size and overlay the │ │ │ │ + // normal text afterwards │ │ │ │ + if (drawOutline) { │ │ │ │ + var outlineStyle = OpenLayers.Util.extend({}, style); │ │ │ │ + outlineStyle.fontColor = outlineStyle.labelOutlineColor; │ │ │ │ + outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor; │ │ │ │ + outlineStyle.fontStrokeWidth = style.labelOutlineWidth; │ │ │ │ + if (style.labelOutlineOpacity) { │ │ │ │ + outlineStyle.fontOpacity = style.labelOutlineOpacity; │ │ │ │ + } │ │ │ │ + delete outlineStyle.labelOutlineWidth; │ │ │ │ + this.drawText(featureId, outlineStyle, location); │ │ │ │ + } │ │ │ │ │ │ │ │ - var firstRendering = (this.tile == null); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ - if (zoomChanged || firstRendering) { │ │ │ │ + var x = ((location.x - this.featureDx) / resolution + this.left); │ │ │ │ + var y = (location.y / resolution - this.top); │ │ │ │ │ │ │ │ - //determine new tile size │ │ │ │ - this.setTileSize(); │ │ │ │ + var suffix = (drawOutline) ? this.LABEL_OUTLINE_SUFFIX : this.LABEL_ID_SUFFIX; │ │ │ │ + var label = this.nodeFactory(featureId + suffix, "text"); │ │ │ │ │ │ │ │ - //determine new position (upper left corner of new bounds) │ │ │ │ - var ulPx = this.map.getLayerPxFromLonLat({ │ │ │ │ - lon: this.extent.left, │ │ │ │ - lat: this.extent.top │ │ │ │ - }); │ │ │ │ + label.setAttributeNS(null, "x", x); │ │ │ │ + label.setAttributeNS(null, "y", -y); │ │ │ │ │ │ │ │ - if (firstRendering) { │ │ │ │ - //create the new tile │ │ │ │ - this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent, │ │ │ │ - null, this.tileSize); │ │ │ │ - this.addTileMonitoringHooks(this.tile); │ │ │ │ + if (style.fontColor) { │ │ │ │ + label.setAttributeNS(null, "fill", style.fontColor); │ │ │ │ + } │ │ │ │ + if (style.fontStrokeColor) { │ │ │ │ + label.setAttributeNS(null, "stroke", style.fontStrokeColor); │ │ │ │ + } │ │ │ │ + if (style.fontStrokeWidth) { │ │ │ │ + label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth); │ │ │ │ + } │ │ │ │ + if (style.fontOpacity) { │ │ │ │ + label.setAttributeNS(null, "opacity", style.fontOpacity); │ │ │ │ + } │ │ │ │ + if (style.fontFamily) { │ │ │ │ + label.setAttributeNS(null, "font-family", style.fontFamily); │ │ │ │ + } │ │ │ │ + if (style.fontSize) { │ │ │ │ + label.setAttributeNS(null, "font-size", style.fontSize); │ │ │ │ + } │ │ │ │ + if (style.fontWeight) { │ │ │ │ + label.setAttributeNS(null, "font-weight", style.fontWeight); │ │ │ │ + } │ │ │ │ + if (style.fontStyle) { │ │ │ │ + label.setAttributeNS(null, "font-style", style.fontStyle); │ │ │ │ + } │ │ │ │ + if (style.labelSelect === true) { │ │ │ │ + label.setAttributeNS(null, "pointer-events", "visible"); │ │ │ │ + label._featureId = featureId; │ │ │ │ + } else { │ │ │ │ + label.setAttributeNS(null, "pointer-events", "none"); │ │ │ │ + } │ │ │ │ + var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign; │ │ │ │ + label.setAttributeNS(null, "text-anchor", │ │ │ │ + OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle"); │ │ │ │ + │ │ │ │ + if (OpenLayers.IS_GECKO === true) { │ │ │ │ + label.setAttributeNS(null, "dominant-baseline", │ │ │ │ + OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central"); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var labelRows = style.label.split('\n'); │ │ │ │ + var numRows = labelRows.length; │ │ │ │ + while (label.childNodes.length > numRows) { │ │ │ │ + label.removeChild(label.lastChild); │ │ │ │ + } │ │ │ │ + for (var i = 0; i < numRows; i++) { │ │ │ │ + var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan"); │ │ │ │ + if (style.labelSelect === true) { │ │ │ │ + tspan._featureId = featureId; │ │ │ │ + tspan._geometry = location; │ │ │ │ + tspan._geometryClass = location.CLASS_NAME; │ │ │ │ + } │ │ │ │ + if (OpenLayers.IS_GECKO === false) { │ │ │ │ + tspan.setAttributeNS(null, "baseline-shift", │ │ │ │ + OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%"); │ │ │ │ + } │ │ │ │ + tspan.setAttribute("x", x); │ │ │ │ + if (i == 0) { │ │ │ │ + var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]]; │ │ │ │ + if (vfactor == null) { │ │ │ │ + vfactor = -.5; │ │ │ │ + } │ │ │ │ + tspan.setAttribute("dy", (vfactor * (numRows - 1)) + "em"); │ │ │ │ } else { │ │ │ │ - //just resize the tile and set it's new position │ │ │ │ - this.tile.size = this.tileSize.clone(); │ │ │ │ - this.tile.position = ulPx.clone(); │ │ │ │ + tspan.setAttribute("dy", "1em"); │ │ │ │ + } │ │ │ │ + tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i]; │ │ │ │ + if (!tspan.parentNode) { │ │ │ │ + label.appendChild(tspan); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (!label.parentNode) { │ │ │ │ + this.textRoot.appendChild(label); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getComponentString │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * components - {Array(<OpenLayers.Geometry.Point>)} Array of points │ │ │ │ + * separator - {String} character between coordinate pairs. Defaults to "," │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} hash with properties "path" (the string created from the │ │ │ │ + * components and "complete" (false if the renderer was unable to │ │ │ │ + * draw all components) │ │ │ │ + */ │ │ │ │ + getComponentsString: function(components, separator) { │ │ │ │ + var renderCmp = []; │ │ │ │ + var complete = true; │ │ │ │ + var len = components.length; │ │ │ │ + var strings = []; │ │ │ │ + var str, component; │ │ │ │ + for (var i = 0; i < len; i++) { │ │ │ │ + component = components[i]; │ │ │ │ + renderCmp.push(component); │ │ │ │ + str = this.getShortString(component); │ │ │ │ + if (str) { │ │ │ │ + strings.push(str); │ │ │ │ + } else { │ │ │ │ + // The current component is outside the valid range. Let's │ │ │ │ + // see if the previous or next component is inside the range. │ │ │ │ + // If so, add the coordinate of the intersection with the │ │ │ │ + // valid range bounds. │ │ │ │ + if (i > 0) { │ │ │ │ + if (this.getShortString(components[i - 1])) { │ │ │ │ + strings.push(this.clipLine(components[i], │ │ │ │ + components[i - 1])); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (i < len - 1) { │ │ │ │ + if (this.getShortString(components[i + 1])) { │ │ │ │ + strings.push(this.clipLine(components[i], │ │ │ │ + components[i + 1])); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + complete = false; │ │ │ │ } │ │ │ │ - this.tile.draw(); │ │ │ │ } │ │ │ │ + │ │ │ │ + return { │ │ │ │ + path: strings.join(separator || ","), │ │ │ │ + complete: complete │ │ │ │ + }; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Set the tile size based on the map size. │ │ │ │ + * Method: clipLine │ │ │ │ + * Given two points (one inside the valid range, and one outside), │ │ │ │ + * clips the line betweeen the two points so that the new points are both │ │ │ │ + * inside the valid range. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * badComponent - {<OpenLayers.Geometry.Point>} original geometry of the │ │ │ │ + * invalid point │ │ │ │ + * goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the │ │ │ │ + * valid point │ │ │ │ + * Returns │ │ │ │ + * {String} the SVG coordinate pair of the clipped point (like │ │ │ │ + * getShortString), or an empty string if both passed componets are at │ │ │ │ + * the same point. │ │ │ │ */ │ │ │ │ - setTileSize: function() { │ │ │ │ - var tileWidth = this.extent.getWidth() / this.map.getResolution(); │ │ │ │ - var tileHeight = this.extent.getHeight() / this.map.getResolution(); │ │ │ │ - this.tileSize = new OpenLayers.Size(tileWidth, tileHeight); │ │ │ │ + clipLine: function(badComponent, goodComponent) { │ │ │ │ + if (goodComponent.equals(badComponent)) { │ │ │ │ + return ""; │ │ │ │ + } │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var maxX = this.MAX_PIXEL - this.translationParameters.x; │ │ │ │ + var maxY = this.MAX_PIXEL - this.translationParameters.y; │ │ │ │ + var x1 = (goodComponent.x - this.featureDx) / resolution + this.left; │ │ │ │ + var y1 = this.top - goodComponent.y / resolution; │ │ │ │ + var x2 = (badComponent.x - this.featureDx) / resolution + this.left; │ │ │ │ + var y2 = this.top - badComponent.y / resolution; │ │ │ │ + var k; │ │ │ │ + if (x2 < -maxX || x2 > maxX) { │ │ │ │ + k = (y2 - y1) / (x2 - x1); │ │ │ │ + x2 = x2 < 0 ? -maxX : maxX; │ │ │ │ + y2 = y1 + (x2 - x1) * k; │ │ │ │ + } │ │ │ │ + if (y2 < -maxY || y2 > maxY) { │ │ │ │ + k = (x2 - x1) / (y2 - y1); │ │ │ │ + y2 = y2 < 0 ? -maxY : maxY; │ │ │ │ + x2 = x1 + (y2 - y1) * k; │ │ │ │ + } │ │ │ │ + return x2 + "," + y2; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: 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. │ │ │ │ + * Method: getShortString │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * tile - {<OpenLayers.Tile>} │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} or false if point is outside the valid range │ │ │ │ */ │ │ │ │ - addTileMonitoringHooks: function(tile) { │ │ │ │ - tile.onLoadStart = function() { │ │ │ │ - this.events.triggerEvent("loadstart"); │ │ │ │ - }; │ │ │ │ - tile.events.register("loadstart", this, tile.onLoadStart); │ │ │ │ + getShortString: function(point) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var x = ((point.x - this.featureDx) / resolution + this.left); │ │ │ │ + var y = (this.top - point.y / resolution); │ │ │ │ │ │ │ │ - tile.onLoadEnd = function() { │ │ │ │ - this.events.triggerEvent("loadend"); │ │ │ │ - }; │ │ │ │ - tile.events.register("loadend", this, tile.onLoadEnd); │ │ │ │ - tile.events.register("unload", this, tile.onLoadEnd); │ │ │ │ + if (this.inValidRange(x, y)) { │ │ │ │ + return x + "," + y; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: removeTileMonitoringHooks │ │ │ │ - * This function takes a tile as input and removes the tile hooks │ │ │ │ - * that were added in <addTileMonitoringHooks>. │ │ │ │ + /** │ │ │ │ + * Method: getPosition │ │ │ │ + * Finds the position of an svg node. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * tile - {<OpenLayers.Tile>} │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} hash with x and y properties, representing the coordinates │ │ │ │ + * within the svg coordinate system │ │ │ │ */ │ │ │ │ - removeTileMonitoringHooks: function(tile) { │ │ │ │ - tile.unload(); │ │ │ │ - tile.events.un({ │ │ │ │ - "loadstart": tile.onLoadStart, │ │ │ │ - "loadend": tile.onLoadEnd, │ │ │ │ - "unload": tile.onLoadEnd, │ │ │ │ - scope: this │ │ │ │ + getPosition: function(node) { │ │ │ │ + return ({ │ │ │ │ + x: parseFloat(node.getAttributeNS(null, "cx")), │ │ │ │ + y: parseFloat(node.getAttributeNS(null, "cy")) │ │ │ │ }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setUrl │ │ │ │ + * Method: importSymbol │ │ │ │ + * add a new symbol definition from the rendererer's symbol hash │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * newUrl - {String} │ │ │ │ + * graphicName - {String} name of the symbol to import │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} - the imported symbol │ │ │ │ */ │ │ │ │ - setUrl: function(newUrl) { │ │ │ │ - this.url = newUrl; │ │ │ │ - this.tile.draw(); │ │ │ │ + importSymbol: function(graphicName) { │ │ │ │ + if (!this.defs) { │ │ │ │ + // create svg defs tag │ │ │ │ + this.defs = this.createDefs(); │ │ │ │ + } │ │ │ │ + var id = this.container.id + "-" + graphicName; │ │ │ │ + │ │ │ │ + // check if symbol already exists in the defs │ │ │ │ + var existing = document.getElementById(id); │ │ │ │ + if (existing != null) { │ │ │ │ + return existing; │ │ │ │ + } │ │ │ │ + │ │ │ │ + var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ + if (!symbol) { │ │ │ │ + throw new Error(graphicName + ' is not a valid symbol name'); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var symbolNode = this.nodeFactory(id, "symbol"); │ │ │ │ + var node = this.nodeFactory(null, "polygon"); │ │ │ │ + symbolNode.appendChild(node); │ │ │ │ + var symbolExtent = new OpenLayers.Bounds( │ │ │ │ + Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ + │ │ │ │ + var points = []; │ │ │ │ + var x, y; │ │ │ │ + for (var i = 0; i < symbol.length; i = i + 2) { │ │ │ │ + x = symbol[i]; │ │ │ │ + y = symbol[i + 1]; │ │ │ │ + symbolExtent.left = Math.min(symbolExtent.left, x); │ │ │ │ + symbolExtent.bottom = Math.min(symbolExtent.bottom, y); │ │ │ │ + symbolExtent.right = Math.max(symbolExtent.right, x); │ │ │ │ + symbolExtent.top = Math.max(symbolExtent.top, y); │ │ │ │ + points.push(x, ",", y); │ │ │ │ + } │ │ │ │ + │ │ │ │ + node.setAttributeNS(null, "points", points.join(" ")); │ │ │ │ + │ │ │ │ + var width = symbolExtent.getWidth(); │ │ │ │ + var height = symbolExtent.getHeight(); │ │ │ │ + // create a viewBox three times as large as the symbol itself, │ │ │ │ + // to allow for strokeWidth being displayed correctly at the corners. │ │ │ │ + var viewBox = [symbolExtent.left - width, │ │ │ │ + symbolExtent.bottom - height, width * 3, height * 3 │ │ │ │ + ]; │ │ │ │ + symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" ")); │ │ │ │ + this.symbolMetrics[id] = [ │ │ │ │ + Math.max(width, height), │ │ │ │ + symbolExtent.getCenterLonLat().lon, │ │ │ │ + symbolExtent.getCenterLonLat().lat │ │ │ │ + ]; │ │ │ │ + │ │ │ │ + this.defs.appendChild(symbolNode); │ │ │ │ + return symbolNode; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getURL │ │ │ │ - * The url we return is always the same (the image itself never changes) │ │ │ │ - * so we can ignore the bounds parameter (it will always be the same, │ │ │ │ - * anyways) │ │ │ │ + /** │ │ │ │ + * Method: getFeatureIdFromEvent │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * evt - {Object} An <OpenLayers.Event> object │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A feature id or undefined. │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - return this.url; │ │ │ │ + getFeatureIdFromEvent: function(evt) { │ │ │ │ + var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments); │ │ │ │ + if (!featureId) { │ │ │ │ + var target = evt.target; │ │ │ │ + featureId = target.parentNode && target != this.rendererRoot ? │ │ │ │ + target.parentNode._featureId : undefined; │ │ │ │ + } │ │ │ │ + return featureId; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Image" │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.SVG" │ │ │ │ }); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN │ │ │ │ + * {Object} │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.SVG.LABEL_ALIGN = { │ │ │ │ + "l": "start", │ │ │ │ + "r": "end", │ │ │ │ + "b": "bottom", │ │ │ │ + "t": "hanging" │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT │ │ │ │ + * {Object} │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.SVG.LABEL_VSHIFT = { │ │ │ │ + // according to │ │ │ │ + // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html │ │ │ │ + // a baseline-shift of -70% shifts the text exactly from the │ │ │ │ + // bottom to the top of the baseline, so -35% moves the text to │ │ │ │ + // the center of the baseline. │ │ │ │ + "t": "-70%", │ │ │ │ + "b": "0" │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR │ │ │ │ + * {Object} │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.SVG.LABEL_VFACTOR = { │ │ │ │ + "t": 0, │ │ │ │ + "b": -1 │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Function: OpenLayers.Renderer.SVG.preventDefault │ │ │ │ + * *Deprecated*. Use <OpenLayers.Event.preventDefault> method instead. │ │ │ │ + * Used to prevent default events (especially opening images in a new tab on │ │ │ │ + * ctrl-click) from being executed for externalGraphic symbols │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.SVG.preventDefault = function(e) { │ │ │ │ + OpenLayers.Event.preventDefault(e); │ │ │ │ +}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/WMTS.js │ │ │ │ + OpenLayers/Handler/Drag.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Layer/Grid.js │ │ │ │ + * @requires OpenLayers/Handler.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.WMTS │ │ │ │ - * Instances of the WMTS class allow viewing of tiles from a service that │ │ │ │ - * implements the OGC WMTS specification version 1.0.0. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Handler.Drag │ │ │ │ + * The drag handler is used to deal with sequences of browser events related │ │ │ │ + * to dragging. The handler is used by controls that want to know when │ │ │ │ + * a drag sequence begins, when a drag is happening, and when it has │ │ │ │ + * finished. │ │ │ │ + * │ │ │ │ + * Controls that use the drag handler typically construct it with callbacks │ │ │ │ + * for 'down', 'move', and 'done'. Callbacks for these keys are called │ │ │ │ + * when the drag begins, with each move, and when the drag is done. In │ │ │ │ + * addition, controls can have callbacks keyed to 'up' and 'out' if they │ │ │ │ + * care to differentiate between the types of events that correspond with │ │ │ │ + * the end of a drag sequence. If no drag actually occurs (no mouse move) │ │ │ │ + * the 'down' and 'up' callbacks will be called, but not the 'done' │ │ │ │ + * callback. │ │ │ │ + * │ │ │ │ + * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ + * - <OpenLayers.Handler> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} The layer will be considered a base layer. Default is true. │ │ │ │ - */ │ │ │ │ - isBaseLayer: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: version │ │ │ │ - * {String} WMTS version. Default is "1.0.0". │ │ │ │ - */ │ │ │ │ - version: "1.0.0", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: requestEncoding │ │ │ │ - * {String} Request encoding. Can be "REST" or "KVP". Default is "KVP". │ │ │ │ - */ │ │ │ │ - requestEncoding: "KVP", │ │ │ │ +OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: url │ │ │ │ - * {String|Array(String)} The base URL or request URL template for the WMTS │ │ │ │ - * service. Must be provided. Array is only supported for base URLs, not │ │ │ │ - * for request URL templates. URL templates are only supported for │ │ │ │ - * REST <requestEncoding>. │ │ │ │ + /** │ │ │ │ + * Property: started │ │ │ │ + * {Boolean} When a mousedown or touchstart event is received, we want to │ │ │ │ + * record it, but not set 'dragging' until the mouse moves after starting. │ │ │ │ */ │ │ │ │ - url: null, │ │ │ │ + started: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: layer │ │ │ │ - * {String} The layer identifier advertised by the WMTS service. Must be │ │ │ │ - * provided. │ │ │ │ + * Property: stopDown │ │ │ │ + * {Boolean} Stop propagation of mousedown events from getting to listeners │ │ │ │ + * on the same element. Default is true. │ │ │ │ */ │ │ │ │ - layer: null, │ │ │ │ + stopDown: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: matrixSet │ │ │ │ - * {String} One of the advertised matrix set identifiers. Must be provided. │ │ │ │ + * Property: dragging │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - matrixSet: null, │ │ │ │ + dragging: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: style │ │ │ │ - * {String} One of the advertised layer styles. Must be provided. │ │ │ │ + * Property: last │ │ │ │ + * {<OpenLayers.Pixel>} The last pixel location of the drag. │ │ │ │ */ │ │ │ │ - style: null, │ │ │ │ + last: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: format │ │ │ │ - * {String} The image MIME type. Default is "image/jpeg". │ │ │ │ - */ │ │ │ │ - format: "image/jpeg", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: tileOrigin │ │ │ │ - * {<OpenLayers.LonLat>} The top-left corner of the tile matrix in map │ │ │ │ - * units. If the tile origin for each matrix in a set is different, │ │ │ │ - * the <matrixIds> should include a topLeftCorner property. If │ │ │ │ - * not provided, the tile origin will default to the top left corner │ │ │ │ - * of the layer <maxExtent>. │ │ │ │ + * Property: start │ │ │ │ + * {<OpenLayers.Pixel>} The first pixel location of the drag. │ │ │ │ */ │ │ │ │ - tileOrigin: null, │ │ │ │ + start: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: tileFullExtent │ │ │ │ - * {<OpenLayers.Bounds>} The full extent of the tile set. If not supplied, │ │ │ │ - * the layer's <maxExtent> property will be used. │ │ │ │ + * Property: lastMoveEvt │ │ │ │ + * {Object} The last mousemove event that occurred. Used to │ │ │ │ + * position the map correctly when our "delay drag" │ │ │ │ + * timeout expired. │ │ │ │ */ │ │ │ │ - tileFullExtent: null, │ │ │ │ + lastMoveEvt: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: formatSuffix │ │ │ │ - * {String} For REST request encoding, an image format suffix must be │ │ │ │ - * included in the request. If not provided, the suffix will be derived │ │ │ │ - * from the <format> property. │ │ │ │ + * Property: oldOnselectstart │ │ │ │ + * {Function} │ │ │ │ */ │ │ │ │ - formatSuffix: null, │ │ │ │ + oldOnselectstart: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: matrixIds │ │ │ │ - * {Array} A list of tile matrix identifiers. If not provided, the matrix │ │ │ │ - * identifiers will be assumed to be integers corresponding to the │ │ │ │ - * map zoom level. If a list of strings is provided, each item should │ │ │ │ - * be the matrix identifier that corresponds to the map zoom level. │ │ │ │ - * Additionally, a list of objects can be provided. Each object should │ │ │ │ - * describe the matrix as presented in the WMTS capabilities. These │ │ │ │ - * objects should have the propertes shown below. │ │ │ │ - * │ │ │ │ - * Matrix properties: │ │ │ │ - * identifier - {String} The matrix identifier (required). │ │ │ │ - * scaleDenominator - {Number} The matrix scale denominator. │ │ │ │ - * topLeftCorner - {<OpenLayers.LonLat>} The top left corner of the │ │ │ │ - * matrix. Must be provided if different than the layer <tileOrigin>. │ │ │ │ - * tileWidth - {Number} The tile width for the matrix. Must be provided │ │ │ │ - * if different than the width given in the layer <tileSize>. │ │ │ │ - * tileHeight - {Number} The tile height for the matrix. Must be provided │ │ │ │ - * if different than the height given in the layer <tileSize>. │ │ │ │ + * Property: interval │ │ │ │ + * {Integer} In order to increase performance, an interval (in │ │ │ │ + * milliseconds) can be set to reduce the number of drag events │ │ │ │ + * called. If set, a new drag event will not be set until the │ │ │ │ + * interval has passed. │ │ │ │ + * Defaults to 0, meaning no interval. │ │ │ │ */ │ │ │ │ - matrixIds: null, │ │ │ │ + interval: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: dimensions │ │ │ │ - * {Array} For RESTful request encoding, extra dimensions may be specified. │ │ │ │ - * Items in this list should be property names in the <params> object. │ │ │ │ - * Values of extra dimensions will be determined from the corresponding │ │ │ │ - * values in the <params> object. │ │ │ │ + * Property: timeoutId │ │ │ │ + * {String} The id of the timeout used for the mousedown interval. │ │ │ │ + * This is "private", and should be left alone. │ │ │ │ */ │ │ │ │ - dimensions: null, │ │ │ │ + timeoutId: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: params │ │ │ │ - * {Object} Extra parameters to include in tile requests. For KVP │ │ │ │ - * <requestEncoding>, these properties will be encoded in the request │ │ │ │ - * query string. For REST <requestEncoding>, these properties will │ │ │ │ - * become part of the request path, with order determined by the │ │ │ │ - * <dimensions> list. │ │ │ │ + * APIProperty: documentDrag │ │ │ │ + * {Boolean} If set to true, the handler will also handle mouse moves when │ │ │ │ + * the cursor has moved out of the map viewport. Default is false. │ │ │ │ */ │ │ │ │ - params: null, │ │ │ │ + documentDrag: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: zoomOffset │ │ │ │ - * {Number} If your cache has more levels than you want to provide │ │ │ │ - * access to with this layer, supply a zoomOffset. This zoom offset │ │ │ │ - * is added to the current map zoom level to determine the level │ │ │ │ - * for a requested tile. For example, if you supply a zoomOffset │ │ │ │ - * of 3, when the map is at the zoom 0, tiles will be requested from │ │ │ │ - * level 3 of your cache. Default is 0 (assumes cache level and map │ │ │ │ - * zoom are equivalent). Additionally, if this layer is to be used │ │ │ │ - * as an overlay and the cache has fewer zoom levels than the base │ │ │ │ - * layer, you can supply a negative zoomOffset. For example, if a │ │ │ │ - * map zoom level of 1 corresponds to your cache level zero, you would │ │ │ │ - * supply a -1 zoomOffset (and set the maxResolution of the layer │ │ │ │ - * appropriately). The zoomOffset value has no effect if complete │ │ │ │ - * matrix definitions (including scaleDenominator) are supplied in │ │ │ │ - * the <matrixIds> property. Defaults to 0 (no zoom offset). │ │ │ │ + * Property: documentEvents │ │ │ │ + * {Boolean} Are we currently observing document events? │ │ │ │ */ │ │ │ │ - zoomOffset: 0, │ │ │ │ + documentEvents: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: serverResolutions │ │ │ │ - * {Array} A list of all resolutions available on the server. Only set this │ │ │ │ - * property if the map resolutions differ from the server. This │ │ │ │ - * property serves two purposes. (a) <serverResolutions> can include │ │ │ │ - * resolutions that the server supports and that you don't want to │ │ │ │ - * provide with this layer; you can also look at <zoomOffset>, which is │ │ │ │ - * an alternative to <serverResolutions> for that specific purpose. │ │ │ │ - * (b) The map can work with resolutions that aren't supported by │ │ │ │ - * the server, i.e. that aren't in <serverResolutions>. When the │ │ │ │ - * map is displayed in such a resolution data for the closest │ │ │ │ - * server-supported resolution is loaded and the layer div is │ │ │ │ - * stretched as necessary. │ │ │ │ + * Constructor: OpenLayers.Handler.Drag │ │ │ │ + * Returns OpenLayers.Handler.Drag │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * control - {<OpenLayers.Control>} The control that is making use of │ │ │ │ + * this handler. If a handler is being used without a control, the │ │ │ │ + * handlers setMap method must be overridden to deal properly with │ │ │ │ + * the map. │ │ │ │ + * callbacks - {Object} An object containing a single function to be │ │ │ │ + * called when the drag operation is finished. The callback should │ │ │ │ + * expect to recieve a single argument, the pixel location of the event. │ │ │ │ + * Callbacks for 'move' and 'done' are supported. You can also speficy │ │ │ │ + * callbacks for 'down', 'up', and 'out' to respond to those events. │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - serverResolutions: null, │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: formatSuffixMap │ │ │ │ - * {Object} a map between WMTS 'format' request parameter and tile image file suffix │ │ │ │ - */ │ │ │ │ - formatSuffixMap: { │ │ │ │ - "image/png": "png", │ │ │ │ - "image/png8": "png", │ │ │ │ - "image/png24": "png", │ │ │ │ - "image/png32": "png", │ │ │ │ - "png": "png", │ │ │ │ - "image/jpeg": "jpg", │ │ │ │ - "image/jpg": "jpg", │ │ │ │ - "jpeg": "jpg", │ │ │ │ - "jpg": "jpg" │ │ │ │ + if (this.documentDrag === true) { │ │ │ │ + var me = this; │ │ │ │ + this._docMove = function(evt) { │ │ │ │ + me.mousemove({ │ │ │ │ + xy: { │ │ │ │ + x: evt.clientX, │ │ │ │ + y: evt.clientY │ │ │ │ + }, │ │ │ │ + element: document │ │ │ │ + }); │ │ │ │ + }; │ │ │ │ + this._docUp = function(evt) { │ │ │ │ + me.mouseup({ │ │ │ │ + xy: { │ │ │ │ + x: evt.clientX, │ │ │ │ + y: evt.clientY │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + }; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: matrix │ │ │ │ - * {Object} Matrix definition for the current map resolution. Updated by │ │ │ │ - * the <updateMatrixProperties> method. │ │ │ │ - */ │ │ │ │ - matrix: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Layer.WMTS │ │ │ │ - * Create a new WMTS layer. │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * var wmts = new OpenLayers.Layer.WMTS({ │ │ │ │ - * name: "My WMTS Layer", │ │ │ │ - * url: "http://example.com/wmts", │ │ │ │ - * layer: "layer_id", │ │ │ │ - * style: "default", │ │ │ │ - * matrixSet: "matrix_id" │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * config - {Object} Configuration properties for the layer. │ │ │ │ - * │ │ │ │ - * Required configuration properties: │ │ │ │ - * url - {String} The base url for the service. See the <url> property. │ │ │ │ - * layer - {String} The layer identifier. See the <layer> property. │ │ │ │ - * style - {String} The layer style identifier. See the <style> property. │ │ │ │ - * matrixSet - {String} The tile matrix set identifier. See the <matrixSet> │ │ │ │ - * property. │ │ │ │ - * │ │ │ │ - * Any other documented layer properties can be provided in the config object. │ │ │ │ - */ │ │ │ │ - initialize: function(config) { │ │ │ │ - │ │ │ │ - // confirm required properties are supplied │ │ │ │ - var required = { │ │ │ │ - url: true, │ │ │ │ - layer: true, │ │ │ │ - style: true, │ │ │ │ - matrixSet: true │ │ │ │ - }; │ │ │ │ - for (var prop in required) { │ │ │ │ - if (!(prop in config)) { │ │ │ │ - throw new Error("Missing property '" + prop + "' in layer configuration."); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - config.params = OpenLayers.Util.upperCaseObject(config.params); │ │ │ │ - var args = [config.name, config.url, config.params, config]; │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, args); │ │ │ │ - │ │ │ │ - │ │ │ │ - // determine format suffix (for REST) │ │ │ │ - if (!this.formatSuffix) { │ │ │ │ - this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split("/").pop(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // expand matrixIds (may be array of string or array of object) │ │ │ │ - if (this.matrixIds) { │ │ │ │ - var len = this.matrixIds.length; │ │ │ │ - if (len && typeof this.matrixIds[0] === "string") { │ │ │ │ - var ids = this.matrixIds; │ │ │ │ - this.matrixIds = new Array(len); │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ - this.matrixIds[i] = { │ │ │ │ - identifier: ids[i] │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - */ │ │ │ │ - setMap: function() { │ │ │ │ - OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: updateMatrixProperties │ │ │ │ - * Called when map resolution changes to update matrix related properties. │ │ │ │ - */ │ │ │ │ - updateMatrixProperties: function() { │ │ │ │ - this.matrix = this.getMatrix(); │ │ │ │ - if (this.matrix) { │ │ │ │ - if (this.matrix.topLeftCorner) { │ │ │ │ - this.tileOrigin = this.matrix.topLeftCorner; │ │ │ │ - } │ │ │ │ - if (this.matrix.tileWidth && this.matrix.tileHeight) { │ │ │ │ - this.tileSize = new OpenLayers.Size( │ │ │ │ - this.matrix.tileWidth, this.matrix.tileHeight │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (!this.tileOrigin) { │ │ │ │ - this.tileOrigin = new OpenLayers.LonLat( │ │ │ │ - this.maxExtent.left, this.maxExtent.top │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (!this.tileFullExtent) { │ │ │ │ - this.tileFullExtent = this.maxExtent; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to │ │ │ │ - * do some init work in that case. │ │ │ │ - * dragging - {Boolean} │ │ │ │ - */ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - if (zoomChanged || !this.matrix) { │ │ │ │ - this.updateMatrixProperties(); │ │ │ │ - } │ │ │ │ - return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.WMTS>} An exact clone of this <OpenLayers.Layer.WMTS> │ │ │ │ - */ │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.WMTS(this.options); │ │ │ │ - } │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - return obj; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getIdentifier │ │ │ │ - * Get the current index in the matrixIds array. │ │ │ │ - */ │ │ │ │ - getIdentifier: function() { │ │ │ │ - return this.getServerZoom(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getMatrix │ │ │ │ - * Get the appropriate matrix definition for the current map resolution. │ │ │ │ - */ │ │ │ │ - getMatrix: function() { │ │ │ │ - var matrix; │ │ │ │ - if (!this.matrixIds || this.matrixIds.length === 0) { │ │ │ │ - matrix = { │ │ │ │ - identifier: this.getIdentifier() │ │ │ │ - }; │ │ │ │ - } else { │ │ │ │ - // get appropriate matrix given the map scale if possible │ │ │ │ - if ("scaleDenominator" in this.matrixIds[0]) { │ │ │ │ - // scale denominator calculation based on WMTS spec │ │ │ │ - var denom = │ │ │ │ - OpenLayers.METERS_PER_INCH * │ │ │ │ - OpenLayers.INCHES_PER_UNIT[this.units] * │ │ │ │ - this.getServerResolution() / 0.28E-3; │ │ │ │ - var diff = Number.POSITIVE_INFINITY; │ │ │ │ - var delta; │ │ │ │ - for (var i = 0, ii = this.matrixIds.length; i < ii; ++i) { │ │ │ │ - delta = Math.abs(1 - (this.matrixIds[i].scaleDenominator / denom)); │ │ │ │ - if (delta < diff) { │ │ │ │ - diff = delta; │ │ │ │ - matrix = this.matrixIds[i]; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - // fall back on zoom as index │ │ │ │ - matrix = this.matrixIds[this.getIdentifier()]; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return matrix; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getTileInfo │ │ │ │ - * Get tile information for a given location at the current map resolution. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * loc - {<OpenLayers.LonLat} A location in map coordinates. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object with "col", "row", "i", and "j" properties. The col │ │ │ │ - * and row values are zero based tile indexes from the top left. The │ │ │ │ - * i and j values are the number of pixels to the left and top │ │ │ │ - * (respectively) of the given location within the target tile. │ │ │ │ - */ │ │ │ │ - getTileInfo: function(loc) { │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - │ │ │ │ - var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w); │ │ │ │ - var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h); │ │ │ │ - │ │ │ │ - var col = Math.floor(fx); │ │ │ │ - var row = Math.floor(fy); │ │ │ │ - │ │ │ │ - return { │ │ │ │ - col: col, │ │ │ │ - row: row, │ │ │ │ - i: Math.floor((fx - col) * this.tileSize.w), │ │ │ │ - j: Math.floor((fy - row) * this.tileSize.h) │ │ │ │ - }; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getURL │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A URL for the tile corresponding to the given bounds. │ │ │ │ - */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var url = ""; │ │ │ │ - if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) { │ │ │ │ - │ │ │ │ - var center = bounds.getCenterLonLat(); │ │ │ │ - var info = this.getTileInfo(center); │ │ │ │ - var matrixId = this.matrix.identifier; │ │ │ │ - var dimensions = this.dimensions, │ │ │ │ - params; │ │ │ │ - │ │ │ │ - if (OpenLayers.Util.isArray(this.url)) { │ │ │ │ - url = this.selectUrl([ │ │ │ │ - this.version, this.style, this.matrixSet, │ │ │ │ - this.matrix.identifier, info.row, info.col │ │ │ │ - ].join(","), this.url); │ │ │ │ - } else { │ │ │ │ - url = this.url; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.requestEncoding.toUpperCase() === "REST") { │ │ │ │ - params = this.params; │ │ │ │ - if (url.indexOf("{") !== -1) { │ │ │ │ - var template = url.replace(/\{/g, "${"); │ │ │ │ - var context = { │ │ │ │ - // spec does not make clear if capital S or not │ │ │ │ - style: this.style, │ │ │ │ - Style: this.style, │ │ │ │ - TileMatrixSet: this.matrixSet, │ │ │ │ - TileMatrix: this.matrix.identifier, │ │ │ │ - TileRow: info.row, │ │ │ │ - TileCol: info.col │ │ │ │ - }; │ │ │ │ - if (dimensions) { │ │ │ │ - var dimension, i; │ │ │ │ - for (i = dimensions.length - 1; i >= 0; --i) { │ │ │ │ - dimension = dimensions[i]; │ │ │ │ - context[dimension] = params[dimension.toUpperCase()]; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - url = OpenLayers.String.format(template, context); │ │ │ │ - } else { │ │ │ │ - // include 'version', 'layer' and 'style' in tile resource url │ │ │ │ - var path = this.version + "/" + this.layer + "/" + this.style + "/"; │ │ │ │ - │ │ │ │ - // append optional dimension path elements │ │ │ │ - if (dimensions) { │ │ │ │ - for (var i = 0; i < dimensions.length; i++) { │ │ │ │ - if (params[dimensions[i]]) { │ │ │ │ - path = path + params[dimensions[i]] + "/"; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - // append other required path elements │ │ │ │ - path = path + this.matrixSet + "/" + this.matrix.identifier + │ │ │ │ - "/" + info.row + "/" + info.col + "." + this.formatSuffix; │ │ │ │ - │ │ │ │ - if (!url.match(/\/$/)) { │ │ │ │ - url = url + "/"; │ │ │ │ - } │ │ │ │ - url = url + path; │ │ │ │ - } │ │ │ │ - } else if (this.requestEncoding.toUpperCase() === "KVP") { │ │ │ │ - │ │ │ │ - // assemble all required parameters │ │ │ │ - params = { │ │ │ │ - SERVICE: "WMTS", │ │ │ │ - REQUEST: "GetTile", │ │ │ │ - VERSION: this.version, │ │ │ │ - LAYER: this.layer, │ │ │ │ - STYLE: this.style, │ │ │ │ - TILEMATRIXSET: this.matrixSet, │ │ │ │ - TILEMATRIX: this.matrix.identifier, │ │ │ │ - TILEROW: info.row, │ │ │ │ - TILECOL: info.col, │ │ │ │ - FORMAT: this.format │ │ │ │ - }; │ │ │ │ - url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]); │ │ │ │ - │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return url; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: mergeNewParams │ │ │ │ - * Extend the existing layer <params> with new properties. Tiles will be │ │ │ │ - * reloaded with updated params in the request. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * newParams - {Object} Properties to extend to existing <params>. │ │ │ │ - */ │ │ │ │ - mergeNewParams: function(newParams) { │ │ │ │ - if (this.requestEncoding.toUpperCase() === "KVP") { │ │ │ │ - return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply( │ │ │ │ - this, [OpenLayers.Util.upperCaseObject(newParams)] │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.WMTS" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/MapGuide.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ - * @requires OpenLayers/Layer/Grid.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.MapGuide │ │ │ │ - * Instances of OpenLayers.Layer.MapGuide are used to display │ │ │ │ - * data from a MapGuide OS instance. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} Treat this layer as a base layer. Default is true. │ │ │ │ - **/ │ │ │ │ - isBaseLayer: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: useHttpTile │ │ │ │ - * {Boolean} use a tile cache exposed directly via a webserver rather than the │ │ │ │ - * via mapguide server. This does require extra configuration on the Mapguide Server, │ │ │ │ - * and will only work when singleTile is false. The url for the layer must be set to the │ │ │ │ - * webserver path rather than the Mapguide mapagent. │ │ │ │ - * See http://trac.osgeo.org/mapguide/wiki/CodeSamples/Tiles/ServingTilesViaHttp │ │ │ │ - **/ │ │ │ │ - useHttpTile: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: singleTile │ │ │ │ - * {Boolean} use tile server or request single tile image. │ │ │ │ - **/ │ │ │ │ - singleTile: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: useOverlay │ │ │ │ - * {Boolean} flag to indicate if the layer should be retrieved using │ │ │ │ - * GETMAPIMAGE (default) or using GETDYNAMICOVERLAY requests. │ │ │ │ - **/ │ │ │ │ - useOverlay: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: useAsyncOverlay │ │ │ │ - * {Boolean} indicates if the MapGuide site supports the asynchronous │ │ │ │ - * GETDYNAMICOVERLAY requests which is available in MapGuide Enterprise 2010 │ │ │ │ - * and MapGuide Open Source v2.0.3 or higher. The newer versions of MG │ │ │ │ - * is called asynchronously, allows selections to be drawn separately from │ │ │ │ - * the map and offers styling options. │ │ │ │ - * │ │ │ │ - * With older versions of MapGuide, set useAsyncOverlay=false. Note that in │ │ │ │ - * this case a synchronous AJAX call is issued and the mapname and session │ │ │ │ - * parameters must be used to initialize the layer, not the mapdefinition │ │ │ │ - * parameter. Also note that this will issue a synchronous AJAX request │ │ │ │ - * before the image request can be issued so the users browser may lock │ │ │ │ - * up if the MG Web tier does not respond in a timely fashion. │ │ │ │ - **/ │ │ │ │ - useAsyncOverlay: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: TILE_PARAMS │ │ │ │ - * {Object} Hashtable of default parameter key/value pairs for tiled layer │ │ │ │ - */ │ │ │ │ - TILE_PARAMS: { │ │ │ │ - operation: 'GETTILEIMAGE', │ │ │ │ - version: '1.2.0' │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: SINGLE_TILE_PARAMS │ │ │ │ - * {Object} Hashtable of default parameter key/value pairs for untiled layer │ │ │ │ - */ │ │ │ │ - SINGLE_TILE_PARAMS: { │ │ │ │ - operation: 'GETMAPIMAGE', │ │ │ │ - format: 'PNG', │ │ │ │ - locale: 'en', │ │ │ │ - clip: '1', │ │ │ │ - version: '1.0.0' │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: OVERLAY_PARAMS │ │ │ │ - * {Object} Hashtable of default parameter key/value pairs for untiled layer │ │ │ │ - */ │ │ │ │ - OVERLAY_PARAMS: { │ │ │ │ - operation: 'GETDYNAMICMAPOVERLAYIMAGE', │ │ │ │ - format: 'PNG', │ │ │ │ - locale: 'en', │ │ │ │ - clip: '1', │ │ │ │ - version: '2.0.0' │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: FOLDER_PARAMS │ │ │ │ - * {Object} Hashtable of parameter key/value pairs which describe │ │ │ │ - * the folder structure for tiles as configured in the mapguide │ │ │ │ - * serverconfig.ini section [TileServiceProperties] │ │ │ │ - */ │ │ │ │ - FOLDER_PARAMS: { │ │ │ │ - tileColumnsPerFolder: 30, │ │ │ │ - tileRowsPerFolder: 30, │ │ │ │ - format: 'png', │ │ │ │ - querystring: null │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: defaultSize │ │ │ │ - * {<OpenLayers.Size>} Tile size as produced by MapGuide server │ │ │ │ - **/ │ │ │ │ - defaultSize: new OpenLayers.Size(300, 300), │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: tileOriginCorner │ │ │ │ - * {String} MapGuide tile server uses top-left as tile origin │ │ │ │ - **/ │ │ │ │ - tileOriginCorner: "tl", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Layer.MapGuide │ │ │ │ - * Create a new Mapguide layer, either tiled or untiled. │ │ │ │ - * │ │ │ │ - * For tiled layers, the 'groupName' and 'mapDefinition' values │ │ │ │ - * must be specified as parameters in the constructor. │ │ │ │ - * │ │ │ │ - * For untiled base layers, specify either combination of 'mapName' and │ │ │ │ - * 'session', or 'mapDefinition' and 'locale'. │ │ │ │ - * │ │ │ │ - * For older versions of MapGuide and overlay layers, set useAsyncOverlay │ │ │ │ - * to false and in this case mapName and session are required parameters │ │ │ │ - * for the constructor. │ │ │ │ - * │ │ │ │ - * NOTE: MapGuide OS uses a DPI value and degrees to meters conversion │ │ │ │ - * factor that are different than the defaults used in OpenLayers, │ │ │ │ - * so these must be adjusted accordingly in your application. │ │ │ │ - * See the MapGuide example for how to set these values for MGOS. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} Name of the layer displayed in the interface │ │ │ │ - * url - {String} Location of the MapGuide mapagent executable │ │ │ │ - * (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi) │ │ │ │ - * params - {Object} hashtable of additional parameters to use. Some │ │ │ │ - * parameters may require additional code on the server. The ones that │ │ │ │ - * you may want to use are: │ │ │ │ - * - mapDefinition - {String} The MapGuide resource definition │ │ │ │ - * (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition) │ │ │ │ - * - locale - Locale setting │ │ │ │ - * (for untiled overlays layers only) │ │ │ │ - * - mapName - {String} Name of the map as stored in the MapGuide session. │ │ │ │ - * (for untiled layers with a session parameter only) │ │ │ │ - * - session - { String} MapGuide session ID │ │ │ │ - * (for untiled overlays layers only) │ │ │ │ - * - basemaplayergroupname - {String} GroupName for tiled MapGuide layers only │ │ │ │ - * - format - Image format to be returned (for untiled overlay layers only) │ │ │ │ - * - showLayers - {String} A comma separated list of GUID's for the │ │ │ │ - * layers to display eg: 'cvc-xcv34,453-345-345sdf'. │ │ │ │ - * - hideLayers - {String} A comma separated list of GUID's for the │ │ │ │ - * layers to hide eg: 'cvc-xcv34,453-345-345sdf'. │ │ │ │ - * - showGroups - {String} A comma separated list of GUID's for the │ │ │ │ - * groups to display eg: 'cvc-xcv34,453-345-345sdf'. │ │ │ │ - * - hideGroups - {String} A comma separated list of GUID's for the │ │ │ │ - * groups to hide eg: 'cvc-xcv34,453-345-345sdf' │ │ │ │ - * - selectionXml - {String} A selection xml string Some server plumbing │ │ │ │ - * is required to read such a value. │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer; │ │ │ │ - * will vary depending if tiled or untiled maps are being requested │ │ │ │ - */ │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments); │ │ │ │ - │ │ │ │ - // unless explicitly set in options, if the layer is transparent, │ │ │ │ - // it will be an overlay │ │ │ │ - if (options == null || options.isBaseLayer == null) { │ │ │ │ - this.isBaseLayer = ((this.transparent != "true") && │ │ │ │ - (this.transparent != true)); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (options && options.useOverlay != null) { │ │ │ │ - this.useOverlay = options.useOverlay; │ │ │ │ - } │ │ │ │ - │ │ │ │ - //initialize for untiled layers │ │ │ │ - if (this.singleTile) { │ │ │ │ - if (this.useOverlay) { │ │ │ │ - OpenLayers.Util.applyDefaults( │ │ │ │ - this.params, │ │ │ │ - this.OVERLAY_PARAMS │ │ │ │ - ); │ │ │ │ - if (!this.useAsyncOverlay) { │ │ │ │ - this.params.version = "1.0.0"; │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - OpenLayers.Util.applyDefaults( │ │ │ │ - this.params, │ │ │ │ - this.SINGLE_TILE_PARAMS │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - //initialize for tiled layers │ │ │ │ - if (this.useHttpTile) { │ │ │ │ - OpenLayers.Util.applyDefaults( │ │ │ │ - this.params, │ │ │ │ - this.FOLDER_PARAMS │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - OpenLayers.Util.applyDefaults( │ │ │ │ - this.params, │ │ │ │ - this.TILE_PARAMS │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - this.setTileSize(this.defaultSize); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.MapGuide>} An exact clone of this layer │ │ │ │ - */ │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.MapGuide(this.name, │ │ │ │ - this.url, │ │ │ │ - this.params, │ │ │ │ - this.getOptions()); │ │ │ │ - } │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - │ │ │ │ - return obj; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getURL │ │ │ │ - * Return a query string for this layer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox │ │ │ │ - * for the request │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A string with the layer's url and parameters and also │ │ │ │ - * the passed-in bounds and appropriate tile size specified │ │ │ │ - * as parameters. │ │ │ │ - */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - var url; │ │ │ │ - var center = bounds.getCenterLonLat(); │ │ │ │ - var mapSize = this.map.getSize(); │ │ │ │ - │ │ │ │ - if (this.singleTile) { │ │ │ │ - //set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY with │ │ │ │ - //dynamic map parameters │ │ │ │ - var params = { │ │ │ │ - setdisplaydpi: OpenLayers.DOTS_PER_INCH, │ │ │ │ - setdisplayheight: mapSize.h * this.ratio, │ │ │ │ - setdisplaywidth: mapSize.w * this.ratio, │ │ │ │ - setviewcenterx: center.lon, │ │ │ │ - setviewcentery: center.lat, │ │ │ │ - setviewscale: this.map.getScale() │ │ │ │ - }; │ │ │ │ - │ │ │ │ - if (this.useOverlay && !this.useAsyncOverlay) { │ │ │ │ - //first we need to call GETVISIBLEMAPEXTENT to set the extent │ │ │ │ - var getVisParams = {}; │ │ │ │ - getVisParams = OpenLayers.Util.extend(getVisParams, params); │ │ │ │ - getVisParams.operation = "GETVISIBLEMAPEXTENT"; │ │ │ │ - getVisParams.version = "1.0.0"; │ │ │ │ - getVisParams.session = this.params.session; │ │ │ │ - getVisParams.mapName = this.params.mapName; │ │ │ │ - getVisParams.format = 'text/xml'; │ │ │ │ - url = this.getFullRequestString(getVisParams); │ │ │ │ - │ │ │ │ - OpenLayers.Request.GET({ │ │ │ │ - url: url, │ │ │ │ - async: false │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - //construct the full URL │ │ │ │ - url = this.getFullRequestString(params); │ │ │ │ - } else { │ │ │ │ - │ │ │ │ - //tiled version │ │ │ │ - var currentRes = this.map.getResolution(); │ │ │ │ - var colidx = Math.floor((bounds.left - this.maxExtent.left) / currentRes); │ │ │ │ - colidx = Math.round(colidx / this.tileSize.w); │ │ │ │ - var rowidx = Math.floor((this.maxExtent.top - bounds.top) / currentRes); │ │ │ │ - rowidx = Math.round(rowidx / this.tileSize.h); │ │ │ │ - │ │ │ │ - if (this.useHttpTile) { │ │ │ │ - url = this.getImageFilePath({ │ │ │ │ - tilecol: colidx, │ │ │ │ - tilerow: rowidx, │ │ │ │ - scaleindex: this.resolutions.length - this.map.zoom - 1 │ │ │ │ - }); │ │ │ │ - │ │ │ │ - } else { │ │ │ │ - url = this.getFullRequestString({ │ │ │ │ - tilecol: colidx, │ │ │ │ - tilerow: rowidx, │ │ │ │ - scaleindex: this.resolutions.length - this.map.zoom - 1 │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return url; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getFullRequestString │ │ │ │ - * getFullRequestString on MapGuide layers is special, because we │ │ │ │ - * do a regular expression replace on ',' in parameters to '+'. │ │ │ │ - * This is why it is subclassed here. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * altUrl - {String} Alternative base URL to use. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A string with the layer's url appropriately encoded for MapGuide │ │ │ │ - */ │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ - // use layer's url unless altUrl passed in │ │ │ │ - var url = (altUrl == null) ? this.url : altUrl; │ │ │ │ - │ │ │ │ - // if url is not a string, it should be an array of strings, │ │ │ │ - // in which case we will randomly select one of them in order │ │ │ │ - // to evenly distribute requests to different urls. │ │ │ │ - if (typeof url == "object") { │ │ │ │ - url = url[Math.floor(Math.random() * url.length)]; │ │ │ │ - } │ │ │ │ - // requestString always starts with url │ │ │ │ - var requestString = url; │ │ │ │ - │ │ │ │ - // create a new params hashtable with all the layer params and the │ │ │ │ - // new params together. then convert to string │ │ │ │ - var allParams = OpenLayers.Util.extend({}, this.params); │ │ │ │ - allParams = OpenLayers.Util.extend(allParams, newParams); │ │ │ │ - // ignore parameters that are already in the url search string │ │ │ │ - var urlParams = OpenLayers.Util.upperCaseObject( │ │ │ │ - OpenLayers.Util.getParameters(url)); │ │ │ │ - for (var key in allParams) { │ │ │ │ - if (key.toUpperCase() in urlParams) { │ │ │ │ - delete allParams[key]; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ - │ │ │ │ - /* MapGuide needs '+' seperating things like bounds/height/width. │ │ │ │ - Since typically this is URL encoded, we use a slight hack: we │ │ │ │ - depend on the list-like functionality of getParameterString to │ │ │ │ - leave ',' only in the case of list items (since otherwise it is │ │ │ │ - encoded) then do a regular expression replace on the , characters │ │ │ │ - to '+' */ │ │ │ │ - paramsString = paramsString.replace(/,/g, "+"); │ │ │ │ - │ │ │ │ - if (paramsString != "") { │ │ │ │ - var lastServerChar = url.charAt(url.length - 1); │ │ │ │ - if ((lastServerChar == "&") || (lastServerChar == "?")) { │ │ │ │ - requestString += paramsString; │ │ │ │ - } else { │ │ │ │ - if (url.indexOf('?') == -1) { │ │ │ │ - //serverPath has no ? -- add one │ │ │ │ - requestString += '?' + paramsString; │ │ │ │ - } else { │ │ │ │ - //serverPath contains ?, so must already have paramsString at the end │ │ │ │ - requestString += '&' + paramsString; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return requestString; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getImageFilePath │ │ │ │ - * special handler to request mapguide tiles from an http exposed tilecache │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * altUrl - {String} Alternative base URL to use. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A string with the url for the tile image │ │ │ │ - */ │ │ │ │ - getImageFilePath: function(newParams, altUrl) { │ │ │ │ - // use layer's url unless altUrl passed in │ │ │ │ - var url = (altUrl == null) ? this.url : altUrl; │ │ │ │ - │ │ │ │ - // if url is not a string, it should be an array of strings, │ │ │ │ - // in which case we will randomly select one of them in order │ │ │ │ - // to evenly distribute requests to different urls. │ │ │ │ - if (typeof url == "object") { │ │ │ │ - url = url[Math.floor(Math.random() * url.length)]; │ │ │ │ - } │ │ │ │ - // requestString always starts with url │ │ │ │ - var requestString = url; │ │ │ │ - │ │ │ │ - var tileRowGroup = ""; │ │ │ │ - var tileColGroup = ""; │ │ │ │ - │ │ │ │ - if (newParams.tilerow < 0) { │ │ │ │ - tileRowGroup = '-'; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (newParams.tilerow == 0) { │ │ │ │ - tileRowGroup += '0'; │ │ │ │ - } else { │ │ │ │ - tileRowGroup += Math.floor(Math.abs(newParams.tilerow / this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (newParams.tilecol < 0) { │ │ │ │ - tileColGroup = '-'; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (newParams.tilecol == 0) { │ │ │ │ - tileColGroup += '0'; │ │ │ │ - } else { │ │ │ │ - tileColGroup += Math.floor(Math.abs(newParams.tilecol / this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var tilePath = '/S' + Math.floor(newParams.scaleindex) + │ │ │ │ - '/' + this.params.basemaplayergroupname + │ │ │ │ - '/R' + tileRowGroup + │ │ │ │ - '/C' + tileColGroup + │ │ │ │ - '/' + (newParams.tilerow % this.params.tileRowsPerFolder) + │ │ │ │ - '_' + (newParams.tilecol % this.params.tileColumnsPerFolder) + │ │ │ │ - '.' + this.params.format; │ │ │ │ - │ │ │ │ - if (this.params.querystring) { │ │ │ │ - tilePath += "?" + this.params.querystring; │ │ │ │ - } │ │ │ │ - │ │ │ │ - requestString += tilePath; │ │ │ │ - return requestString; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.MapGuide" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/Vector/RootContainer.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/Vector.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.Vector.RootContainer │ │ │ │ - * A special layer type to combine multiple vector layers inside a single │ │ │ │ - * renderer root container. This class is not supposed to be instantiated │ │ │ │ - * from user space, it is a helper class for controls that require event │ │ │ │ - * processing for multiple vector layers. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Vector> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: displayInLayerSwitcher │ │ │ │ - * Set to false for this layer type │ │ │ │ - */ │ │ │ │ - displayInLayerSwitcher: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: layers │ │ │ │ - * Layers that are attached to this container. Required config option. │ │ │ │ - */ │ │ │ │ - layers: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Layer.Vector.RootContainer │ │ │ │ - * Create a new root container for multiple vector layer. This constructor │ │ │ │ - * is not supposed to be used from user space, it is only to be used by │ │ │ │ - * controls that need feature selection across multiple vector layers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} A name for the layer │ │ │ │ - * options - {Object} Optional object with non-default properties to set on │ │ │ │ - * the layer. │ │ │ │ - * │ │ │ │ - * Required options properties: │ │ │ │ - * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this │ │ │ │ - * container │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root │ │ │ │ - * container │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: display │ │ │ │ - */ │ │ │ │ - display: function() {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getFeatureFromEvent │ │ │ │ - * walk through the layers to find the feature returned by the event │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} event object with a feature property │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} │ │ │ │ - */ │ │ │ │ - getFeatureFromEvent: function(evt) { │ │ │ │ - var layers = this.layers; │ │ │ │ - var feature; │ │ │ │ - for (var i = 0; i < layers.length; i++) { │ │ │ │ - feature = layers[i].getFeatureFromEvent(evt); │ │ │ │ - if (feature) { │ │ │ │ - return feature; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); │ │ │ │ - this.collectRoots(); │ │ │ │ - map.events.register("changelayer", this, this.handleChangeLayer); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: removeMap │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - removeMap: function(map) { │ │ │ │ - map.events.unregister("changelayer", this, this.handleChangeLayer); │ │ │ │ - this.resetRoots(); │ │ │ │ - OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: collectRoots │ │ │ │ - * Collects the root nodes of all layers this control is configured with │ │ │ │ - * and moveswien the nodes to this control's layer │ │ │ │ - */ │ │ │ │ - collectRoots: function() { │ │ │ │ - var layer; │ │ │ │ - // walk through all map layers, because we want to keep the order │ │ │ │ - for (var i = 0; i < this.map.layers.length; ++i) { │ │ │ │ - layer = this.map.layers[i]; │ │ │ │ - if (OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ - layer.renderer.moveRoot(this.renderer); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: resetRoots │ │ │ │ - * Resets the root nodes back into the layers they belong to. │ │ │ │ - */ │ │ │ │ - resetRoots: function() { │ │ │ │ - var layer; │ │ │ │ - for (var i = 0; i < this.layers.length; ++i) { │ │ │ │ - layer = this.layers[i]; │ │ │ │ - if (this.renderer && layer.renderer.getRenderLayerId() == this.id) { │ │ │ │ - this.renderer.moveRoot(layer.renderer); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: handleChangeLayer │ │ │ │ - * Event handler for the map's changelayer event. We need to rebuild │ │ │ │ - * this container's layer dom if order of one of its layers changes. │ │ │ │ - * This handler is added with the setMap method, and removed with the │ │ │ │ - * removeMap method. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} │ │ │ │ - */ │ │ │ │ - handleChangeLayer: function(evt) { │ │ │ │ - var layer = evt.layer; │ │ │ │ - if (evt.property == "order" && │ │ │ │ - OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ - this.resetRoots(); │ │ │ │ - this.collectRoots(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/Google/v3.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/Google.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Layer.Google.v3 │ │ │ │ - * │ │ │ │ - * Mixin providing functionality specific to the Google Maps API v3. │ │ │ │ - * │ │ │ │ - * To use this layer, you must include the GMaps v3 API in your html. │ │ │ │ - * │ │ │ │ - * Note that this layer configures the google.maps.map object with the │ │ │ │ - * "disableDefaultUI" option set to true. Using UI controls that the Google │ │ │ │ - * Maps API provides is not supported by the OpenLayers API. │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Google.v3 = { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: DEFAULTS │ │ │ │ - * {Object} It is not recommended to change the properties set here. Note │ │ │ │ - * that Google.v3 layers only work when sphericalMercator is set to true. │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * { │ │ │ │ - * sphericalMercator: true, │ │ │ │ - * projection: "EPSG:900913" │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ - DEFAULTS: { │ │ │ │ - sphericalMercator: true, │ │ │ │ - projection: "EPSG:900913" │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: animationEnabled │ │ │ │ - * {Boolean} If set to true, the transition between zoom levels will be │ │ │ │ - * animated (if supported by the GMaps API for the device used). Set to │ │ │ │ - * false to match the zooming experience of other layer types. Default │ │ │ │ - * is true. Note that the GMaps API does not give us control over zoom │ │ │ │ - * animation, so if set to false, when zooming, this will make the │ │ │ │ - * layer temporarily invisible, wait until GMaps reports the map being │ │ │ │ - * idle, and make it visible again. The result will be a blank layer │ │ │ │ - * for a few moments while zooming. │ │ │ │ - */ │ │ │ │ - animationEnabled: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: loadMapObject │ │ │ │ - * Load the GMap and register appropriate event listeners. │ │ │ │ - */ │ │ │ │ - loadMapObject: function() { │ │ │ │ - if (!this.type) { │ │ │ │ - this.type = google.maps.MapTypeId.ROADMAP; │ │ │ │ - } │ │ │ │ - var mapObject; │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - // there are already Google layers added to this map │ │ │ │ - mapObject = cache.mapObject; │ │ │ │ - // increment the layer count │ │ │ │ - ++cache.count; │ │ │ │ - } else { │ │ │ │ - // this is the first Google layer for this map │ │ │ │ - // create GMap │ │ │ │ - var center = this.map.getCenter(); │ │ │ │ - var container = document.createElement('div'); │ │ │ │ - container.className = "olForeignContainer"; │ │ │ │ - container.style.width = '100%'; │ │ │ │ - container.style.height = '100%'; │ │ │ │ - mapObject = new google.maps.Map(container, { │ │ │ │ - center: center ? │ │ │ │ - new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0), │ │ │ │ - zoom: this.map.getZoom() || 0, │ │ │ │ - mapTypeId: this.type, │ │ │ │ - disableDefaultUI: true, │ │ │ │ - keyboardShortcuts: false, │ │ │ │ - draggable: false, │ │ │ │ - disableDoubleClickZoom: true, │ │ │ │ - scrollwheel: false, │ │ │ │ - streetViewControl: false │ │ │ │ - }); │ │ │ │ - var googleControl = document.createElement('div'); │ │ │ │ - googleControl.style.width = '100%'; │ │ │ │ - googleControl.style.height = '100%'; │ │ │ │ - mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl); │ │ │ │ - │ │ │ │ - // cache elements for use by any other google layers added to │ │ │ │ - // this same map │ │ │ │ - cache = { │ │ │ │ - googleControl: googleControl, │ │ │ │ - mapObject: mapObject, │ │ │ │ - count: 1 │ │ │ │ - }; │ │ │ │ - OpenLayers.Layer.Google.cache[this.map.id] = cache; │ │ │ │ - } │ │ │ │ - this.mapObject = mapObject; │ │ │ │ - this.setGMapVisibility(this.visibility); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: onMapResize │ │ │ │ - */ │ │ │ │ - onMapResize: function() { │ │ │ │ - if (this.visibility) { │ │ │ │ - google.maps.event.trigger(this.mapObject, "resize"); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setGMapVisibility │ │ │ │ - * Display the GMap container and associated elements. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * visible - {Boolean} Display the GMap elements. │ │ │ │ - */ │ │ │ │ - setGMapVisibility: function(visible) { │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - var map = this.map; │ │ │ │ - if (cache) { │ │ │ │ - var type = this.type; │ │ │ │ - var layers = map.layers; │ │ │ │ - var layer; │ │ │ │ - for (var i = layers.length - 1; i >= 0; --i) { │ │ │ │ - layer = layers[i]; │ │ │ │ - if (layer instanceof OpenLayers.Layer.Google && │ │ │ │ - layer.visibility === true && layer.inRange === true) { │ │ │ │ - type = layer.type; │ │ │ │ - visible = true; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var container = this.mapObject.getDiv(); │ │ │ │ - if (visible === true) { │ │ │ │ - if (container.parentNode !== map.div) { │ │ │ │ - if (!cache.rendered) { │ │ │ │ - var me = this; │ │ │ │ - google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() { │ │ │ │ - cache.rendered = true; │ │ │ │ - me.setGMapVisibility(me.getVisibility()); │ │ │ │ - me.moveTo(me.map.getCenter()); │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - map.div.appendChild(container); │ │ │ │ - cache.googleControl.appendChild(map.viewPortDiv); │ │ │ │ - google.maps.event.trigger(this.mapObject, 'resize'); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.mapObject.setMapTypeId(type); │ │ │ │ - } else if (cache.googleControl.hasChildNodes()) { │ │ │ │ - map.div.appendChild(map.viewPortDiv); │ │ │ │ - map.div.removeChild(container); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getMapContainer │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} the GMap container's div │ │ │ │ - */ │ │ │ │ - getMapContainer: function() { │ │ │ │ - return this.mapObject.getDiv(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - // │ │ │ │ - // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds │ │ │ │ - // │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectBoundsFromOLBounds │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * olBounds - {<OpenLayers.Bounds>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} A MapObject Bounds, translated from olBounds │ │ │ │ - * Returns null if null value is passed in │ │ │ │ - */ │ │ │ │ - getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ - var moBounds = null; │ │ │ │ - if (olBounds != null) { │ │ │ │ - var sw = this.sphericalMercator ? │ │ │ │ - this.inverseMercator(olBounds.bottom, olBounds.left) : │ │ │ │ - new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ - var ne = this.sphericalMercator ? │ │ │ │ - this.inverseMercator(olBounds.top, olBounds.right) : │ │ │ │ - new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ - moBounds = new google.maps.LatLngBounds( │ │ │ │ - new google.maps.LatLng(sw.lat, sw.lon), │ │ │ │ - new google.maps.LatLng(ne.lat, ne.lon) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - return moBounds; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - /************************************ │ │ │ │ - * * │ │ │ │ - * MapObject Interface Controls * │ │ │ │ - * * │ │ │ │ - ************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // LonLat - Pixel Translation │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectLonLatFromMapObjectPixel │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moPixel - {Object} MapObject Pixel format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} MapObject LonLat translated from MapObject Pixel │ │ │ │ - */ │ │ │ │ - getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ - var size = this.map.getSize(); │ │ │ │ - var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ - var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ - var res = this.map.getResolution(); │ │ │ │ - │ │ │ │ - var delta_x = moPixel.x - (size.w / 2); │ │ │ │ - var delta_y = moPixel.y - (size.h / 2); │ │ │ │ - │ │ │ │ - var lonlat = new OpenLayers.LonLat( │ │ │ │ - lon + delta_x * res, │ │ │ │ - lat - delta_y * res │ │ │ │ - ); │ │ │ │ - │ │ │ │ - if (this.wrapDateLine) { │ │ │ │ - lonlat = lonlat.wrapDateLine(this.maxExtent); │ │ │ │ - } │ │ │ │ - return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectPixelFromMapObjectLonLat │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moLonLat - {Object} MapObject LonLat format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} MapObject Pixel transtlated from MapObject LonLat │ │ │ │ - */ │ │ │ │ - getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - var res = this.map.getResolution(); │ │ │ │ - var extent = this.map.getExtent(); │ │ │ │ - return this.getMapObjectPixelFromXY((1 / res * (lon - extent.left)), │ │ │ │ - (1 / res * (extent.top - lat))); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setMapObjectCenter │ │ │ │ - * Set the mapObject to the specified center and zoom │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * center - {Object} MapObject LonLat format │ │ │ │ - * zoom - {int} MapObject zoom format │ │ │ │ - */ │ │ │ │ - setMapObjectCenter: function(center, zoom) { │ │ │ │ - if (this.animationEnabled === false && zoom != this.mapObject.zoom) { │ │ │ │ - var mapContainer = this.getMapContainer(); │ │ │ │ - google.maps.event.addListenerOnce( │ │ │ │ - this.mapObject, │ │ │ │ - "idle", │ │ │ │ - function() { │ │ │ │ - mapContainer.style.visibility = ""; │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - mapContainer.style.visibility = "hidden"; │ │ │ │ - } │ │ │ │ - this.mapObject.setOptions({ │ │ │ │ - center: center, │ │ │ │ - zoom: zoom │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - // Bounds │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectZoomFromMapObjectBounds │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moBounds - {Object} MapObject Bounds format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} MapObject Zoom for specified MapObject Bounds │ │ │ │ - */ │ │ │ │ - getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ - return this.mapObject.getBoundsZoomLevel(moBounds); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /************************************ │ │ │ │ - * * │ │ │ │ - * MapObject Primitives * │ │ │ │ - * * │ │ │ │ - ************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // LonLat │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectLonLatFromLonLat │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * lon - {Float} │ │ │ │ - * lat - {Float} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} MapObject LonLat built from lon and lat params │ │ │ │ - */ │ │ │ │ - getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ - var gLatLng; │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - var lonlat = this.inverseMercator(lon, lat); │ │ │ │ - gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon); │ │ │ │ - } else { │ │ │ │ - gLatLng = new google.maps.LatLng(lat, lon); │ │ │ │ - } │ │ │ │ - return gLatLng; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - // Pixel │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectPixelFromXY │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * x - {Integer} │ │ │ │ - * y - {Integer} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} MapObject Pixel from x and y parameters │ │ │ │ - */ │ │ │ │ - getMapObjectPixelFromXY: function(x, y) { │ │ │ │ - return new google.maps.Point(x, y); │ │ │ │ - } │ │ │ │ - │ │ │ │ -}; │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Events/buttonclick.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Events.buttonclick │ │ │ │ - * Extension event type for handling buttons on top of a dom element. This │ │ │ │ - * event type fires "buttonclick" on its <target> when a button was │ │ │ │ - * clicked. Buttons are detected by the "olButton" class. │ │ │ │ - * │ │ │ │ - * This event type makes sure that button clicks do not interfere with other │ │ │ │ - * events that are registered on the same <element>. │ │ │ │ - * │ │ │ │ - * Event types provided by this extension: │ │ │ │ - * - *buttonclick* Triggered when a button is clicked. Listeners receive an │ │ │ │ - * object with a *buttonElement* property referencing the dom element of │ │ │ │ - * the clicked button, and an *buttonXY* property with the click position │ │ │ │ - * relative to the button. │ │ │ │ - */ │ │ │ │ -OpenLayers.Events.buttonclick = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: target │ │ │ │ - * {<OpenLayers.Events>} The events instance that the buttonclick event will │ │ │ │ - * be triggered on. │ │ │ │ - */ │ │ │ │ - target: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: events │ │ │ │ - * {Array} Events to observe and conditionally stop from propagating when │ │ │ │ - * an element with the olButton class (or its olAlphaImg child) is │ │ │ │ - * clicked. │ │ │ │ - */ │ │ │ │ - events: [ │ │ │ │ - 'mousedown', 'mouseup', 'click', 'dblclick', │ │ │ │ - 'touchstart', 'touchmove', 'touchend', 'keydown' │ │ │ │ - ], │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: startRegEx │ │ │ │ - * {RegExp} Regular expression to test Event.type for events that start │ │ │ │ - * a buttonclick sequence. │ │ │ │ - */ │ │ │ │ - startRegEx: /^mousedown|touchstart$/, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: cancelRegEx │ │ │ │ - * {RegExp} Regular expression to test Event.type for events that cancel │ │ │ │ - * a buttonclick sequence. │ │ │ │ - */ │ │ │ │ - cancelRegEx: /^touchmove$/, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: completeRegEx │ │ │ │ - * {RegExp} Regular expression to test Event.type for events that complete │ │ │ │ - * a buttonclick sequence. │ │ │ │ - */ │ │ │ │ - completeRegEx: /^mouseup|touchend$/, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: startEvt │ │ │ │ - * {Event} The event that started the click sequence │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Events.buttonclick │ │ │ │ - * Construct a buttonclick event type. Applications are not supposed to │ │ │ │ - * create instances of this class - they are created on demand by │ │ │ │ - * <OpenLayers.Events> instances. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * target - {<OpenLayers.Events>} The events instance that the buttonclick │ │ │ │ - * event will be triggered on. │ │ │ │ - */ │ │ │ │ - initialize: function(target) { │ │ │ │ - this.target = target; │ │ │ │ - for (var i = this.events.length - 1; i >= 0; --i) { │ │ │ │ - this.target.register(this.events[i], this, this.buttonClick, { │ │ │ │ - extension: true │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - for (var i = this.events.length - 1; i >= 0; --i) { │ │ │ │ - this.target.unregister(this.events[i], this, this.buttonClick); │ │ │ │ - } │ │ │ │ - delete this.target; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getPressedButton │ │ │ │ - * Get the pressed button, if any. Returns undefined if no button │ │ │ │ - * was pressed. │ │ │ │ - * │ │ │ │ - * Arguments: │ │ │ │ - * element - {DOMElement} The event target. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} The button element, or undefined. │ │ │ │ - */ │ │ │ │ - getPressedButton: function(element) { │ │ │ │ - var depth = 3, // limit the search depth │ │ │ │ - button; │ │ │ │ - do { │ │ │ │ - if (OpenLayers.Element.hasClass(element, "olButton")) { │ │ │ │ - // hit! │ │ │ │ - button = element; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - element = element.parentNode; │ │ │ │ - } while (--depth > 0 && element); │ │ │ │ - return button; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: ignore │ │ │ │ - * Check for event target elements that should be ignored by OpenLayers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * element - {DOMElement} The event target. │ │ │ │ - */ │ │ │ │ - ignore: function(element) { │ │ │ │ - var depth = 3, │ │ │ │ - ignore = false; │ │ │ │ - do { │ │ │ │ - if (element.nodeName.toLowerCase() === 'a') { │ │ │ │ - ignore = true; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - element = element.parentNode; │ │ │ │ - } while (--depth > 0 && element); │ │ │ │ - return ignore; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: buttonClick │ │ │ │ - * Check if a button was clicked, and fire the buttonclick event │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - */ │ │ │ │ - buttonClick: function(evt) { │ │ │ │ - var propagate = true, │ │ │ │ - element = OpenLayers.Event.element(evt); │ │ │ │ - if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) { │ │ │ │ - // was a button pressed? │ │ │ │ - var button = this.getPressedButton(element); │ │ │ │ - if (button) { │ │ │ │ - if (evt.type === "keydown") { │ │ │ │ - switch (evt.keyCode) { │ │ │ │ - case OpenLayers.Event.KEY_RETURN: │ │ │ │ - case OpenLayers.Event.KEY_SPACE: │ │ │ │ - this.target.triggerEvent("buttonclick", { │ │ │ │ - buttonElement: button │ │ │ │ - }); │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - propagate = false; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } else if (this.startEvt) { │ │ │ │ - if (this.completeRegEx.test(evt.type)) { │ │ │ │ - var pos = OpenLayers.Util.pagePosition(button); │ │ │ │ - var viewportElement = OpenLayers.Util.getViewportElement(); │ │ │ │ - var scrollTop = window.pageYOffset || viewportElement.scrollTop; │ │ │ │ - var scrollLeft = window.pageXOffset || viewportElement.scrollLeft; │ │ │ │ - pos[0] = pos[0] - scrollLeft; │ │ │ │ - pos[1] = pos[1] - scrollTop; │ │ │ │ - │ │ │ │ - this.target.triggerEvent("buttonclick", { │ │ │ │ - buttonElement: button, │ │ │ │ - buttonXY: { │ │ │ │ - x: this.startEvt.clientX - pos[0], │ │ │ │ - y: this.startEvt.clientY - pos[1] │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - if (this.cancelRegEx.test(evt.type)) { │ │ │ │ - delete this.startEvt; │ │ │ │ - } │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - propagate = false; │ │ │ │ - } │ │ │ │ - if (this.startRegEx.test(evt.type)) { │ │ │ │ - this.startEvt = evt; │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - propagate = false; │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - propagate = !this.ignore(OpenLayers.Event.element(evt)); │ │ │ │ - delete this.startEvt; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return propagate; │ │ │ │ - } │ │ │ │ - │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/Panel.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Events/buttonclick.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.Panel │ │ │ │ - * The Panel control is a container for other controls. With it toolbars │ │ │ │ - * may be composed. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - /** │ │ │ │ - * Property: controls │ │ │ │ - * {Array(<OpenLayers.Control>)} │ │ │ │ - */ │ │ │ │ - controls: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * true. │ │ │ │ - */ │ │ │ │ - autoActivate: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: defaultControl │ │ │ │ - * {<OpenLayers.Control>} The control which is activated when the control is │ │ │ │ - * activated (turned on), which also happens at instantiation. │ │ │ │ - * If <saveState> is true, <defaultControl> will be nullified after the │ │ │ │ - * first activation of the panel. │ │ │ │ - */ │ │ │ │ - defaultControl: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: saveState │ │ │ │ - * {Boolean} If set to true, the active state of this panel's controls will │ │ │ │ - * be stored on panel deactivation, and restored on reactivation. Default │ │ │ │ - * is false. │ │ │ │ - */ │ │ │ │ - saveState: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: allowDepress │ │ │ │ - * {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can │ │ │ │ - * be deactivated by clicking the icon that represents them. Default │ │ │ │ - * is false. │ │ │ │ - */ │ │ │ │ - allowDepress: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: activeState │ │ │ │ - * {Object} stores the active state of this panel's controls. │ │ │ │ - */ │ │ │ │ - activeState: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.Panel │ │ │ │ - * Create a new control panel. │ │ │ │ - * │ │ │ │ - * Each control in the panel is represented by an icon. When clicking │ │ │ │ - * on an icon, the <activateControl> method is called. │ │ │ │ - * │ │ │ │ - * Specific properties for controls on a panel: │ │ │ │ - * type - {Number} One of <OpenLayers.Control.TYPE_TOOL>, │ │ │ │ - * <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>. │ │ │ │ - * If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed. │ │ │ │ - * title - {string} Text displayed when mouse is over the icon that │ │ │ │ - * represents the control. │ │ │ │ - * │ │ │ │ - * The <OpenLayers.Control.type> of a control determines the behavior when │ │ │ │ - * clicking its icon: │ │ │ │ - * <OpenLayers.Control.TYPE_TOOL> - The control is activated and other │ │ │ │ - * controls of this type in the same panel are deactivated. This is │ │ │ │ - * the default type. │ │ │ │ - * <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is │ │ │ │ - * toggled. │ │ │ │ - * <OpenLayers.Control.TYPE_BUTTON> - The │ │ │ │ - * <OpenLayers.Control.Button.trigger> method of the control is called, │ │ │ │ - * but its active state is not changed. │ │ │ │ - * │ │ │ │ - * If a control is <OpenLayers.Control.active>, it will be drawn with the │ │ │ │ - * olControl[Name]ItemActive class, otherwise with the │ │ │ │ - * olControl[Name]ItemInactive class. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be used │ │ │ │ - * to extend the control. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.controls = []; │ │ │ │ - this.activeState = {}; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - for (var ctl, i = this.controls.length - 1; i >= 0; i--) { │ │ │ │ - ctl = this.controls[i]; │ │ │ │ - if (ctl.events) { │ │ │ │ - ctl.events.un({ │ │ │ │ - activate: this.iconOn, │ │ │ │ - deactivate: this.iconOff │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - ctl.panel_div = null; │ │ │ │ - } │ │ │ │ - this.activeState = null; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - var control; │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - control = this.controls[i]; │ │ │ │ - if (control === this.defaultControl || │ │ │ │ - (this.saveState && this.activeState[control.id])) { │ │ │ │ - control.activate(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.saveState === true) { │ │ │ │ - this.defaultControl = null; │ │ │ │ - } │ │ │ │ - this.redraw(); │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - var control; │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - control = this.controls[i]; │ │ │ │ - this.activeState[control.id] = control.deactivate(); │ │ │ │ - } │ │ │ │ - this.redraw(); │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (this.outsideViewport) { │ │ │ │ - this.events.attachToElement(this.div); │ │ │ │ - this.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ - } else { │ │ │ │ - this.map.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ - } │ │ │ │ - this.addControlsToMap(this.controls); │ │ │ │ - return this.div; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: redraw │ │ │ │ - */ │ │ │ │ - redraw: function() { │ │ │ │ - for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) { │ │ │ │ - this.div.removeChild(this.div.childNodes[i]); │ │ │ │ - } │ │ │ │ - this.div.innerHTML = ""; │ │ │ │ - if (this.active) { │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - this.div.appendChild(this.controls[i].panel_div); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: activateControl │ │ │ │ - * This method is called when the user click on the icon representing a │ │ │ │ - * control in the panel. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} │ │ │ │ - */ │ │ │ │ - activateControl: function(control) { │ │ │ │ - if (!this.active) { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - if (control.type == OpenLayers.Control.TYPE_BUTTON) { │ │ │ │ - control.trigger(); │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (control.type == OpenLayers.Control.TYPE_TOGGLE) { │ │ │ │ - if (control.active) { │ │ │ │ - control.deactivate(); │ │ │ │ - } else { │ │ │ │ - control.activate(); │ │ │ │ - } │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (this.allowDepress && control.active) { │ │ │ │ - control.deactivate(); │ │ │ │ - } else { │ │ │ │ - var c; │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - c = this.controls[i]; │ │ │ │ - if (c != control && │ │ │ │ - (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) { │ │ │ │ - c.deactivate(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - control.activate(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: addControls │ │ │ │ - * To build a toolbar, you add a set of controls to it. addControls │ │ │ │ - * lets you add a single control or a list of controls to the │ │ │ │ - * Control Panel. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * controls - {<OpenLayers.Control>} Controls to add in the panel. │ │ │ │ - */ │ │ │ │ - addControls: function(controls) { │ │ │ │ - if (!(OpenLayers.Util.isArray(controls))) { │ │ │ │ - controls = [controls]; │ │ │ │ - } │ │ │ │ - this.controls = this.controls.concat(controls); │ │ │ │ - │ │ │ │ - for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ - var control = controls[i], │ │ │ │ - element = this.createControlMarkup(control); │ │ │ │ - OpenLayers.Element.addClass(element, │ │ │ │ - control.displayClass + "ItemInactive"); │ │ │ │ - OpenLayers.Element.addClass(element, "olButton"); │ │ │ │ - if (control.title != "" && !element.title) { │ │ │ │ - element.title = control.title; │ │ │ │ - } │ │ │ │ - control.panel_div = element; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.map) { // map.addControl() has already been called on the panel │ │ │ │ - this.addControlsToMap(controls); │ │ │ │ - this.redraw(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: createControlMarkup │ │ │ │ - * This function just creates a div for the control. If specific HTML │ │ │ │ - * markup is needed this function can be overridden in specific classes, │ │ │ │ - * or at panel instantiation time: │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * var panel = new OpenLayers.Control.Panel({ │ │ │ │ - * defaultControl: control, │ │ │ │ - * // ovverride createControlMarkup to create actual buttons │ │ │ │ - * // including texts wrapped into span elements. │ │ │ │ - * createControlMarkup: function(control) { │ │ │ │ - * var button = document.createElement('button'), │ │ │ │ - * span = document.createElement('span'); │ │ │ │ - * if (control.text) { │ │ │ │ - * span.innerHTML = control.text; │ │ │ │ - * } │ │ │ │ - * return button; │ │ │ │ - * } │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} The control to create the HTML │ │ │ │ - * markup for. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} The markup. │ │ │ │ - */ │ │ │ │ - createControlMarkup: function(control) { │ │ │ │ - return document.createElement("div"); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: addControlsToMap │ │ │ │ - * Only for internal use in draw() and addControls() methods. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * controls - {Array(<OpenLayers.Control>)} Controls to add into map. │ │ │ │ - */ │ │ │ │ - addControlsToMap: function(controls) { │ │ │ │ - var control; │ │ │ │ - for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ - control = controls[i]; │ │ │ │ - if (control.autoActivate === true) { │ │ │ │ - control.autoActivate = false; │ │ │ │ - this.map.addControl(control); │ │ │ │ - control.autoActivate = true; │ │ │ │ - } else { │ │ │ │ - this.map.addControl(control); │ │ │ │ - control.deactivate(); │ │ │ │ - } │ │ │ │ - control.events.on({ │ │ │ │ - activate: this.iconOn, │ │ │ │ - deactivate: this.iconOff │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: iconOn │ │ │ │ - * Internal use, for use only with "controls[i].events.on/un". │ │ │ │ - */ │ │ │ │ - iconOn: function() { │ │ │ │ - var d = this.panel_div; // "this" refers to a control on panel! │ │ │ │ - var re = new RegExp("\\b(" + this.displayClass + "Item)Inactive\\b"); │ │ │ │ - d.className = d.className.replace(re, "$1Active"); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: iconOff │ │ │ │ - * Internal use, for use only with "controls[i].events.on/un". │ │ │ │ - */ │ │ │ │ - iconOff: function() { │ │ │ │ - var d = this.panel_div; // "this" refers to a control on panel! │ │ │ │ - var re = new RegExp("\\b(" + this.displayClass + "Item)Active\\b"); │ │ │ │ - d.className = d.className.replace(re, "$1Inactive"); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: onButtonClick │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - */ │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - var controls = this.controls, │ │ │ │ - button = evt.buttonElement; │ │ │ │ - for (var i = controls.length - 1; i >= 0; --i) { │ │ │ │ - if (controls[i].panel_div === button) { │ │ │ │ - this.activateControl(controls[i]); │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getControlsBy │ │ │ │ - * Get a list of controls with properties matching the given criteria. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * property - {String} A control property to be matched. │ │ │ │ - * match - {String | Object} A string to match. Can also be a regular │ │ │ │ - * expression literal or object. In addition, it can be any object │ │ │ │ - * with a method named test. For reqular expressions or other, if │ │ │ │ - * match.test(control[property]) evaluates to true, the control will be │ │ │ │ - * included in the array returned. If no controls are found, an empty │ │ │ │ - * array is returned. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array(<OpenLayers.Control>)} A list of controls matching the given criteria. │ │ │ │ - * An empty array is returned if no matches are found. │ │ │ │ - */ │ │ │ │ - getControlsBy: function(property, match) { │ │ │ │ - var test = (typeof match.test == "function"); │ │ │ │ - var found = OpenLayers.Array.filter(this.controls, function(item) { │ │ │ │ - return item[property] == match || (test && match.test(item[property])); │ │ │ │ - }); │ │ │ │ - return found; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getControlsByName │ │ │ │ - * Get a list of contorls with names matching the given name. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * match - {String | Object} A control name. The name can also be a regular │ │ │ │ - * expression literal or object. In addition, it can be any object │ │ │ │ - * with a method named test. For reqular expressions or other, if │ │ │ │ - * name.test(control.name) evaluates to true, the control will be included │ │ │ │ - * in the list of controls returned. If no controls are found, an empty │ │ │ │ - * array is returned. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array(<OpenLayers.Control>)} A list of controls matching the given name. │ │ │ │ - * An empty array is returned if no matches are found. │ │ │ │ - */ │ │ │ │ - getControlsByName: function(match) { │ │ │ │ - return this.getControlsBy("name", match); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getControlsByClass │ │ │ │ - * Get a list of controls of a given type (CLASS_NAME). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * match - {String | Object} A control class name. The type can also be a │ │ │ │ - * regular expression literal or object. In addition, it can be any │ │ │ │ - * object with a method named test. For reqular expressions or other, │ │ │ │ - * if type.test(control.CLASS_NAME) evaluates to true, the control will │ │ │ │ - * be included in the list of controls returned. If no controls are │ │ │ │ - * found, an empty array is returned. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array(<OpenLayers.Control>)} A list of controls matching the given type. │ │ │ │ - * An empty array is returned if no matches are found. │ │ │ │ - */ │ │ │ │ - getControlsByClass: function(match) { │ │ │ │ - return this.getControlsBy("CLASS_NAME", match); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Panel" │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/Snapping.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Layer/Vector.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.Snapping │ │ │ │ - * Acts as a snapping agent while editing vector features. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.Snapping = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * beforesnap - Triggered before a snap occurs. Listeners receive an │ │ │ │ - * event object with *point*, *x*, *y*, *distance*, *layer*, and │ │ │ │ - * *snapType* properties. The point property will be original point │ │ │ │ - * geometry considered for snapping. The x and y properties represent │ │ │ │ - * coordinates the point will receive. The distance is the distance │ │ │ │ - * of the snap. The layer is the target layer. The snapType property │ │ │ │ - * will be one of "node", "vertex", or "edge". Return false to stop │ │ │ │ - * snapping from occurring. │ │ │ │ - * snap - Triggered when a snap occurs. Listeners receive an event with │ │ │ │ - * *point*, *snapType*, *layer*, and *distance* properties. The point │ │ │ │ - * will be the location snapped to. The snapType will be one of "node", │ │ │ │ - * "vertex", or "edge". The layer will be the target layer. The │ │ │ │ - * distance will be the distance of the snap in map units. │ │ │ │ - * unsnap - Triggered when a vertex is unsnapped. Listeners receive an │ │ │ │ - * event with a *point* property. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * CONSTANT: DEFAULTS │ │ │ │ - * Default target properties. │ │ │ │ - */ │ │ │ │ - DEFAULTS: { │ │ │ │ - tolerance: 10, │ │ │ │ - node: true, │ │ │ │ - edge: true, │ │ │ │ - vertex: true │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: greedy │ │ │ │ - * {Boolean} Snap to closest feature in first layer with an eligible │ │ │ │ - * feature. Default is true. │ │ │ │ - */ │ │ │ │ - greedy: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: precedence │ │ │ │ - * {Array} List representing precedence of different snapping types. │ │ │ │ - * Default is "node", "vertex", "edge". │ │ │ │ - */ │ │ │ │ - precedence: ["node", "vertex", "edge"], │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: resolution │ │ │ │ - * {Float} The map resolution for the previously considered snap. │ │ │ │ - */ │ │ │ │ - resolution: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: geoToleranceCache │ │ │ │ - * {Object} A cache of geo-tolerances. Tolerance values (in map units) are │ │ │ │ - * calculated when the map resolution changes. │ │ │ │ - */ │ │ │ │ - geoToleranceCache: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: layer │ │ │ │ - * {<OpenLayers.Layer.Vector>} The current editable layer. Set at │ │ │ │ - * construction or after construction with <setLayer>. │ │ │ │ - */ │ │ │ │ - layer: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: feature │ │ │ │ - * {<OpenLayers.Feature.Vector>} The current editable feature. │ │ │ │ - */ │ │ │ │ - feature: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: point │ │ │ │ - * {<OpenLayers.Geometry.Point>} The currently snapped vertex. │ │ │ │ - */ │ │ │ │ - point: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.Snapping │ │ │ │ - * Creates a new snapping control. A control is constructed with an editable │ │ │ │ - * layer and a set of configuration objects for target layers. While the │ │ │ │ - * control is active, dragging vertices while drawing new features or │ │ │ │ - * modifying existing features on the editable layer will engage │ │ │ │ - * snapping to features on the target layers. Whether a vertex snaps to │ │ │ │ - * a feature on a target layer depends on the target layer configuration. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An object containing all configuration properties for │ │ │ │ - * the control. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} The editable layer. Features from this │ │ │ │ - * layer that are digitized or modified may have vertices snapped to │ │ │ │ - * features from any of the target layers. │ │ │ │ - * targets - {Array(Object | OpenLayers.Layer.Vector)} A list of objects for │ │ │ │ - * configuring target layers. See valid properties of the target │ │ │ │ - * objects below. If the items in the targets list are vector layers │ │ │ │ - * (instead of configuration objects), the defaults from the <defaults> │ │ │ │ - * property will apply. The editable layer itself may be a target │ │ │ │ - * layer, allowing newly created or edited features to be snapped to │ │ │ │ - * existing features from the same layer. If no targets are provided │ │ │ │ - * the layer given in the constructor (as <layer>) will become the │ │ │ │ - * initial target. │ │ │ │ - * defaults - {Object} An object with default properties to be applied │ │ │ │ - * to all target objects. │ │ │ │ - * greedy - {Boolean} Snap to closest feature in first target layer that │ │ │ │ - * applies. Default is true. If false, all features in all target │ │ │ │ - * layers will be checked and the closest feature in all target layers │ │ │ │ - * will be chosen. The greedy property determines if the order of the │ │ │ │ - * target layers is significant. By default, the order of the target │ │ │ │ - * layers is significant where layers earlier in the target layer list │ │ │ │ - * have precedence over layers later in the list. Within a single │ │ │ │ - * layer, the closest feature is always chosen for snapping. This │ │ │ │ - * property only determines whether the search for a closer feature │ │ │ │ - * continues after an eligible feature is found in a target layer. │ │ │ │ - * │ │ │ │ - * Valid target properties: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} A target layer. Features from this │ │ │ │ - * layer will be eligible to act as snapping target for the editable │ │ │ │ - * layer. │ │ │ │ - * tolerance - {Float} The distance (in pixels) at which snapping may occur. │ │ │ │ - * Default is 10. │ │ │ │ - * node - {Boolean} Snap to nodes (first or last point in a geometry) in │ │ │ │ - * target layer. Default is true. │ │ │ │ - * nodeTolerance - {Float} Optional distance at which snapping may occur │ │ │ │ - * for nodes specifically. If none is provided, <tolerance> will be │ │ │ │ - * used. │ │ │ │ - * vertex - {Boolean} Snap to vertices in target layer. Default is true. │ │ │ │ - * vertexTolerance - {Float} Optional distance at which snapping may occur │ │ │ │ - * for vertices specifically. If none is provided, <tolerance> will be │ │ │ │ - * used. │ │ │ │ - * edge - {Boolean} Snap to edges in target layer. Default is true. │ │ │ │ - * edgeTolerance - {Float} Optional distance at which snapping may occur │ │ │ │ - * for edges specifically. If none is provided, <tolerance> will be │ │ │ │ - * used. │ │ │ │ - * filter - {<OpenLayers.Filter>} Optional filter to evaluate to determine if │ │ │ │ - * feature is eligible for snapping. If filter evaluates to true for a │ │ │ │ - * target feature a vertex may be snapped to the feature. │ │ │ │ - * minResolution - {Number} If a minResolution is provided, snapping to this │ │ │ │ - * target will only be considered if the map resolution is greater than │ │ │ │ - * or equal to this value (the minResolution is inclusive). Default is │ │ │ │ - * no minimum resolution limit. │ │ │ │ - * maxResolution - {Number} If a maxResolution is provided, snapping to this │ │ │ │ - * target will only be considered if the map resolution is strictly │ │ │ │ - * less than this value (the maxResolution is exclusive). Default is │ │ │ │ - * no maximum resolution limit. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.options = options || {}; // TODO: this could be done by the super │ │ │ │ - │ │ │ │ - // set the editable layer if provided │ │ │ │ - if (this.options.layer) { │ │ │ │ - this.setLayer(this.options.layer); │ │ │ │ - } │ │ │ │ - // configure target layers │ │ │ │ - var defaults = OpenLayers.Util.extend({}, this.options.defaults); │ │ │ │ - this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS); │ │ │ │ - this.setTargets(this.options.targets); │ │ │ │ - if (this.targets.length === 0 && this.layer) { │ │ │ │ - this.addTargetLayer(this.layer); │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.geoToleranceCache = {}; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setLayer │ │ │ │ - * Set the editable layer. Call the setLayer method if the editable layer │ │ │ │ - * changes and the same control should be used on a new editable layer. │ │ │ │ - * If the control is already active, it will be active after the new │ │ │ │ - * layer is set. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} The new editable layer. │ │ │ │ - */ │ │ │ │ - setLayer: function(layer) { │ │ │ │ - if (this.active) { │ │ │ │ - this.deactivate(); │ │ │ │ - this.layer = layer; │ │ │ │ - this.activate(); │ │ │ │ - } else { │ │ │ │ - this.layer = layer; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setTargets │ │ │ │ - * Set the targets for the snapping agent. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * targets - {Array} An array of target configs or target layers. │ │ │ │ - */ │ │ │ │ - setTargets: function(targets) { │ │ │ │ - this.targets = []; │ │ │ │ - if (targets && targets.length) { │ │ │ │ - var target; │ │ │ │ - for (var i = 0, len = targets.length; i < len; ++i) { │ │ │ │ - target = targets[i]; │ │ │ │ - if (target instanceof OpenLayers.Layer.Vector) { │ │ │ │ - this.addTargetLayer(target); │ │ │ │ - } else { │ │ │ │ - this.addTarget(target); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: addTargetLayer │ │ │ │ - * Add a target layer with the default target config. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} A target layer. │ │ │ │ - */ │ │ │ │ - addTargetLayer: function(layer) { │ │ │ │ - this.addTarget({ │ │ │ │ - layer: layer │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: addTarget │ │ │ │ - * Add a configured target layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * target - {Object} A target config. │ │ │ │ - */ │ │ │ │ - addTarget: function(target) { │ │ │ │ - target = OpenLayers.Util.applyDefaults(target, this.defaults); │ │ │ │ - target.nodeTolerance = target.nodeTolerance || target.tolerance; │ │ │ │ - target.vertexTolerance = target.vertexTolerance || target.tolerance; │ │ │ │ - target.edgeTolerance = target.edgeTolerance || target.tolerance; │ │ │ │ - this.targets.push(target); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: removeTargetLayer │ │ │ │ - * Remove a target layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} The target layer to remove. │ │ │ │ - */ │ │ │ │ - removeTargetLayer: function(layer) { │ │ │ │ - var target; │ │ │ │ - for (var i = this.targets.length - 1; i >= 0; --i) { │ │ │ │ - target = this.targets[i]; │ │ │ │ - if (target.layer === layer) { │ │ │ │ - this.removeTarget(target); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: removeTarget │ │ │ │ - * Remove a target. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * target - {Object} A target config. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} The targets array. │ │ │ │ - */ │ │ │ │ - removeTarget: function(target) { │ │ │ │ - return OpenLayers.Util.removeItem(this.targets, target); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Activate the control. Activating the control registers listeners for │ │ │ │ - * editing related events so that during feature creation and │ │ │ │ - * modification, moving vertices will trigger snapping. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Control.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - if (this.layer && this.layer.events) { │ │ │ │ - this.layer.events.on({ │ │ │ │ - sketchstarted: this.onSketchModified, │ │ │ │ - sketchmodified: this.onSketchModified, │ │ │ │ - vertexmodified: this.onVertexModified, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return activated; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the control. Deactivating the control unregisters listeners │ │ │ │ - * so feature editing may proceed without engaging the snapping agent. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Control.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - if (this.layer && this.layer.events) { │ │ │ │ - this.layer.events.un({ │ │ │ │ - sketchstarted: this.onSketchModified, │ │ │ │ - sketchmodified: this.onSketchModified, │ │ │ │ - vertexmodified: this.onVertexModified, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.feature = null; │ │ │ │ - this.point = null; │ │ │ │ - return deactivated; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: onSketchModified │ │ │ │ - * Registered as a listener for the sketchmodified event on the editable │ │ │ │ - * layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * event - {Object} The sketch modified event. │ │ │ │ - */ │ │ │ │ - onSketchModified: function(event) { │ │ │ │ - this.feature = event.feature; │ │ │ │ - this.considerSnapping(event.vertex, event.vertex); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: onVertexModified │ │ │ │ - * Registered as a listener for the vertexmodified event on the editable │ │ │ │ - * layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * event - {Object} The vertex modified event. │ │ │ │ - */ │ │ │ │ - onVertexModified: function(event) { │ │ │ │ - this.feature = event.feature; │ │ │ │ - var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel); │ │ │ │ - this.considerSnapping( │ │ │ │ - event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat) │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: considerSnapping │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} The vertex to be snapped (or │ │ │ │ - * unsnapped). │ │ │ │ - * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map │ │ │ │ - * coords. │ │ │ │ - */ │ │ │ │ - considerSnapping: function(point, loc) { │ │ │ │ - var best = { │ │ │ │ - rank: Number.POSITIVE_INFINITY, │ │ │ │ - dist: Number.POSITIVE_INFINITY, │ │ │ │ - x: null, │ │ │ │ - y: null │ │ │ │ - }; │ │ │ │ - var snapped = false; │ │ │ │ - var result, target; │ │ │ │ - for (var i = 0, len = this.targets.length; i < len; ++i) { │ │ │ │ - target = this.targets[i]; │ │ │ │ - result = this.testTarget(target, loc); │ │ │ │ - if (result) { │ │ │ │ - if (this.greedy) { │ │ │ │ - best = result; │ │ │ │ - best.target = target; │ │ │ │ - snapped = true; │ │ │ │ - break; │ │ │ │ - } else { │ │ │ │ - if ((result.rank < best.rank) || │ │ │ │ - (result.rank === best.rank && result.dist < best.dist)) { │ │ │ │ - best = result; │ │ │ │ - best.target = target; │ │ │ │ - snapped = true; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (snapped) { │ │ │ │ - var proceed = this.events.triggerEvent("beforesnap", { │ │ │ │ - point: point, │ │ │ │ - x: best.x, │ │ │ │ - y: best.y, │ │ │ │ - distance: best.dist, │ │ │ │ - layer: best.target.layer, │ │ │ │ - snapType: this.precedence[best.rank] │ │ │ │ - }); │ │ │ │ - if (proceed !== false) { │ │ │ │ - point.x = best.x; │ │ │ │ - point.y = best.y; │ │ │ │ - this.point = point; │ │ │ │ - this.events.triggerEvent("snap", { │ │ │ │ - point: point, │ │ │ │ - snapType: this.precedence[best.rank], │ │ │ │ - layer: best.target.layer, │ │ │ │ - distance: best.dist │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - snapped = false; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.point && !snapped) { │ │ │ │ - point.x = loc.x; │ │ │ │ - point.y = loc.y; │ │ │ │ - this.point = null; │ │ │ │ - this.events.triggerEvent("unsnap", { │ │ │ │ - point: point │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: testTarget │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * target - {Object} Object with target layer configuration. │ │ │ │ - * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map │ │ │ │ - * coords. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} A result object with rank, dist, x, and y properties. │ │ │ │ - * Returns null if candidate is not eligible for snapping. │ │ │ │ - */ │ │ │ │ - testTarget: function(target, loc) { │ │ │ │ - var resolution = this.layer.map.getResolution(); │ │ │ │ - if ("minResolution" in target) { │ │ │ │ - if (resolution < target.minResolution) { │ │ │ │ - return null; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if ("maxResolution" in target) { │ │ │ │ - if (resolution >= target.maxResolution) { │ │ │ │ - return null; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var tolerance = { │ │ │ │ - node: this.getGeoTolerance(target.nodeTolerance, resolution), │ │ │ │ - vertex: this.getGeoTolerance(target.vertexTolerance, resolution), │ │ │ │ - edge: this.getGeoTolerance(target.edgeTolerance, resolution) │ │ │ │ - }; │ │ │ │ - // this could be cached if we don't support setting tolerance values directly │ │ │ │ - var maxTolerance = Math.max( │ │ │ │ - tolerance.node, tolerance.vertex, tolerance.edge │ │ │ │ - ); │ │ │ │ - var result = { │ │ │ │ - rank: Number.POSITIVE_INFINITY, │ │ │ │ - dist: Number.POSITIVE_INFINITY │ │ │ │ - }; │ │ │ │ - var eligible = false; │ │ │ │ - var features = target.layer.features; │ │ │ │ - var feature, type, vertices, vertex, closest, dist, found; │ │ │ │ - var numTypes = this.precedence.length; │ │ │ │ - var ll = new OpenLayers.LonLat(loc.x, loc.y); │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - feature = features[i]; │ │ │ │ - if (feature !== this.feature && !feature._sketch && │ │ │ │ - feature.state !== OpenLayers.State.DELETE && │ │ │ │ - (!target.filter || target.filter.evaluate(feature))) { │ │ │ │ - if (feature.atPoint(ll, maxTolerance, maxTolerance)) { │ │ │ │ - for (var j = 0, stop = Math.min(result.rank + 1, numTypes); j < stop; ++j) { │ │ │ │ - type = this.precedence[j]; │ │ │ │ - if (target[type]) { │ │ │ │ - if (type === "edge") { │ │ │ │ - closest = feature.geometry.distanceTo(loc, { │ │ │ │ - details: true │ │ │ │ - }); │ │ │ │ - dist = closest.distance; │ │ │ │ - if (dist <= tolerance[type] && dist < result.dist) { │ │ │ │ - result = { │ │ │ │ - rank: j, │ │ │ │ - dist: dist, │ │ │ │ - x: closest.x0, │ │ │ │ - y: closest.y0 // closest coords on feature │ │ │ │ - }; │ │ │ │ - eligible = true; │ │ │ │ - // don't look for lower precedence types for this feature │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - // look for nodes or vertices │ │ │ │ - vertices = feature.geometry.getVertices(type === "node"); │ │ │ │ - found = false; │ │ │ │ - for (var k = 0, klen = vertices.length; k < klen; ++k) { │ │ │ │ - vertex = vertices[k]; │ │ │ │ - dist = vertex.distanceTo(loc); │ │ │ │ - if (dist <= tolerance[type] && │ │ │ │ - (j < result.rank || (j === result.rank && dist < result.dist))) { │ │ │ │ - result = { │ │ │ │ - rank: j, │ │ │ │ - dist: dist, │ │ │ │ - x: vertex.x, │ │ │ │ - y: vertex.y │ │ │ │ - }; │ │ │ │ - eligible = true; │ │ │ │ - found = true; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (found) { │ │ │ │ - // don't look for lower precedence types for this feature │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return eligible ? result : null; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getGeoTolerance │ │ │ │ - * Calculate a tolerance in map units given a tolerance in pixels. This │ │ │ │ - * takes advantage of the <geoToleranceCache> when the map resolution │ │ │ │ - * has not changed. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * tolerance - {Number} A tolerance value in pixels. │ │ │ │ - * resolution - {Number} Map resolution. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Number} A tolerance value in map units. │ │ │ │ - */ │ │ │ │ - getGeoTolerance: function(tolerance, resolution) { │ │ │ │ - if (resolution !== this.resolution) { │ │ │ │ - this.resolution = resolution; │ │ │ │ - this.geoToleranceCache = {}; │ │ │ │ - } │ │ │ │ - var geoTolerance = this.geoToleranceCache[tolerance]; │ │ │ │ - if (geoTolerance === undefined) { │ │ │ │ - geoTolerance = tolerance * resolution; │ │ │ │ - this.geoToleranceCache[tolerance] = geoTolerance; │ │ │ │ - } │ │ │ │ - return geoTolerance; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * Clean up the control. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.deactivate(); // TODO: this should be handled by the super │ │ │ │ - } │ │ │ │ - delete this.layer; │ │ │ │ - delete this.targets; │ │ │ │ - OpenLayers.Control.prototype.destroy.call(this); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Snapping" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/CacheWrite.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Request.js │ │ │ │ - * @requires OpenLayers/Console.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.CacheWrite │ │ │ │ - * A control for caching image tiles in the browser's local storage. The │ │ │ │ - * <OpenLayers.Control.CacheRead> control is used to fetch and use the cached │ │ │ │ - * tile images. │ │ │ │ - * │ │ │ │ - * Note: Before using this control on any layer that is not your own, make sure │ │ │ │ - * that the terms of service of the tile provider allow local storage of tiles. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * To register events in the constructor, configure <eventListeners>. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * cachefull - Triggered when the cache is full. Listeners receive an │ │ │ │ - * object with a tile property as first argument. The tile references │ │ │ │ - * the tile that couldn't be cached. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: eventListeners │ │ │ │ - * {Object} Object with event listeners, keyed by event name. An optional │ │ │ │ - * scope property defines the scope that listeners will be executed in. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: layers │ │ │ │ - * {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, caching │ │ │ │ - * will be enabled for these layers only, otherwise for all cacheable │ │ │ │ - * layers. │ │ │ │ - */ │ │ │ │ - layers: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: imageFormat │ │ │ │ - * {String} The image format used for caching. The default is "image/png". │ │ │ │ - * Supported formats depend on the user agent. If an unsupported │ │ │ │ - * <imageFormat> is provided, "image/png" will be used. For aerial │ │ │ │ - * imagery, "image/jpeg" is recommended. │ │ │ │ - */ │ │ │ │ - imageFormat: "image/png", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: quotaRegEx │ │ │ │ - * {RegExp} │ │ │ │ - */ │ │ │ │ - quotaRegEx: (/quota/i), │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.CacheWrite │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Object with API properties for this control. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the control. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - var i, layers = this.layers || map.layers; │ │ │ │ - for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ - this.addLayer({ │ │ │ │ - layer: layers[i] │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - if (!this.layers) { │ │ │ │ - map.events.on({ │ │ │ │ - addlayer: this.addLayer, │ │ │ │ - removeLayer: this.removeLayer, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: addLayer │ │ │ │ - * Adds a layer to the control. Once added, tiles requested for this layer │ │ │ │ - * will be cached. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} Object with a layer property referencing an │ │ │ │ - * <OpenLayers.Layer> instance │ │ │ │ - */ │ │ │ │ - addLayer: function(evt) { │ │ │ │ - evt.layer.events.on({ │ │ │ │ - tileloadstart: this.makeSameOrigin, │ │ │ │ - tileloaded: this.onTileLoaded, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: removeLayer │ │ │ │ - * Removes a layer from the control. Once removed, tiles requested for this │ │ │ │ - * layer will no longer be cached. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} Object with a layer property referencing an │ │ │ │ - * <OpenLayers.Layer> instance │ │ │ │ - */ │ │ │ │ - removeLayer: function(evt) { │ │ │ │ - evt.layer.events.un({ │ │ │ │ - tileloadstart: this.makeSameOrigin, │ │ │ │ - tileloaded: this.onTileLoaded, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: makeSameOrigin │ │ │ │ - * If the tile does not have CORS image loading enabled and is from a │ │ │ │ - * different origin, use OpenLayers.ProxyHost to make it a same origin url. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ - */ │ │ │ │ - makeSameOrigin: function(evt) { │ │ │ │ - if (this.active) { │ │ │ │ - var tile = evt.tile; │ │ │ │ - if (tile instanceof OpenLayers.Tile.Image && │ │ │ │ - !tile.crossOriginKeyword && │ │ │ │ - tile.url.substr(0, 5) !== "data:") { │ │ │ │ - var sameOriginUrl = OpenLayers.Request.makeSameOrigin( │ │ │ │ - tile.url, OpenLayers.ProxyHost │ │ │ │ - ); │ │ │ │ - OpenLayers.Control.CacheWrite.urlMap[sameOriginUrl] = tile.url; │ │ │ │ - tile.url = sameOriginUrl; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: onTileLoaded │ │ │ │ - * Decides whether a tile can be cached and calls the cache method. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - */ │ │ │ │ - onTileLoaded: function(evt) { │ │ │ │ - if (this.active && !evt.aborted && │ │ │ │ - evt.tile instanceof OpenLayers.Tile.Image && │ │ │ │ - evt.tile.url.substr(0, 5) !== 'data:') { │ │ │ │ - this.cache({ │ │ │ │ - tile: evt.tile │ │ │ │ - }); │ │ │ │ - delete OpenLayers.Control.CacheWrite.urlMap[evt.tile.url]; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: cache │ │ │ │ - * Adds a tile to the cache. When the cache is full, the "cachefull" event │ │ │ │ - * is triggered. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {Object} Object with a tile property, tile being the │ │ │ │ - * <OpenLayers.Tile.Image> with the data to add to the cache │ │ │ │ - */ │ │ │ │ - cache: function(obj) { │ │ │ │ - if (window.localStorage) { │ │ │ │ - var tile = obj.tile; │ │ │ │ - try { │ │ │ │ - var canvasContext = tile.getCanvasContext(); │ │ │ │ - if (canvasContext) { │ │ │ │ - var urlMap = OpenLayers.Control.CacheWrite.urlMap; │ │ │ │ - var url = urlMap[tile.url] || tile.url; │ │ │ │ - window.localStorage.setItem( │ │ │ │ - "olCache_" + url, │ │ │ │ - canvasContext.canvas.toDataURL(this.imageFormat) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } catch (e) { │ │ │ │ - // local storage full or CORS violation │ │ │ │ - var reason = e.name || e.message; │ │ │ │ - if (reason && this.quotaRegEx.test(reason)) { │ │ │ │ - this.events.triggerEvent("cachefull", { │ │ │ │ - tile: tile │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - OpenLayers.Console.error(e.toString()); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * The destroy method is used to perform any clean up before the control │ │ │ │ - * is dereferenced. Typically this is where event listeners are removed │ │ │ │ - * to prevent memory leaks. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.layers || this.map) { │ │ │ │ - var i, layers = this.layers || this.map.layers; │ │ │ │ - for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ - this.removeLayer({ │ │ │ │ - layer: layers[i] │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.un({ │ │ │ │ - addlayer: this.addLayer, │ │ │ │ - removeLayer: this.removeLayer, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Control.CacheWrite" │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * APIFunction: OpenLayers.Control.CacheWrite.clearCache │ │ │ │ - * Clears all tiles cached with <OpenLayers.Control.CacheWrite> from the cache. │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.CacheWrite.clearCache = function() { │ │ │ │ - if (!window.localStorage) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - var i, key; │ │ │ │ - for (i = window.localStorage.length - 1; i >= 0; --i) { │ │ │ │ - key = window.localStorage.key(i); │ │ │ │ - if (key.substr(0, 8) === "olCache_") { │ │ │ │ - window.localStorage.removeItem(key); │ │ │ │ - } │ │ │ │ - } │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Property: OpenLayers.Control.CacheWrite.urlMap │ │ │ │ - * {Object} Mapping of same origin urls to cache url keys. Entries will be │ │ │ │ - * deleted as soon as a tile was cached. │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.CacheWrite.urlMap = {}; │ │ │ │ - │ │ │ │ - │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Handler/Drag.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Handler.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Handler.Drag │ │ │ │ - * The drag handler is used to deal with sequences of browser events related │ │ │ │ - * to dragging. The handler is used by controls that want to know when │ │ │ │ - * a drag sequence begins, when a drag is happening, and when it has │ │ │ │ - * finished. │ │ │ │ - * │ │ │ │ - * Controls that use the drag handler typically construct it with callbacks │ │ │ │ - * for 'down', 'move', and 'done'. Callbacks for these keys are called │ │ │ │ - * when the drag begins, with each move, and when the drag is done. In │ │ │ │ - * addition, controls can have callbacks keyed to 'up' and 'out' if they │ │ │ │ - * care to differentiate between the types of events that correspond with │ │ │ │ - * the end of a drag sequence. If no drag actually occurs (no mouse move) │ │ │ │ - * the 'down' and 'up' callbacks will be called, but not the 'done' │ │ │ │ - * callback. │ │ │ │ - * │ │ │ │ - * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Handler> │ │ │ │ - */ │ │ │ │ -OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: started │ │ │ │ - * {Boolean} When a mousedown or touchstart event is received, we want to │ │ │ │ - * record it, but not set 'dragging' until the mouse moves after starting. │ │ │ │ - */ │ │ │ │ - started: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: stopDown │ │ │ │ - * {Boolean} Stop propagation of mousedown events from getting to listeners │ │ │ │ - * on the same element. Default is true. │ │ │ │ - */ │ │ │ │ - stopDown: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: dragging │ │ │ │ - * {Boolean} │ │ │ │ - */ │ │ │ │ - dragging: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: last │ │ │ │ - * {<OpenLayers.Pixel>} The last pixel location of the drag. │ │ │ │ - */ │ │ │ │ - last: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: start │ │ │ │ - * {<OpenLayers.Pixel>} The first pixel location of the drag. │ │ │ │ - */ │ │ │ │ - start: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: lastMoveEvt │ │ │ │ - * {Object} The last mousemove event that occurred. Used to │ │ │ │ - * position the map correctly when our "delay drag" │ │ │ │ - * timeout expired. │ │ │ │ - */ │ │ │ │ - lastMoveEvt: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: oldOnselectstart │ │ │ │ - * {Function} │ │ │ │ - */ │ │ │ │ - oldOnselectstart: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: interval │ │ │ │ - * {Integer} In order to increase performance, an interval (in │ │ │ │ - * milliseconds) can be set to reduce the number of drag events │ │ │ │ - * called. If set, a new drag event will not be set until the │ │ │ │ - * interval has passed. │ │ │ │ - * Defaults to 0, meaning no interval. │ │ │ │ - */ │ │ │ │ - interval: 0, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: timeoutId │ │ │ │ - * {String} The id of the timeout used for the mousedown interval. │ │ │ │ - * This is "private", and should be left alone. │ │ │ │ - */ │ │ │ │ - timeoutId: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: documentDrag │ │ │ │ - * {Boolean} If set to true, the handler will also handle mouse moves when │ │ │ │ - * the cursor has moved out of the map viewport. Default is false. │ │ │ │ - */ │ │ │ │ - documentDrag: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: documentEvents │ │ │ │ - * {Boolean} Are we currently observing document events? │ │ │ │ - */ │ │ │ │ - documentEvents: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Handler.Drag │ │ │ │ - * Returns OpenLayers.Handler.Drag │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} The control that is making use of │ │ │ │ - * this handler. If a handler is being used without a control, the │ │ │ │ - * handlers setMap method must be overridden to deal properly with │ │ │ │ - * the map. │ │ │ │ - * callbacks - {Object} An object containing a single function to be │ │ │ │ - * called when the drag operation is finished. The callback should │ │ │ │ - * expect to recieve a single argument, the pixel location of the event. │ │ │ │ - * Callbacks for 'move' and 'done' are supported. You can also speficy │ │ │ │ - * callbacks for 'down', 'up', and 'out' to respond to those events. │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - │ │ │ │ - if (this.documentDrag === true) { │ │ │ │ - var me = this; │ │ │ │ - this._docMove = function(evt) { │ │ │ │ - me.mousemove({ │ │ │ │ - xy: { │ │ │ │ - x: evt.clientX, │ │ │ │ - y: evt.clientY │ │ │ │ - }, │ │ │ │ - element: document │ │ │ │ - }); │ │ │ │ - }; │ │ │ │ - this._docUp = function(evt) { │ │ │ │ - me.mouseup({ │ │ │ │ - xy: { │ │ │ │ - x: evt.clientX, │ │ │ │ - y: evt.clientY │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ + │ │ │ │ /** │ │ │ │ * Method: dragstart │ │ │ │ * This private method is factorized from mousedown and touchstart methods │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ * evt - {Event} The event │ │ │ │ * │ │ │ │ @@ -46851,383 +41206,280 @@ │ │ │ │ this.map.getLayerIndex(this.layer)); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ CLASS_NAME: "OpenLayers.Handler.Feature" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/DragFeature.js │ │ │ │ + OpenLayers/Handler/MouseWheel.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Handler/Drag.js │ │ │ │ - * @requires OpenLayers/Handler/Feature.js │ │ │ │ + * @requires OpenLayers/Handler.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.DragFeature │ │ │ │ - * The DragFeature control moves a feature with a drag of the mouse. Create a │ │ │ │ - * new control with the <OpenLayers.Control.DragFeature> constructor. │ │ │ │ - * │ │ │ │ - * Inherits From: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * Class: OpenLayers.Handler.MouseWheel │ │ │ │ + * Handler for wheel up/down events. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Handler> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: geometryTypes │ │ │ │ - * {Array(String)} To restrict dragging to a limited set of geometry types, │ │ │ │ - * send a list of strings corresponding to the geometry class names. │ │ │ │ +OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + /** │ │ │ │ + * Property: wheelListener │ │ │ │ + * {function} │ │ │ │ */ │ │ │ │ - geometryTypes: null, │ │ │ │ + wheelListener: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: onStart │ │ │ │ - * {Function} Define this function if you want to know when a drag starts. │ │ │ │ - * The function should expect to receive two arguments: the feature │ │ │ │ - * that is about to be dragged and the pixel location of the mouse. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The feature that is about to be │ │ │ │ - * dragged. │ │ │ │ - * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse. │ │ │ │ + * Property: interval │ │ │ │ + * {Integer} In order to increase server performance, an interval (in │ │ │ │ + * milliseconds) can be set to reduce the number of up/down events │ │ │ │ + * called. If set, a new up/down event will not be set until the │ │ │ │ + * interval has passed. │ │ │ │ + * Defaults to 0, meaning no interval. │ │ │ │ */ │ │ │ │ - onStart: function(feature, pixel) {}, │ │ │ │ + interval: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: onDrag │ │ │ │ - * {Function} Define this function if you want to know about each move of a │ │ │ │ - * feature. The function should expect to receive two arguments: the │ │ │ │ - * feature that is being dragged and the pixel location of the mouse. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged. │ │ │ │ - * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse. │ │ │ │ + * Property: maxDelta │ │ │ │ + * {Integer} Maximum delta to collect before breaking from the current │ │ │ │ + * interval. In cumulative mode, this also limits the maximum delta │ │ │ │ + * returned from the handler. Default is Number.POSITIVE_INFINITY. │ │ │ │ */ │ │ │ │ - onDrag: function(feature, pixel) {}, │ │ │ │ + maxDelta: Number.POSITIVE_INFINITY, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: onComplete │ │ │ │ - * {Function} Define this function if you want to know when a feature is │ │ │ │ - * done dragging. The function should expect to receive two arguments: │ │ │ │ - * the feature that is being dragged and the pixel location of the │ │ │ │ - * mouse. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged. │ │ │ │ - * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse. │ │ │ │ + * Property: delta │ │ │ │ + * {Integer} When interval is set, delta collects the mousewheel z-deltas │ │ │ │ + * of the events that occur within the interval. │ │ │ │ + * See also the cumulative option │ │ │ │ */ │ │ │ │ - onComplete: function(feature, pixel) {}, │ │ │ │ + delta: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: onEnter │ │ │ │ - * {Function} Define this function if you want to know when the mouse │ │ │ │ - * goes over a feature and thereby makes this feature a candidate │ │ │ │ - * for dragging. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The feature that is ready │ │ │ │ - * to be dragged. │ │ │ │ + * Property: cumulative │ │ │ │ + * {Boolean} When interval is set: true to collect all the mousewheel │ │ │ │ + * z-deltas, false to only record the delta direction (positive or │ │ │ │ + * negative) │ │ │ │ */ │ │ │ │ - onEnter: function(feature) {}, │ │ │ │ + cumulative: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: onLeave │ │ │ │ - * {Function} Define this function if you want to know when the mouse │ │ │ │ - * goes out of the feature that was dragged. │ │ │ │ + * Constructor: OpenLayers.Handler.MouseWheel │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged. │ │ │ │ + * control - {<OpenLayers.Control>} │ │ │ │ + * callbacks - {Object} An object containing a single function to be │ │ │ │ + * called when the drag operation is finished. │ │ │ │ + * The callback should expect to recieve a single │ │ │ │ + * argument, the point geometry. │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - onLeave: function(feature) {}, │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ + this.wheelListener = OpenLayers.Function.bindAsEventListener( │ │ │ │ + this.onWheelEvent, this │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: documentDrag │ │ │ │ - * {Boolean} If set to true, mouse dragging will continue even if the │ │ │ │ - * mouse cursor leaves the map viewport. Default is false. │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - documentDrag: false, │ │ │ │ + destroy: function() { │ │ │ │ + OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ + this.wheelListener = null; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: layer │ │ │ │ - * {<OpenLayers.Layer.Vector>} │ │ │ │ + * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/ │ │ │ │ */ │ │ │ │ - layer: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: feature │ │ │ │ - * {<OpenLayers.Feature.Vector>} │ │ │ │ + /** │ │ │ │ + * Method: onWheelEvent │ │ │ │ + * Catch the wheel event and handle it xbrowserly │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * e - {Event} │ │ │ │ */ │ │ │ │ - feature: null, │ │ │ │ + onWheelEvent: function(e) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: dragCallbacks │ │ │ │ - * {Object} The functions that are sent to the drag handler for callback. │ │ │ │ - */ │ │ │ │ - dragCallbacks: {}, │ │ │ │ + // make sure we have a map and check keyboard modifiers │ │ │ │ + if (!this.map || !this.checkModifiers(e)) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: featureCallbacks │ │ │ │ - * {Object} The functions that are sent to the feature handler for callback. │ │ │ │ - */ │ │ │ │ - featureCallbacks: {}, │ │ │ │ + // Ride up the element's DOM hierarchy to determine if it or any of │ │ │ │ + // its ancestors was: │ │ │ │ + // * specifically marked as scrollable (CSS overflow property) │ │ │ │ + // * one of our layer divs or a div marked as scrollable │ │ │ │ + // ('olScrollable' CSS class) │ │ │ │ + // * the map div │ │ │ │ + // │ │ │ │ + var overScrollableDiv = false; │ │ │ │ + var allowScroll = false; │ │ │ │ + var overMapDiv = false; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: lastPixel │ │ │ │ - * {<OpenLayers.Pixel>} │ │ │ │ - */ │ │ │ │ - lastPixel: null, │ │ │ │ + var elem = OpenLayers.Event.element(e); │ │ │ │ + while ((elem != null) && !overMapDiv && !overScrollableDiv) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.DragFeature │ │ │ │ - * Create a new control to drag features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} The layer containing features to be │ │ │ │ - * dragged. │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * control. │ │ │ │ - */ │ │ │ │ - initialize: function(layer, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.layer = layer; │ │ │ │ - this.handlers = { │ │ │ │ - drag: new OpenLayers.Handler.Drag( │ │ │ │ - this, OpenLayers.Util.extend({ │ │ │ │ - down: this.downFeature, │ │ │ │ - move: this.moveFeature, │ │ │ │ - up: this.upFeature, │ │ │ │ - out: this.cancel, │ │ │ │ - done: this.doneDragging │ │ │ │ - }, this.dragCallbacks), { │ │ │ │ - documentDrag: this.documentDrag │ │ │ │ - } │ │ │ │ - ), │ │ │ │ - feature: new OpenLayers.Handler.Feature( │ │ │ │ - this, this.layer, OpenLayers.Util.extend({ │ │ │ │ - // 'click' and 'clickout' callback are for the mobile │ │ │ │ - // support: no 'over' or 'out' in touch based browsers. │ │ │ │ - click: this.clickFeature, │ │ │ │ - clickout: this.clickoutFeature, │ │ │ │ - over: this.overFeature, │ │ │ │ - out: this.outFeature │ │ │ │ - }, this.featureCallbacks), { │ │ │ │ - geometryTypes: this.geometryTypes │ │ │ │ + if (!overScrollableDiv) { │ │ │ │ + try { │ │ │ │ + var overflow; │ │ │ │ + if (elem.currentStyle) { │ │ │ │ + overflow = elem.currentStyle["overflow"]; │ │ │ │ + } else { │ │ │ │ + var style = │ │ │ │ + document.defaultView.getComputedStyle(elem, null); │ │ │ │ + overflow = style.getPropertyValue("overflow"); │ │ │ │ + } │ │ │ │ + overScrollableDiv = (overflow && │ │ │ │ + (overflow == "auto") || (overflow == "scroll")); │ │ │ │ + } catch (err) { │ │ │ │ + //sometimes when scrolling in a popup, this causes │ │ │ │ + // obscure browser error │ │ │ │ } │ │ │ │ - ) │ │ │ │ - }; │ │ │ │ - }, │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: clickFeature │ │ │ │ - * Called when the feature handler detects a click-in on a feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - */ │ │ │ │ - clickFeature: function(feature) { │ │ │ │ - if (this.handlers.feature.touch && !this.over && this.overFeature(feature)) { │ │ │ │ - this.handlers.drag.dragstart(this.handlers.feature.evt); │ │ │ │ - // to let the events propagate to the feature handler (click callback) │ │ │ │ - this.handlers.drag.stopDown = false; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + if (!allowScroll) { │ │ │ │ + allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable'); │ │ │ │ + if (!allowScroll) { │ │ │ │ + for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ + // Are we in the layer div? Note that we have two cases │ │ │ │ + // here: one is to catch EventPane layers, which have a │ │ │ │ + // pane above the layer (layer.pane) │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + if (elem == layer.div || elem == layer.pane) { │ │ │ │ + allowScroll = true; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + overMapDiv = (elem == this.map.div); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: clickoutFeature │ │ │ │ - * Called when the feature handler detects a click-out on a feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - */ │ │ │ │ - clickoutFeature: function(feature) { │ │ │ │ - if (this.handlers.feature.touch && this.over) { │ │ │ │ - this.outFeature(feature); │ │ │ │ - this.handlers.drag.stopDown = true; │ │ │ │ + elem = elem.parentNode; │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Take care of things that are not handled in superclass │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - this.layer = null; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, []); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Activate the control and the feature handler. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Successfully activated the control and feature handler. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - return (this.handlers.feature.activate() && │ │ │ │ - OpenLayers.Control.prototype.activate.apply(this, arguments)); │ │ │ │ - }, │ │ │ │ + // Logic below is the following: │ │ │ │ + // │ │ │ │ + // If we are over a scrollable div or not over the map div: │ │ │ │ + // * do nothing (let the browser handle scrolling) │ │ │ │ + // │ │ │ │ + // otherwise │ │ │ │ + // │ │ │ │ + // If we are over the layer div or a 'olScrollable' div: │ │ │ │ + // * zoom/in out │ │ │ │ + // then │ │ │ │ + // * kill event (so as not to also scroll the page after zooming) │ │ │ │ + // │ │ │ │ + // otherwise │ │ │ │ + // │ │ │ │ + // Kill the event (dont scroll the page if we wheel over the │ │ │ │ + // layerswitcher or the pan/zoom control) │ │ │ │ + // │ │ │ │ + if (!overScrollableDiv && overMapDiv) { │ │ │ │ + if (allowScroll) { │ │ │ │ + var delta = 0; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the control and all handlers. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Successfully deactivated the control. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - // the return from the handlers is unimportant in this case │ │ │ │ - this.handlers.drag.deactivate(); │ │ │ │ - this.handlers.feature.deactivate(); │ │ │ │ - this.feature = null; │ │ │ │ - this.dragging = false; │ │ │ │ - this.lastPixel = null; │ │ │ │ - OpenLayers.Element.removeClass( │ │ │ │ - this.map.viewPortDiv, this.displayClass + "Over" │ │ │ │ - ); │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + if (e.wheelDelta) { │ │ │ │ + delta = e.wheelDelta; │ │ │ │ + if (delta % 160 === 0) { │ │ │ │ + // opera have steps of 160 instead of 120 │ │ │ │ + delta = delta * 0.75; │ │ │ │ + } │ │ │ │ + delta = delta / 120; │ │ │ │ + } else if (e.detail) { │ │ │ │ + // detail in Firefox on OS X is 1/3 of Windows │ │ │ │ + // so force delta 1 / -1 │ │ │ │ + delta = -(e.detail / Math.abs(e.detail)); │ │ │ │ + } │ │ │ │ + this.delta += delta; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: overFeature │ │ │ │ - * Called when the feature handler detects a mouse-over on a feature. │ │ │ │ - * This activates the drag handler. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The selected feature. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Successfully activated the drag handler. │ │ │ │ - */ │ │ │ │ - overFeature: function(feature) { │ │ │ │ - var activated = false; │ │ │ │ - if (!this.handlers.drag.dragging) { │ │ │ │ - this.feature = feature; │ │ │ │ - this.handlers.drag.activate(); │ │ │ │ - activated = true; │ │ │ │ - this.over = true; │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + "Over"); │ │ │ │ - this.onEnter(feature); │ │ │ │ - } else { │ │ │ │ - if (this.feature.id == feature.id) { │ │ │ │ - this.over = true; │ │ │ │ - } else { │ │ │ │ - this.over = false; │ │ │ │ + window.clearTimeout(this._timeoutId); │ │ │ │ + if (this.interval && Math.abs(this.delta) < this.maxDelta) { │ │ │ │ + // store e because window.event might change during delay │ │ │ │ + var evt = OpenLayers.Util.extend({}, e); │ │ │ │ + this._timeoutId = window.setTimeout( │ │ │ │ + OpenLayers.Function.bind(function() { │ │ │ │ + this.wheelZoom(evt); │ │ │ │ + }, this), │ │ │ │ + this.interval │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + this.wheelZoom(e); │ │ │ │ + } │ │ │ │ } │ │ │ │ + OpenLayers.Event.stop(e); │ │ │ │ } │ │ │ │ - return activated; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: downFeature │ │ │ │ - * Called when the drag handler detects a mouse-down. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * pixel - {<OpenLayers.Pixel>} Location of the mouse event. │ │ │ │ - */ │ │ │ │ - downFeature: function(pixel) { │ │ │ │ - this.lastPixel = pixel; │ │ │ │ - this.onStart(this.feature, pixel); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveFeature │ │ │ │ - * Called when the drag handler detects a mouse-move. Also calls the │ │ │ │ - * optional onDrag method. │ │ │ │ + * Method: wheelZoom │ │ │ │ + * Given the wheel event, we carry out the appropriate zooming in or out, │ │ │ │ + * based on the 'wheelDelta' or 'detail' property of the event. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * pixel - {<OpenLayers.Pixel>} Location of the mouse event. │ │ │ │ + * e - {Event} │ │ │ │ */ │ │ │ │ - moveFeature: function(pixel) { │ │ │ │ - var res = this.map.getResolution(); │ │ │ │ - this.feature.geometry.move(res * (pixel.x - this.lastPixel.x), │ │ │ │ - res * (this.lastPixel.y - pixel.y)); │ │ │ │ - this.layer.drawFeature(this.feature); │ │ │ │ - this.lastPixel = pixel; │ │ │ │ - this.onDrag(this.feature, pixel); │ │ │ │ - }, │ │ │ │ + wheelZoom: function(e) { │ │ │ │ + var delta = this.delta; │ │ │ │ + this.delta = 0; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: upFeature │ │ │ │ - * Called when the drag handler detects a mouse-up. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * pixel - {<OpenLayers.Pixel>} Location of the mouse event. │ │ │ │ - */ │ │ │ │ - upFeature: function(pixel) { │ │ │ │ - if (!this.over) { │ │ │ │ - this.handlers.drag.deactivate(); │ │ │ │ + if (delta) { │ │ │ │ + e.xy = this.map.events.getMousePosition(e); │ │ │ │ + if (delta < 0) { │ │ │ │ + this.callback("down", │ │ │ │ + [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]); │ │ │ │ + } else { │ │ │ │ + this.callback("up", │ │ │ │ + [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]); │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: doneDragging │ │ │ │ - * Called when the drag handler is done dragging. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * pixel - {<OpenLayers.Pixel>} The last event pixel location. If this event │ │ │ │ - * came from a mouseout, this may not be in the map viewport. │ │ │ │ - */ │ │ │ │ - doneDragging: function(pixel) { │ │ │ │ - this.onComplete(this.feature, pixel); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: outFeature │ │ │ │ - * Called when the feature handler detects a mouse-out on a feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The feature that the mouse left. │ │ │ │ + * Method: activate │ │ │ │ */ │ │ │ │ - outFeature: function(feature) { │ │ │ │ - if (!this.handlers.drag.dragging) { │ │ │ │ - this.over = false; │ │ │ │ - this.handlers.drag.deactivate(); │ │ │ │ - OpenLayers.Element.removeClass( │ │ │ │ - this.map.viewPortDiv, this.displayClass + "Over" │ │ │ │ - ); │ │ │ │ - this.onLeave(feature); │ │ │ │ - this.feature = null; │ │ │ │ + activate: function(evt) { │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + //register mousewheel events specifically on the window and document │ │ │ │ + var wheelListener = this.wheelListener; │ │ │ │ + OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener); │ │ │ │ + OpenLayers.Event.observe(window, "mousewheel", wheelListener); │ │ │ │ + OpenLayers.Event.observe(document, "mousewheel", wheelListener); │ │ │ │ + return true; │ │ │ │ } else { │ │ │ │ - if (this.feature.id == feature.id) { │ │ │ │ - this.over = false; │ │ │ │ - } │ │ │ │ + return false; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: cancel │ │ │ │ - * Called when the drag handler detects a mouse-out (from the map viewport). │ │ │ │ - */ │ │ │ │ - cancel: function() { │ │ │ │ - this.handlers.drag.deactivate(); │ │ │ │ - this.over = false; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the control and all handlers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} The control's map. │ │ │ │ + * Method: deactivate │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - this.handlers.drag.setMap(map); │ │ │ │ - this.handlers.feature.setMap(map); │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ + deactivate: function(evt) { │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + // unregister mousewheel events specifically on the window and document │ │ │ │ + var wheelListener = this.wheelListener; │ │ │ │ + OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener); │ │ │ │ + OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener); │ │ │ │ + OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener); │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.DragFeature" │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.MouseWheel" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ OpenLayers/Handler/RegularPolygon.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ @@ -48769,333 +43021,14 @@ │ │ │ │ } │ │ │ │ return false; │ │ │ │ }, │ │ │ │ │ │ │ │ CLASS_NAME: "OpenLayers.Handler.Path" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Handler/Polygon.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Handler/Path.js │ │ │ │ - * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Handler.Polygon │ │ │ │ - * Handler to draw a polygon on the map. Polygon is displayed on mouse down, │ │ │ │ - * moves on mouse move, and is finished on mouse up. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Handler.Path> │ │ │ │ - * - <OpenLayers.Handler> │ │ │ │ - */ │ │ │ │ -OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: holeModifier │ │ │ │ - * {String} Key modifier to trigger hole digitizing. Acceptable values are │ │ │ │ - * "altKey", "shiftKey", or "ctrlKey". If not set, no hole digitizing │ │ │ │ - * will take place. Default is null. │ │ │ │ - */ │ │ │ │ - holeModifier: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: drawingHole │ │ │ │ - * {Boolean} Currently drawing an interior ring. │ │ │ │ - */ │ │ │ │ - drawingHole: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: polygon │ │ │ │ - * {<OpenLayers.Feature.Vector>} │ │ │ │ - */ │ │ │ │ - polygon: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Handler.Polygon │ │ │ │ - * Create a Polygon Handler. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} The control that owns this handler │ │ │ │ - * callbacks - {Object} An object with a properties whose values are │ │ │ │ - * functions. Various callbacks described below. │ │ │ │ - * options - {Object} An optional object with properties to be set on the │ │ │ │ - * handler │ │ │ │ - * │ │ │ │ - * Named callbacks: │ │ │ │ - * create - Called when a sketch is first created. Callback called with │ │ │ │ - * the creation point geometry and sketch feature. │ │ │ │ - * modify - Called with each move of a vertex with the vertex (point) │ │ │ │ - * geometry and the sketch feature. │ │ │ │ - * point - Called as each point is added. Receives the new point geometry. │ │ │ │ - * done - Called when the point drawing is finished. The callback will │ │ │ │ - * recieve a single argument, the polygon geometry. │ │ │ │ - * cancel - Called when the handler is deactivated while drawing. The │ │ │ │ - * cancel callback will receive a geometry. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: createFeature │ │ │ │ - * Add temporary geometries │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new │ │ │ │ - * feature. │ │ │ │ - */ │ │ │ │ - createFeature: function(pixel) { │ │ │ │ - var lonlat = this.layer.getLonLatFromViewPortPx(pixel); │ │ │ │ - var geometry = new OpenLayers.Geometry.Point( │ │ │ │ - lonlat.lon, lonlat.lat │ │ │ │ - ); │ │ │ │ - this.point = new OpenLayers.Feature.Vector(geometry); │ │ │ │ - this.line = new OpenLayers.Feature.Vector( │ │ │ │ - new OpenLayers.Geometry.LinearRing([this.point.geometry]) │ │ │ │ - ); │ │ │ │ - this.polygon = new OpenLayers.Feature.Vector( │ │ │ │ - new OpenLayers.Geometry.Polygon([this.line.geometry]) │ │ │ │ - ); │ │ │ │ - this.callback("create", [this.point.geometry, this.getSketch()]); │ │ │ │ - this.point.geometry.clearBounds(); │ │ │ │ - this.layer.addFeatures([this.polygon, this.point], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: addPoint │ │ │ │ - * Add point to geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * pixel - {<OpenLayers.Pixel>} The pixel location for the new point. │ │ │ │ - */ │ │ │ │ - addPoint: function(pixel) { │ │ │ │ - if (!this.drawingHole && this.holeModifier && │ │ │ │ - this.evt && this.evt[this.holeModifier]) { │ │ │ │ - var geometry = this.point.geometry; │ │ │ │ - var features = this.control.layer.features; │ │ │ │ - var candidate, polygon; │ │ │ │ - // look for intersections, last drawn gets priority │ │ │ │ - for (var i = features.length - 1; i >= 0; --i) { │ │ │ │ - candidate = features[i].geometry; │ │ │ │ - if ((candidate instanceof OpenLayers.Geometry.Polygon || │ │ │ │ - candidate instanceof OpenLayers.Geometry.MultiPolygon) && │ │ │ │ - candidate.intersects(geometry)) { │ │ │ │ - polygon = features[i]; │ │ │ │ - this.control.layer.removeFeatures([polygon], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.control.layer.events.registerPriority( │ │ │ │ - "sketchcomplete", this, this.finalizeInteriorRing │ │ │ │ - ); │ │ │ │ - this.control.layer.events.registerPriority( │ │ │ │ - "sketchmodified", this, this.enforceTopology │ │ │ │ - ); │ │ │ │ - polygon.geometry.addComponent(this.line.geometry); │ │ │ │ - this.polygon = polygon; │ │ │ │ - this.drawingHole = true; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getCurrentPointIndex │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Number} The index of the most recently drawn point. │ │ │ │ - */ │ │ │ │ - getCurrentPointIndex: function() { │ │ │ │ - return this.line.geometry.components.length - 2; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: enforceTopology │ │ │ │ - * Simple topology enforcement for drawing interior rings. Ensures vertices │ │ │ │ - * of interior rings are contained by exterior ring. Other topology │ │ │ │ - * rules are enforced in <finalizeInteriorRing> to allow drawing of │ │ │ │ - * rings that intersect only during the sketch (e.g. a "C" shaped ring │ │ │ │ - * that nearly encloses another ring). │ │ │ │ - */ │ │ │ │ - enforceTopology: function(event) { │ │ │ │ - var point = event.vertex; │ │ │ │ - var components = this.line.geometry.components; │ │ │ │ - // ensure that vertices of interior ring are contained by exterior ring │ │ │ │ - if (!this.polygon.geometry.intersects(point)) { │ │ │ │ - var last = components[components.length - 3]; │ │ │ │ - point.x = last.x; │ │ │ │ - point.y = last.y; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: finishGeometry │ │ │ │ - * Finish the geometry and send it back to the control. │ │ │ │ - */ │ │ │ │ - finishGeometry: function() { │ │ │ │ - var index = this.line.geometry.components.length - 2; │ │ │ │ - this.line.geometry.removeComponent(this.line.geometry.components[index]); │ │ │ │ - this.removePoint(); │ │ │ │ - this.finalize(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: finalizeInteriorRing │ │ │ │ - * Enforces that new ring has some area and doesn't contain vertices of any │ │ │ │ - * other rings. │ │ │ │ - */ │ │ │ │ - finalizeInteriorRing: function() { │ │ │ │ - var ring = this.line.geometry; │ │ │ │ - // ensure that ring has some area │ │ │ │ - var modified = (ring.getArea() !== 0); │ │ │ │ - if (modified) { │ │ │ │ - // ensure that new ring doesn't intersect any other rings │ │ │ │ - var rings = this.polygon.geometry.components; │ │ │ │ - for (var i = rings.length - 2; i >= 0; --i) { │ │ │ │ - if (ring.intersects(rings[i])) { │ │ │ │ - modified = false; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (modified) { │ │ │ │ - // ensure that new ring doesn't contain any other rings │ │ │ │ - var target; │ │ │ │ - outer: for (var i = rings.length - 2; i > 0; --i) { │ │ │ │ - var points = rings[i].components; │ │ │ │ - for (var j = 0, jj = points.length; j < jj; ++j) { │ │ │ │ - if (ring.containsPoint(points[j])) { │ │ │ │ - modified = false; │ │ │ │ - break outer; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (modified) { │ │ │ │ - if (this.polygon.state !== OpenLayers.State.INSERT) { │ │ │ │ - this.polygon.state = OpenLayers.State.UPDATE; │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.polygon.geometry.removeComponent(ring); │ │ │ │ - } │ │ │ │ - this.restoreFeature(); │ │ │ │ - return false; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: cancel │ │ │ │ - * Finish the geometry and call the "cancel" callback. │ │ │ │ - */ │ │ │ │ - cancel: function() { │ │ │ │ - if (this.drawingHole) { │ │ │ │ - this.polygon.geometry.removeComponent(this.line.geometry); │ │ │ │ - this.restoreFeature(true); │ │ │ │ - } │ │ │ │ - return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: restoreFeature │ │ │ │ - * Move the feature from the sketch layer to the target layer. │ │ │ │ - * │ │ │ │ - * Properties: │ │ │ │ - * cancel - {Boolean} Cancel drawing. If falsey, the "sketchcomplete" event │ │ │ │ - * will be fired. │ │ │ │ - */ │ │ │ │ - restoreFeature: function(cancel) { │ │ │ │ - this.control.layer.events.unregister( │ │ │ │ - "sketchcomplete", this, this.finalizeInteriorRing │ │ │ │ - ); │ │ │ │ - this.control.layer.events.unregister( │ │ │ │ - "sketchmodified", this, this.enforceTopology │ │ │ │ - ); │ │ │ │ - this.layer.removeFeatures([this.polygon], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.control.layer.addFeatures([this.polygon], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.drawingHole = false; │ │ │ │ - if (!cancel) { │ │ │ │ - // Re-trigger "sketchcomplete" so other listeners can do their │ │ │ │ - // business. While this is somewhat sloppy (if a listener is │ │ │ │ - // registered with registerPriority - not common - between the start │ │ │ │ - // and end of a single ring drawing - very uncommon - it will be │ │ │ │ - // called twice). │ │ │ │ - // TODO: In 3.0, collapse sketch handlers into geometry specific │ │ │ │ - // drawing controls. │ │ │ │ - this.control.layer.events.triggerEvent( │ │ │ │ - "sketchcomplete", { │ │ │ │ - feature: this.polygon │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroyFeature │ │ │ │ - * Destroy temporary geometries │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * force - {Boolean} Destroy even if persist is true. │ │ │ │ - */ │ │ │ │ - destroyFeature: function(force) { │ │ │ │ - OpenLayers.Handler.Path.prototype.destroyFeature.call( │ │ │ │ - this, force); │ │ │ │ - this.polygon = null; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: drawFeature │ │ │ │ - * Render geometries on the temporary layer. │ │ │ │ - */ │ │ │ │ - drawFeature: function() { │ │ │ │ - this.layer.drawFeature(this.polygon, this.style); │ │ │ │ - this.layer.drawFeature(this.point, this.style); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getSketch │ │ │ │ - * Return the sketch feature. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} │ │ │ │ - */ │ │ │ │ - getSketch: function() { │ │ │ │ - return this.polygon; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getGeometry │ │ │ │ - * Return the sketch geometry. If <multi> is true, this will return │ │ │ │ - * a multi-part geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry.Polygon>} │ │ │ │ - */ │ │ │ │ - getGeometry: function() { │ │ │ │ - var geometry = this.polygon && this.polygon.geometry; │ │ │ │ - if (geometry && this.multi) { │ │ │ │ - geometry = new OpenLayers.Geometry.MultiPolygon([geometry]); │ │ │ │ - } │ │ │ │ - return geometry; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Polygon" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ OpenLayers/Handler/Click.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ @@ -49597,36996 +43530,43063 @@ │ │ │ │ } │ │ │ │ return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ CLASS_NAME: "OpenLayers.Handler.Click" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/SLD.js │ │ │ │ + OpenLayers/Handler/Keyboard.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ - * @requires OpenLayers/Style.js │ │ │ │ - * @requires OpenLayers/Rule.js │ │ │ │ - * @requires OpenLayers/Filter/FeatureId.js │ │ │ │ - * @requires OpenLayers/Filter/Logical.js │ │ │ │ - * @requires OpenLayers/Filter/Comparison.js │ │ │ │ - * @requires OpenLayers/Filter/Spatial.js │ │ │ │ + * @requires OpenLayers/Handler.js │ │ │ │ + * @requires OpenLayers/Events.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.SLD │ │ │ │ - * Read/Write SLD. Create a new instance with the <OpenLayers.Format.SLD> │ │ │ │ - * constructor. │ │ │ │ + * Class: OpenLayers.handler.Keyboard │ │ │ │ + * A handler for keyboard events. Create a new instance with the │ │ │ │ + * <OpenLayers.Handler.Keyboard> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ + * - <OpenLayers.Handler> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ +OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + │ │ │ │ + /* http://www.quirksmode.org/js/keys.html explains key x-browser │ │ │ │ + key handling quirks in pretty nice detail */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: KEY_EVENTS │ │ │ │ + * keydown, keypress, keyup │ │ │ │ + */ │ │ │ │ + KEY_EVENTS: ["keydown", "keyup"], │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: eventListener │ │ │ │ + * {Function} │ │ │ │ + */ │ │ │ │ + eventListener: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: profile │ │ │ │ - * {String} If provided, use a custom profile. │ │ │ │ - * │ │ │ │ - * Currently supported profiles: │ │ │ │ - * - GeoServer - parses GeoServer vendor specific capabilities for SLD. │ │ │ │ + * Property: observeElement │ │ │ │ + * {DOMElement|String} The DOM element on which we listen for │ │ │ │ + * key events. Default to the document. │ │ │ │ */ │ │ │ │ - profile: null, │ │ │ │ + observeElement: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ - * {String} Version number to assume if none found. Default is "1.0.0". │ │ │ │ + * Constructor: OpenLayers.Handler.Keyboard │ │ │ │ + * Returns a new keyboard handler. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * control - {<OpenLayers.Control>} The control that is making use of │ │ │ │ + * this handler. If a handler is being used without a control, the │ │ │ │ + * handlers setMap method must be overridden to deal properly with │ │ │ │ + * the map. │ │ │ │ + * callbacks - {Object} An object containing a single function to be │ │ │ │ + * called when the drag operation is finished. The callback should │ │ │ │ + * expect to recieve a single argument, the pixel location of the event. │ │ │ │ + * Callbacks for 'keydown', 'keypress', and 'keyup' are supported. │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * handler. │ │ │ │ */ │ │ │ │ - defaultVersion: "1.0.0", │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ + // cache the bound event listener method so it can be unobserved later │ │ │ │ + this.eventListener = OpenLayers.Function.bindAsEventListener( │ │ │ │ + this.handleKeyEvent, this │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: stringifyOutput │ │ │ │ - * {Boolean} If true, write will return a string otherwise a DOMElement. │ │ │ │ - * Default is true. │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - stringifyOutput: true, │ │ │ │ + destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ + this.eventListener = null; │ │ │ │ + OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: namedLayersAsArray │ │ │ │ - * {Boolean} Generate a namedLayers array. If false, the namedLayers │ │ │ │ - * property value will be an object keyed by layer name. Default is │ │ │ │ - * false. │ │ │ │ + * Method: activate │ │ │ │ */ │ │ │ │ - namedLayersAsArray: false, │ │ │ │ + activate: function() { │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.observeElement = this.observeElement || document; │ │ │ │ + for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) { │ │ │ │ + OpenLayers.Event.observe( │ │ │ │ + this.observeElement, this.KEY_EVENTS[i], this.eventListener); │ │ │ │ + } │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Write a SLD document given a list of styles. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * sld - {Object} An object representing the SLD. │ │ │ │ - * options - {Object} Optional configuration object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} An SLD document string. │ │ │ │ + * Method: deactivate │ │ │ │ */ │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) { │ │ │ │ + OpenLayers.Event.stopObserving( │ │ │ │ + this.observeElement, this.KEY_EVENTS[i], this.eventListener); │ │ │ │ + } │ │ │ │ + deactivated = true; │ │ │ │ + } │ │ │ │ + return deactivated; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read and SLD doc and return an object representing the SLD. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String | DOMElement} Data to read. │ │ │ │ - * options - {Object} Options for the reader. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object representing the SLD. │ │ │ │ + * Method: handleKeyEvent │ │ │ │ */ │ │ │ │ + handleKeyEvent: function(evt) { │ │ │ │ + if (this.checkModifiers(evt)) { │ │ │ │ + this.callback(evt.type, [evt]); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.SLD" │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Keyboard" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/GML/v2.js │ │ │ │ + OpenLayers/Handler/Polygon.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/GML/Base.js │ │ │ │ + * @requires OpenLayers/Handler/Path.js │ │ │ │ + * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.GML.v2 │ │ │ │ - * Parses GML version 2. │ │ │ │ + * Class: OpenLayers.Handler.Polygon │ │ │ │ + * Handler to draw a polygon on the map. Polygon is displayed on mouse down, │ │ │ │ + * moves on mouse move, and is finished on mouse up. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.GML.Base> │ │ │ │ + * - <OpenLayers.Handler.Path> │ │ │ │ + * - <OpenLayers.Handler> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, { │ │ │ │ +OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: holeModifier │ │ │ │ + * {String} Key modifier to trigger hole digitizing. Acceptable values are │ │ │ │ + * "altKey", "shiftKey", or "ctrlKey". If not set, no hole digitizing │ │ │ │ + * will take place. Default is null. │ │ │ │ + */ │ │ │ │ + holeModifier: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} Schema location for a particular minor version. │ │ │ │ + * Property: drawingHole │ │ │ │ + * {Boolean} Currently drawing an interior ring. │ │ │ │ */ │ │ │ │ - schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd", │ │ │ │ + drawingHole: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.GML.v2 │ │ │ │ - * Create a parser for GML v2. │ │ │ │ + * Property: polygon │ │ │ │ + * {<OpenLayers.Feature.Vector>} │ │ │ │ + */ │ │ │ │ + polygon: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Handler.Polygon │ │ │ │ + * Create a Polygon Handler. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * control - {<OpenLayers.Control>} The control that owns this handler │ │ │ │ + * callbacks - {Object} An object with a properties whose values are │ │ │ │ + * functions. Various callbacks described below. │ │ │ │ + * options - {Object} An optional object with properties to be set on the │ │ │ │ + * handler │ │ │ │ * │ │ │ │ - * Valid options properties: │ │ │ │ - * featureType - {String} Local (without prefix) feature typeName (required). │ │ │ │ - * featureNS - {String} Feature namespace (required). │ │ │ │ - * geometryName - {String} Geometry element name. │ │ │ │ + * Named callbacks: │ │ │ │ + * create - Called when a sketch is first created. Callback called with │ │ │ │ + * the creation point geometry and sketch feature. │ │ │ │ + * modify - Called with each move of a vertex with the vertex (point) │ │ │ │ + * geometry and the sketch feature. │ │ │ │ + * point - Called as each point is added. Receives the new point geometry. │ │ │ │ + * done - Called when the point drawing is finished. The callback will │ │ │ │ + * recieve a single argument, the polygon geometry. │ │ │ │ + * cancel - Called when the handler is deactivated while drawing. The │ │ │ │ + * cancel callback will receive a geometry. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]); │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: createFeature │ │ │ │ + * Add temporary geometries │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * pixel - {<OpenLayers.Pixel>} The initial pixel location for the new │ │ │ │ + * feature. │ │ │ │ + */ │ │ │ │ + createFeature: function(pixel) { │ │ │ │ + var lonlat = this.layer.getLonLatFromViewPortPx(pixel); │ │ │ │ + var geometry = new OpenLayers.Geometry.Point( │ │ │ │ + lonlat.lon, lonlat.lat │ │ │ │ + ); │ │ │ │ + this.point = new OpenLayers.Feature.Vector(geometry); │ │ │ │ + this.line = new OpenLayers.Feature.Vector( │ │ │ │ + new OpenLayers.Geometry.LinearRing([this.point.geometry]) │ │ │ │ + ); │ │ │ │ + this.polygon = new OpenLayers.Feature.Vector( │ │ │ │ + new OpenLayers.Geometry.Polygon([this.line.geometry]) │ │ │ │ + ); │ │ │ │ + this.callback("create", [this.point.geometry, this.getSketch()]); │ │ │ │ + this.point.geometry.clearBounds(); │ │ │ │ + this.layer.addFeatures([this.polygon, this.point], { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: addPoint │ │ │ │ + * Add point to geometry. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * pixel - {<OpenLayers.Pixel>} The pixel location for the new point. │ │ │ │ */ │ │ │ │ - readers: { │ │ │ │ - "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ - "outerBoundaryIs": function(node, container) { │ │ │ │ - var obj = {}; │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - container.outer = obj.components[0]; │ │ │ │ - }, │ │ │ │ - "innerBoundaryIs": function(node, container) { │ │ │ │ - var obj = {}; │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - container.inner.push(obj.components[0]); │ │ │ │ - }, │ │ │ │ - "Box": function(node, container) { │ │ │ │ - var obj = {}; │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - if (!container.components) { │ │ │ │ - container.components = []; │ │ │ │ + addPoint: function(pixel) { │ │ │ │ + if (!this.drawingHole && this.holeModifier && │ │ │ │ + this.evt && this.evt[this.holeModifier]) { │ │ │ │ + var geometry = this.point.geometry; │ │ │ │ + var features = this.control.layer.features; │ │ │ │ + var candidate, polygon; │ │ │ │ + // look for intersections, last drawn gets priority │ │ │ │ + for (var i = features.length - 1; i >= 0; --i) { │ │ │ │ + candidate = features[i].geometry; │ │ │ │ + if ((candidate instanceof OpenLayers.Geometry.Polygon || │ │ │ │ + candidate instanceof OpenLayers.Geometry.MultiPolygon) && │ │ │ │ + candidate.intersects(geometry)) { │ │ │ │ + polygon = features[i]; │ │ │ │ + this.control.layer.removeFeatures([polygon], { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.control.layer.events.registerPriority( │ │ │ │ + "sketchcomplete", this, this.finalizeInteriorRing │ │ │ │ + ); │ │ │ │ + this.control.layer.events.registerPriority( │ │ │ │ + "sketchmodified", this, this.enforceTopology │ │ │ │ + ); │ │ │ │ + polygon.geometry.addComponent(this.line.geometry); │ │ │ │ + this.polygon = polygon; │ │ │ │ + this.drawingHole = true; │ │ │ │ + break; │ │ │ │ } │ │ │ │ - var min = obj.points[0]; │ │ │ │ - var max = obj.points[1]; │ │ │ │ - container.components.push( │ │ │ │ - new OpenLayers.Bounds(min.x, min.y, max.x, max.y) │ │ │ │ - ); │ │ │ │ } │ │ │ │ - }, OpenLayers.Format.GML.Base.prototype.readers["gml"]), │ │ │ │ - "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"], │ │ │ │ - "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"] │ │ │ │ + } │ │ │ │ + OpenLayers.Handler.Path.prototype.addPoint.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector} │ │ │ │ - * An array of features or a single feature. │ │ │ │ - * │ │ │ │ + * Method: getCurrentPointIndex │ │ │ │ + * │ │ │ │ * 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. │ │ │ │ + * {Number} The index of the most recently drawn point. │ │ │ │ */ │ │ │ │ - write: function(features) { │ │ │ │ - var name; │ │ │ │ - if (OpenLayers.Util.isArray(features)) { │ │ │ │ - // GML2 only has abstract feature collections │ │ │ │ - // wfs provides a feature collection from a well-known schema │ │ │ │ - name = "wfs:FeatureCollection"; │ │ │ │ - } else { │ │ │ │ - name = "gml:featureMember"; │ │ │ │ - } │ │ │ │ - var root = this.writeNode(name, features); │ │ │ │ - this.setAttributeNS( │ │ │ │ - root, this.namespaces["xsi"], │ │ │ │ - "xsi:schemaLocation", this.schemaLocation │ │ │ │ - ); │ │ │ │ + getCurrentPointIndex: function() { │ │ │ │ + return this.line.geometry.components.length - 2; │ │ │ │ + }, │ │ │ │ │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [root]); │ │ │ │ + /** │ │ │ │ + * Method: enforceTopology │ │ │ │ + * Simple topology enforcement for drawing interior rings. Ensures vertices │ │ │ │ + * of interior rings are contained by exterior ring. Other topology │ │ │ │ + * rules are enforced in <finalizeInteriorRing> to allow drawing of │ │ │ │ + * rings that intersect only during the sketch (e.g. a "C" shaped ring │ │ │ │ + * that nearly encloses another ring). │ │ │ │ + */ │ │ │ │ + enforceTopology: function(event) { │ │ │ │ + var point = event.vertex; │ │ │ │ + var components = this.line.geometry.components; │ │ │ │ + // ensure that vertices of interior ring are contained by exterior ring │ │ │ │ + if (!this.polygon.geometry.intersects(point)) { │ │ │ │ + var last = components[components.length - 3]; │ │ │ │ + point.x = last.x; │ │ │ │ + point.y = last.y; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: finishGeometry │ │ │ │ + * Finish the geometry and send it back to the control. │ │ │ │ */ │ │ │ │ - writers: { │ │ │ │ - "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ - "Point": function(geometry) { │ │ │ │ - var node = this.createElementNSPlus("gml:Point"); │ │ │ │ - this.writeNode("coordinates", [geometry], node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "coordinates": function(points) { │ │ │ │ - var numPoints = points.length; │ │ │ │ - var parts = new Array(numPoints); │ │ │ │ - var point; │ │ │ │ - for (var i = 0; i < numPoints; ++i) { │ │ │ │ - point = points[i]; │ │ │ │ - if (this.xy) { │ │ │ │ - parts[i] = point.x + "," + point.y; │ │ │ │ - } else { │ │ │ │ - parts[i] = point.y + "," + point.x; │ │ │ │ - } │ │ │ │ - if (point.z != undefined) { // allow null or undefined │ │ │ │ - parts[i] += "," + point.z; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return this.createElementNSPlus("gml:coordinates", { │ │ │ │ - attributes: { │ │ │ │ - decimal: ".", │ │ │ │ - cs: ",", │ │ │ │ - ts: " " │ │ │ │ - }, │ │ │ │ - value: (numPoints == 1) ? parts[0] : parts.join(" ") │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "LineString": function(geometry) { │ │ │ │ - var node = this.createElementNSPlus("gml:LineString"); │ │ │ │ - this.writeNode("coordinates", geometry.components, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Polygon": function(geometry) { │ │ │ │ - var node = this.createElementNSPlus("gml:Polygon"); │ │ │ │ - this.writeNode("outerBoundaryIs", geometry.components[0], node); │ │ │ │ - for (var i = 1; i < geometry.components.length; ++i) { │ │ │ │ - this.writeNode( │ │ │ │ - "innerBoundaryIs", geometry.components[i], node │ │ │ │ - ); │ │ │ │ + finishGeometry: function() { │ │ │ │ + var index = this.line.geometry.components.length - 2; │ │ │ │ + this.line.geometry.removeComponent(this.line.geometry.components[index]); │ │ │ │ + this.removePoint(); │ │ │ │ + this.finalize(); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: finalizeInteriorRing │ │ │ │ + * Enforces that new ring has some area and doesn't contain vertices of any │ │ │ │ + * other rings. │ │ │ │ + */ │ │ │ │ + finalizeInteriorRing: function() { │ │ │ │ + var ring = this.line.geometry; │ │ │ │ + // ensure that ring has some area │ │ │ │ + var modified = (ring.getArea() !== 0); │ │ │ │ + if (modified) { │ │ │ │ + // ensure that new ring doesn't intersect any other rings │ │ │ │ + var rings = this.polygon.geometry.components; │ │ │ │ + for (var i = rings.length - 2; i >= 0; --i) { │ │ │ │ + if (ring.intersects(rings[i])) { │ │ │ │ + modified = false; │ │ │ │ + break; │ │ │ │ } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "outerBoundaryIs": function(ring) { │ │ │ │ - var node = this.createElementNSPlus("gml:outerBoundaryIs"); │ │ │ │ - this.writeNode("LinearRing", ring, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "innerBoundaryIs": function(ring) { │ │ │ │ - var node = this.createElementNSPlus("gml:innerBoundaryIs"); │ │ │ │ - this.writeNode("LinearRing", ring, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "LinearRing": function(ring) { │ │ │ │ - var node = this.createElementNSPlus("gml:LinearRing"); │ │ │ │ - this.writeNode("coordinates", ring.components, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Box": function(bounds) { │ │ │ │ - var node = this.createElementNSPlus("gml:Box"); │ │ │ │ - this.writeNode("coordinates", [{ │ │ │ │ - x: bounds.left, │ │ │ │ - y: bounds.bottom │ │ │ │ - }, { │ │ │ │ - x: bounds.right, │ │ │ │ - y: bounds.top │ │ │ │ - }], node); │ │ │ │ - // srsName attribute is optional for gml:Box │ │ │ │ - if (this.srsName) { │ │ │ │ - node.setAttribute("srsName", this.srsName); │ │ │ │ + } │ │ │ │ + if (modified) { │ │ │ │ + // ensure that new ring doesn't contain any other rings │ │ │ │ + var target; │ │ │ │ + outer: for (var i = rings.length - 2; i > 0; --i) { │ │ │ │ + var points = rings[i].components; │ │ │ │ + for (var j = 0, jj = points.length; j < jj; ++j) { │ │ │ │ + if (ring.containsPoint(points[j])) { │ │ │ │ + modified = false; │ │ │ │ + break outer; │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - return node; │ │ │ │ } │ │ │ │ - }, OpenLayers.Format.GML.Base.prototype.writers["gml"]), │ │ │ │ - "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"], │ │ │ │ - "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"] │ │ │ │ + } │ │ │ │ + if (modified) { │ │ │ │ + if (this.polygon.state !== OpenLayers.State.INSERT) { │ │ │ │ + this.polygon.state = OpenLayers.State.UPDATE; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.polygon.geometry.removeComponent(ring); │ │ │ │ + } │ │ │ │ + this.restoreFeature(); │ │ │ │ + return false; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.GML.v2" │ │ │ │ - │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/Filter/v1_0_0.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/GML/v2.js │ │ │ │ - * @requires OpenLayers/Format/Filter/v1.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.Filter.v1_0_0 │ │ │ │ - * Write ogc:Filter version 1.0.0. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.GML.v2> │ │ │ │ - * - <OpenLayers.Format.Filter.v1> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.GML.v2, OpenLayers.Format.Filter.v1, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: VERSION │ │ │ │ - * {String} 1.0.0 │ │ │ │ - */ │ │ │ │ - VERSION: "1.0.0", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd │ │ │ │ - */ │ │ │ │ - schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.Filter.v1_0_0 │ │ │ │ - * Instances of this class are not created directly. Use the │ │ │ │ - * <OpenLayers.Format.Filter> constructor instead. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.GML.v2.prototype.initialize.apply( │ │ │ │ - this, [options] │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIMethod: cancel │ │ │ │ + * Finish the geometry and call the "cancel" callback. │ │ │ │ + */ │ │ │ │ + cancel: function() { │ │ │ │ + if (this.drawingHole) { │ │ │ │ + this.polygon.geometry.removeComponent(this.line.geometry); │ │ │ │ + this.restoreFeature(true); │ │ │ │ + } │ │ │ │ + return OpenLayers.Handler.Path.prototype.cancel.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "ogc": OpenLayers.Util.applyDefaults({ │ │ │ │ - "PropertyIsEqualTo": function(node, obj) { │ │ │ │ - var filter = new OpenLayers.Filter.Comparison({ │ │ │ │ - type: OpenLayers.Filter.Comparison.EQUAL_TO │ │ │ │ - }); │ │ │ │ - this.readChildNodes(node, filter); │ │ │ │ - obj.filters.push(filter); │ │ │ │ - }, │ │ │ │ - "PropertyIsNotEqualTo": function(node, obj) { │ │ │ │ - var filter = new OpenLayers.Filter.Comparison({ │ │ │ │ - type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO │ │ │ │ - }); │ │ │ │ - this.readChildNodes(node, filter); │ │ │ │ - obj.filters.push(filter); │ │ │ │ - }, │ │ │ │ - "PropertyIsLike": function(node, obj) { │ │ │ │ - var filter = new OpenLayers.Filter.Comparison({ │ │ │ │ - type: OpenLayers.Filter.Comparison.LIKE │ │ │ │ - }); │ │ │ │ - this.readChildNodes(node, filter); │ │ │ │ - var wildCard = node.getAttribute("wildCard"); │ │ │ │ - var singleChar = node.getAttribute("singleChar"); │ │ │ │ - var esc = node.getAttribute("escape"); │ │ │ │ - filter.value2regex(wildCard, singleChar, esc); │ │ │ │ - obj.filters.push(filter); │ │ │ │ + /** │ │ │ │ + * Method: restoreFeature │ │ │ │ + * Move the feature from the sketch layer to the target layer. │ │ │ │ + * │ │ │ │ + * Properties: │ │ │ │ + * cancel - {Boolean} Cancel drawing. If falsey, the "sketchcomplete" event │ │ │ │ + * will be fired. │ │ │ │ + */ │ │ │ │ + restoreFeature: function(cancel) { │ │ │ │ + this.control.layer.events.unregister( │ │ │ │ + "sketchcomplete", this, this.finalizeInteriorRing │ │ │ │ + ); │ │ │ │ + this.control.layer.events.unregister( │ │ │ │ + "sketchmodified", this, this.enforceTopology │ │ │ │ + ); │ │ │ │ + this.layer.removeFeatures([this.polygon], { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.control.layer.addFeatures([this.polygon], { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.drawingHole = false; │ │ │ │ + if (!cancel) { │ │ │ │ + // Re-trigger "sketchcomplete" so other listeners can do their │ │ │ │ + // business. While this is somewhat sloppy (if a listener is │ │ │ │ + // registered with registerPriority - not common - between the start │ │ │ │ + // and end of a single ring drawing - very uncommon - it will be │ │ │ │ + // called twice). │ │ │ │ + // TODO: In 3.0, collapse sketch handlers into geometry specific │ │ │ │ + // drawing controls. │ │ │ │ + this.control.layer.events.triggerEvent( │ │ │ │ + "sketchcomplete", { │ │ │ │ + feature: this.polygon │ │ │ │ } │ │ │ │ - }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]), │ │ │ │ - "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"], │ │ │ │ - "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"] │ │ │ │ - }, │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: writers │ │ │ │ - * As a compliment to the readers property, this structure contains public │ │ │ │ - * writing functions grouped by namespace alias and named like the │ │ │ │ - * node names they produce. │ │ │ │ - */ │ │ │ │ - writers: { │ │ │ │ - "ogc": OpenLayers.Util.applyDefaults({ │ │ │ │ - "PropertyIsEqualTo": function(filter) { │ │ │ │ - var node = this.createElementNSPlus("ogc:PropertyIsEqualTo"); │ │ │ │ - // no ogc:expression handling for PropertyName for now │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ - // handle Literals or Functions for now │ │ │ │ - this.writeOgcExpression(filter.value, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "PropertyIsNotEqualTo": function(filter) { │ │ │ │ - var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo"); │ │ │ │ - // no ogc:expression handling for PropertyName for now │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ - // handle Literals or Functions for now │ │ │ │ - this.writeOgcExpression(filter.value, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "PropertyIsLike": function(filter) { │ │ │ │ - var node = this.createElementNSPlus("ogc:PropertyIsLike", { │ │ │ │ - attributes: { │ │ │ │ - wildCard: "*", │ │ │ │ - singleChar: ".", │ │ │ │ - escape: "!" │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - // no ogc:expression handling for now │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ - // convert regex string to ogc string │ │ │ │ - this.writeNode("Literal", filter.regex2value(), node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "BBOX": function(filter) { │ │ │ │ - var node = this.createElementNSPlus("ogc:BBOX"); │ │ │ │ - // PropertyName is mandatory in 1.0.0, but e.g. GeoServer also │ │ │ │ - // accepts filters without it. When this is used with │ │ │ │ - // OpenLayers.Protocol.WFS, OpenLayers.Format.WFST will set a │ │ │ │ - // missing filter.property to the geometryName that is │ │ │ │ - // configured with the protocol, which defaults to "the_geom". │ │ │ │ - // So the only way to omit this mandatory property is to not │ │ │ │ - // set the property on the filter and to set the geometryName │ │ │ │ - // on the WFS protocol to null. The latter also happens when │ │ │ │ - // the protocol is configured without a geometryName and a │ │ │ │ - // featureNS. │ │ │ │ - filter.property && this.writeNode("PropertyName", filter, node); │ │ │ │ - var box = this.writeNode("gml:Box", filter.value, node); │ │ │ │ - if (filter.projection) { │ │ │ │ - box.setAttribute("srsName", filter.projection); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]), │ │ │ │ - "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"], │ │ │ │ - "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"] │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: destroyFeature │ │ │ │ + * Destroy temporary geometries │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * force - {Boolean} Destroy even if persist is true. │ │ │ │ + */ │ │ │ │ + destroyFeature: function(force) { │ │ │ │ + OpenLayers.Handler.Path.prototype.destroyFeature.call( │ │ │ │ + this, force); │ │ │ │ + this.polygon = null; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: writeSpatial │ │ │ │ - * │ │ │ │ - * Read a {<OpenLayers.Filter.Spatial>} filter and converts it into XML. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * filter - {<OpenLayers.Filter.Spatial>} The filter. │ │ │ │ - * name - {String} Name of the generated XML element. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} The created XML element. │ │ │ │ - */ │ │ │ │ - writeSpatial: function(filter, name) { │ │ │ │ - var node = this.createElementNSPlus("ogc:" + name); │ │ │ │ - this.writeNode("PropertyName", filter, node); │ │ │ │ - if (filter.value instanceof OpenLayers.Filter.Function) { │ │ │ │ - this.writeNode("Function", filter.value, node); │ │ │ │ - } else { │ │ │ │ - var child; │ │ │ │ - if (filter.value instanceof OpenLayers.Geometry) { │ │ │ │ - child = this.writeNode("feature:_geometry", filter.value).firstChild; │ │ │ │ - } else { │ │ │ │ - child = this.writeNode("gml:Box", filter.value); │ │ │ │ - } │ │ │ │ - if (filter.projection) { │ │ │ │ - child.setAttribute("srsName", filter.projection); │ │ │ │ - } │ │ │ │ - node.appendChild(child); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: drawFeature │ │ │ │ + * Render geometries on the temporary layer. │ │ │ │ + */ │ │ │ │ + drawFeature: function() { │ │ │ │ + this.layer.drawFeature(this.polygon, this.style); │ │ │ │ + this.layer.drawFeature(this.point, this.style); │ │ │ │ + }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Method: getSketch │ │ │ │ + * Return the sketch feature. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Feature.Vector>} │ │ │ │ + */ │ │ │ │ + getSketch: function() { │ │ │ │ + return this.polygon; │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0" │ │ │ │ + /** │ │ │ │ + * Method: getGeometry │ │ │ │ + * Return the sketch geometry. If <multi> is true, this will return │ │ │ │ + * a multi-part geometry. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry.Polygon>} │ │ │ │ + */ │ │ │ │ + getGeometry: function() { │ │ │ │ + var geometry = this.polygon && this.polygon.geometry; │ │ │ │ + if (geometry && this.multi) { │ │ │ │ + geometry = new OpenLayers.Geometry.MultiPolygon([geometry]); │ │ │ │ + } │ │ │ │ + return geometry; │ │ │ │ + }, │ │ │ │ │ │ │ │ - }); │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Polygon" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/SLD/v1.js │ │ │ │ + OpenLayers/Handler/Pinch.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Rule.js │ │ │ │ - * @requires OpenLayers/Format/SLD.js │ │ │ │ - * @requires OpenLayers/Format/Filter/v1_0_0.js │ │ │ │ - * @requires OpenLayers/Symbolizer/Point.js │ │ │ │ - * @requires OpenLayers/Symbolizer/Line.js │ │ │ │ - * @requires OpenLayers/Symbolizer/Polygon.js │ │ │ │ - * @requires OpenLayers/Symbolizer/Text.js │ │ │ │ - * @requires OpenLayers/Symbolizer/Raster.js │ │ │ │ + * @requires OpenLayers/Handler.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.SLD.v1 │ │ │ │ - * Superclass for SLD version 1 parsers. │ │ │ │ + * Class: OpenLayers.Handler.Pinch │ │ │ │ + * The pinch handler is used to deal with sequences of browser events related │ │ │ │ + * to pinch gestures. The handler is used by controls that want to know │ │ │ │ + * when a pinch sequence begins, when a pinch is happening, and when it has │ │ │ │ + * finished. │ │ │ │ + * │ │ │ │ + * Controls that use the pinch handler typically construct it with callbacks │ │ │ │ + * for 'start', 'move', and 'done'. Callbacks for these keys are │ │ │ │ + * called when the pinch begins, with each change, and when the pinch is │ │ │ │ + * done. │ │ │ │ + * │ │ │ │ + * Create a new pinch handler with the <OpenLayers.Handler.Pinch> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.Filter.v1_0_0> │ │ │ │ + * - <OpenLayers.Handler> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, { │ │ │ │ +OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + * Property: started │ │ │ │ + * {Boolean} When a touchstart event is received, we want to record it, │ │ │ │ + * but not set 'pinching' until the touchmove get started after │ │ │ │ + * starting. │ │ │ │ */ │ │ │ │ - namespaces: { │ │ │ │ - sld: "http://www.opengis.net/sld", │ │ │ │ - ogc: "http://www.opengis.net/ogc", │ │ │ │ - gml: "http://www.opengis.net/gml", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ - }, │ │ │ │ + started: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ + * Property: stopDown │ │ │ │ + * {Boolean} Stop propagation of touchstart events from getting to │ │ │ │ + * listeners on the same element. Default is false. │ │ │ │ */ │ │ │ │ - defaultPrefix: "sld", │ │ │ │ + stopDown: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} Schema location for a particular minor version. │ │ │ │ - */ │ │ │ │ - schemaLocation: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: multipleSymbolizers │ │ │ │ - * {Boolean} Support multiple symbolizers per rule. Default is false. if │ │ │ │ - * true, an OpenLayers.Style2 instance will be created to represent │ │ │ │ - * user styles instead of an OpenLayers.Style instace. The │ │ │ │ - * OpenLayers.Style2 class allows collections of rules with multiple │ │ │ │ - * symbolizers, but is not currently useful for client side rendering. │ │ │ │ - * If multiple symbolizers is true, multiple FeatureTypeStyle elements │ │ │ │ - * are preserved in reading/writing by setting symbolizer zIndex values. │ │ │ │ - * In addition, the <defaultSymbolizer> property is ignored if │ │ │ │ - * multiple symbolizers are supported (defaults should be applied │ │ │ │ - * when rendering). │ │ │ │ + * Property: pinching │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - multipleSymbolizers: false, │ │ │ │ + pinching: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: featureTypeCounter │ │ │ │ - * {Number} Private counter for multiple feature type styles. │ │ │ │ + * Property: last │ │ │ │ + * {Object} Object that store informations related to pinch last touch. │ │ │ │ */ │ │ │ │ - featureTypeCounter: null, │ │ │ │ + last: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: defaultSymbolizer. │ │ │ │ - * {Object} A symbolizer with the SLD defaults. │ │ │ │ + * Property: start │ │ │ │ + * {Object} Object that store informations related to pinch touchstart. │ │ │ │ */ │ │ │ │ - defaultSymbolizer: { │ │ │ │ - fillColor: "#808080", │ │ │ │ - fillOpacity: 1, │ │ │ │ - strokeColor: "#000000", │ │ │ │ - strokeOpacity: 1, │ │ │ │ - strokeWidth: 1, │ │ │ │ - strokeDashstyle: "solid", │ │ │ │ - pointRadius: 3, │ │ │ │ - graphicName: "square" │ │ │ │ - }, │ │ │ │ + start: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.SLD.v1 │ │ │ │ - * Instances of this class are not created directly. Use the │ │ │ │ - * <OpenLayers.Format.SLD> constructor instead. │ │ │ │ + * Constructor: OpenLayers.Handler.Pinch │ │ │ │ + * Returns OpenLayers.Handler.Pinch │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * control - {<OpenLayers.Control>} The control that is making use of │ │ │ │ + * this handler. If a handler is being used without a control, the │ │ │ │ + * handlers setMap method must be overridden to deal properly with │ │ │ │ + * the map. │ │ │ │ + * callbacks - {Object} An object containing functions to be called when │ │ │ │ + * the pinch operation start, change, or is finished. The callbacks │ │ │ │ + * should expect to receive an object argument, which contains │ │ │ │ + * information about scale, distance, and position of touch points. │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read │ │ │ │ + * Method: touchstart │ │ │ │ + * Handle touchstart events │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * data - {DOMElement} An SLD document element. │ │ │ │ - * options - {Object} Options for the reader. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * namedLayersAsArray - {Boolean} Generate a namedLayers array. If false, │ │ │ │ - * the namedLayers property value will be an object keyed by layer name. │ │ │ │ - * Default is false. │ │ │ │ + * evt - {Event} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} An object representing the SLD. │ │ │ │ - */ │ │ │ │ - read: function(data, options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - var sld = { │ │ │ │ - namedLayers: options.namedLayersAsArray === true ? [] : {} │ │ │ │ - }; │ │ │ │ - this.readChildNodes(data, sld); │ │ │ │ - return sld; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ */ │ │ │ │ - readers: OpenLayers.Util.applyDefaults({ │ │ │ │ - "sld": { │ │ │ │ - "StyledLayerDescriptor": function(node, sld) { │ │ │ │ - sld.version = node.getAttribute("version"); │ │ │ │ - this.readChildNodes(node, sld); │ │ │ │ - }, │ │ │ │ - "Name": function(node, obj) { │ │ │ │ - obj.name = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Title": function(node, obj) { │ │ │ │ - obj.title = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Abstract": function(node, obj) { │ │ │ │ - obj.description = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "NamedLayer": function(node, sld) { │ │ │ │ - var layer = { │ │ │ │ - userStyles: [], │ │ │ │ - namedStyles: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, layer); │ │ │ │ - // give each of the user styles this layer name │ │ │ │ - for (var i = 0, len = layer.userStyles.length; i < len; ++i) { │ │ │ │ - layer.userStyles[i].layerName = layer.name; │ │ │ │ - } │ │ │ │ - if (OpenLayers.Util.isArray(sld.namedLayers)) { │ │ │ │ - sld.namedLayers.push(layer); │ │ │ │ - } else { │ │ │ │ - sld.namedLayers[layer.name] = layer; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "NamedStyle": function(node, layer) { │ │ │ │ - layer.namedStyles.push( │ │ │ │ - this.getChildName(node.firstChild) │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - "UserStyle": function(node, layer) { │ │ │ │ - var obj = { │ │ │ │ - defaultsPerSymbolizer: true, │ │ │ │ - rules: [] │ │ │ │ - }; │ │ │ │ - this.featureTypeCounter = -1; │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - var style; │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - delete obj.defaultsPerSymbolizer; │ │ │ │ - style = new OpenLayers.Style2(obj); │ │ │ │ - } else { │ │ │ │ - style = new OpenLayers.Style(this.defaultSymbolizer, obj); │ │ │ │ - } │ │ │ │ - layer.userStyles.push(style); │ │ │ │ - }, │ │ │ │ - "IsDefault": function(node, style) { │ │ │ │ - if (this.getChildValue(node) == "1") { │ │ │ │ - style.isDefault = true; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "FeatureTypeStyle": function(node, style) { │ │ │ │ - ++this.featureTypeCounter; │ │ │ │ - var obj = { │ │ │ │ - rules: this.multipleSymbolizers ? style.rules : [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - if (!this.multipleSymbolizers) { │ │ │ │ - style.rules = obj.rules; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Rule": function(node, obj) { │ │ │ │ - var config; │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - config = { │ │ │ │ - symbolizers: [] │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - var rule = new OpenLayers.Rule(config); │ │ │ │ - this.readChildNodes(node, rule); │ │ │ │ - obj.rules.push(rule); │ │ │ │ - }, │ │ │ │ - "ElseFilter": function(node, rule) { │ │ │ │ - rule.elseFilter = true; │ │ │ │ - }, │ │ │ │ - "MinScaleDenominator": function(node, rule) { │ │ │ │ - rule.minScaleDenominator = parseFloat(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "MaxScaleDenominator": function(node, rule) { │ │ │ │ - rule.maxScaleDenominator = parseFloat(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "TextSymbolizer": function(node, rule) { │ │ │ │ - var config = {}; │ │ │ │ - this.readChildNodes(node, config); │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - config.zIndex = this.featureTypeCounter; │ │ │ │ - rule.symbolizers.push( │ │ │ │ - new OpenLayers.Symbolizer.Text(config) │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - rule.symbolizer["Text"] = OpenLayers.Util.applyDefaults( │ │ │ │ - config, rule.symbolizer["Text"] │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "LabelPlacement": function(node, symbolizer) { │ │ │ │ - this.readChildNodes(node, symbolizer); │ │ │ │ - }, │ │ │ │ - "PointPlacement": function(node, symbolizer) { │ │ │ │ - var config = {}; │ │ │ │ - this.readChildNodes(node, config); │ │ │ │ - config.labelRotation = config.rotation; │ │ │ │ - delete config.rotation; │ │ │ │ - var labelAlign, │ │ │ │ - x = symbolizer.labelAnchorPointX, │ │ │ │ - y = symbolizer.labelAnchorPointY; │ │ │ │ - if (x <= 1 / 3) { │ │ │ │ - labelAlign = 'l'; │ │ │ │ - } else if (x > 1 / 3 && x < 2 / 3) { │ │ │ │ - labelAlign = 'c'; │ │ │ │ - } else if (x >= 2 / 3) { │ │ │ │ - labelAlign = 'r'; │ │ │ │ - } │ │ │ │ - if (y <= 1 / 3) { │ │ │ │ - labelAlign += 'b'; │ │ │ │ - } else if (y > 1 / 3 && y < 2 / 3) { │ │ │ │ - labelAlign += 'm'; │ │ │ │ - } else if (y >= 2 / 3) { │ │ │ │ - labelAlign += 't'; │ │ │ │ - } │ │ │ │ - config.labelAlign = labelAlign; │ │ │ │ - OpenLayers.Util.applyDefaults(symbolizer, config); │ │ │ │ - }, │ │ │ │ - "AnchorPoint": function(node, symbolizer) { │ │ │ │ - this.readChildNodes(node, symbolizer); │ │ │ │ - }, │ │ │ │ - "AnchorPointX": function(node, symbolizer) { │ │ │ │ - var labelAnchorPointX = this.readers.ogc._expression.call(this, node); │ │ │ │ - // always string, could be empty string │ │ │ │ - if (labelAnchorPointX) { │ │ │ │ - symbolizer.labelAnchorPointX = labelAnchorPointX; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "AnchorPointY": function(node, symbolizer) { │ │ │ │ - var labelAnchorPointY = this.readers.ogc._expression.call(this, node); │ │ │ │ - // always string, could be empty string │ │ │ │ - if (labelAnchorPointY) { │ │ │ │ - symbolizer.labelAnchorPointY = labelAnchorPointY; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Displacement": function(node, symbolizer) { │ │ │ │ - this.readChildNodes(node, symbolizer); │ │ │ │ - }, │ │ │ │ - "DisplacementX": function(node, symbolizer) { │ │ │ │ - var labelXOffset = this.readers.ogc._expression.call(this, node); │ │ │ │ - // always string, could be empty string │ │ │ │ - if (labelXOffset) { │ │ │ │ - symbolizer.labelXOffset = labelXOffset; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "DisplacementY": function(node, symbolizer) { │ │ │ │ - var labelYOffset = this.readers.ogc._expression.call(this, node); │ │ │ │ - // always string, could be empty string │ │ │ │ - if (labelYOffset) { │ │ │ │ - symbolizer.labelYOffset = labelYOffset; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "LinePlacement": function(node, symbolizer) { │ │ │ │ - this.readChildNodes(node, symbolizer); │ │ │ │ - }, │ │ │ │ - "PerpendicularOffset": function(node, symbolizer) { │ │ │ │ - var labelPerpendicularOffset = this.readers.ogc._expression.call(this, node); │ │ │ │ - // always string, could be empty string │ │ │ │ - if (labelPerpendicularOffset) { │ │ │ │ - symbolizer.labelPerpendicularOffset = labelPerpendicularOffset; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Label": function(node, symbolizer) { │ │ │ │ - var value = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (value) { │ │ │ │ - symbolizer.label = value; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Font": function(node, symbolizer) { │ │ │ │ - this.readChildNodes(node, symbolizer); │ │ │ │ - }, │ │ │ │ - "Halo": function(node, symbolizer) { │ │ │ │ - // halo has a fill, so send fresh object │ │ │ │ - var obj = {}; │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - symbolizer.haloRadius = obj.haloRadius; │ │ │ │ - symbolizer.haloColor = obj.fillColor; │ │ │ │ - symbolizer.haloOpacity = obj.fillOpacity; │ │ │ │ - }, │ │ │ │ - "Radius": function(node, symbolizer) { │ │ │ │ - var radius = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (radius != null) { │ │ │ │ - // radius is only used for halo │ │ │ │ - symbolizer.haloRadius = radius; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "RasterSymbolizer": function(node, rule) { │ │ │ │ - var config = {}; │ │ │ │ - this.readChildNodes(node, config); │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - config.zIndex = this.featureTypeCounter; │ │ │ │ - rule.symbolizers.push( │ │ │ │ - new OpenLayers.Symbolizer.Raster(config) │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - rule.symbolizer["Raster"] = OpenLayers.Util.applyDefaults( │ │ │ │ - config, rule.symbolizer["Raster"] │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Geometry": function(node, obj) { │ │ │ │ - obj.geometry = {}; │ │ │ │ - this.readChildNodes(node, obj.geometry); │ │ │ │ - }, │ │ │ │ - "ColorMap": function(node, symbolizer) { │ │ │ │ - symbolizer.colorMap = []; │ │ │ │ - this.readChildNodes(node, symbolizer.colorMap); │ │ │ │ - }, │ │ │ │ - "ColorMapEntry": function(node, colorMap) { │ │ │ │ - var q = node.getAttribute("quantity"); │ │ │ │ - var o = node.getAttribute("opacity"); │ │ │ │ - colorMap.push({ │ │ │ │ - color: node.getAttribute("color"), │ │ │ │ - quantity: q !== null ? parseFloat(q) : undefined, │ │ │ │ - label: node.getAttribute("label") || undefined, │ │ │ │ - opacity: o !== null ? parseFloat(o) : undefined │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "LineSymbolizer": function(node, rule) { │ │ │ │ - var config = {}; │ │ │ │ - this.readChildNodes(node, config); │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - config.zIndex = this.featureTypeCounter; │ │ │ │ - rule.symbolizers.push( │ │ │ │ - new OpenLayers.Symbolizer.Line(config) │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - rule.symbolizer["Line"] = OpenLayers.Util.applyDefaults( │ │ │ │ - config, rule.symbolizer["Line"] │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "PolygonSymbolizer": function(node, rule) { │ │ │ │ - var config = { │ │ │ │ - fill: false, │ │ │ │ - stroke: false │ │ │ │ - }; │ │ │ │ - if (!this.multipleSymbolizers) { │ │ │ │ - config = rule.symbolizer["Polygon"] || config; │ │ │ │ - } │ │ │ │ - this.readChildNodes(node, config); │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - config.zIndex = this.featureTypeCounter; │ │ │ │ - rule.symbolizers.push( │ │ │ │ - new OpenLayers.Symbolizer.Polygon(config) │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - rule.symbolizer["Polygon"] = config; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "PointSymbolizer": function(node, rule) { │ │ │ │ - var config = { │ │ │ │ - fill: false, │ │ │ │ - stroke: false, │ │ │ │ - graphic: false │ │ │ │ - }; │ │ │ │ - if (!this.multipleSymbolizers) { │ │ │ │ - config = rule.symbolizer["Point"] || config; │ │ │ │ - } │ │ │ │ - this.readChildNodes(node, config); │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - config.zIndex = this.featureTypeCounter; │ │ │ │ - rule.symbolizers.push( │ │ │ │ - new OpenLayers.Symbolizer.Point(config) │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - rule.symbolizer["Point"] = config; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Stroke": function(node, symbolizer) { │ │ │ │ - symbolizer.stroke = true; │ │ │ │ - this.readChildNodes(node, symbolizer); │ │ │ │ - }, │ │ │ │ - "Fill": function(node, symbolizer) { │ │ │ │ - symbolizer.fill = true; │ │ │ │ - this.readChildNodes(node, symbolizer); │ │ │ │ - }, │ │ │ │ - "CssParameter": function(node, symbolizer) { │ │ │ │ - var cssProperty = node.getAttribute("name"); │ │ │ │ - var symProperty = this.cssMap[cssProperty]; │ │ │ │ - // for labels, fill should map to fontColor and fill-opacity │ │ │ │ - // to fontOpacity │ │ │ │ - if (symbolizer.label) { │ │ │ │ - if (cssProperty === 'fill') { │ │ │ │ - symProperty = "fontColor"; │ │ │ │ - } else if (cssProperty === 'fill-opacity') { │ │ │ │ - symProperty = "fontOpacity"; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (symProperty) { │ │ │ │ - // Limited support for parsing of OGC expressions │ │ │ │ - var value = this.readers.ogc._expression.call(this, node); │ │ │ │ - // always string, could be an empty string │ │ │ │ - if (value) { │ │ │ │ - symbolizer[symProperty] = value; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Graphic": function(node, symbolizer) { │ │ │ │ - symbolizer.graphic = true; │ │ │ │ - var graphic = {}; │ │ │ │ - // painter's order not respected here, clobber previous with next │ │ │ │ - this.readChildNodes(node, graphic); │ │ │ │ - // directly properties with names that match symbolizer properties │ │ │ │ - var properties = [ │ │ │ │ - "stroke", "strokeColor", "strokeWidth", "strokeOpacity", │ │ │ │ - "strokeLinecap", "fill", "fillColor", "fillOpacity", │ │ │ │ - "graphicName", "rotation", "graphicFormat" │ │ │ │ - ]; │ │ │ │ - var prop, value; │ │ │ │ - for (var i = 0, len = properties.length; i < len; ++i) { │ │ │ │ - prop = properties[i]; │ │ │ │ - value = graphic[prop]; │ │ │ │ - if (value != undefined) { │ │ │ │ - symbolizer[prop] = value; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - // set other generic properties with specific graphic property names │ │ │ │ - if (graphic.opacity != undefined) { │ │ │ │ - symbolizer.graphicOpacity = graphic.opacity; │ │ │ │ - } │ │ │ │ - if (graphic.size != undefined) { │ │ │ │ - var pointRadius = graphic.size / 2; │ │ │ │ - if (isNaN(pointRadius)) { │ │ │ │ - // likely a property name │ │ │ │ - symbolizer.graphicWidth = graphic.size; │ │ │ │ - } else { │ │ │ │ - symbolizer.pointRadius = graphic.size / 2; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (graphic.href != undefined) { │ │ │ │ - symbolizer.externalGraphic = graphic.href; │ │ │ │ - } │ │ │ │ - if (graphic.rotation != undefined) { │ │ │ │ - symbolizer.rotation = graphic.rotation; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "ExternalGraphic": function(node, graphic) { │ │ │ │ - this.readChildNodes(node, graphic); │ │ │ │ - }, │ │ │ │ - "Mark": function(node, graphic) { │ │ │ │ - this.readChildNodes(node, graphic); │ │ │ │ - }, │ │ │ │ - "WellKnownName": function(node, graphic) { │ │ │ │ - graphic.graphicName = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Opacity": function(node, obj) { │ │ │ │ - var opacity = this.readers.ogc._expression.call(this, node); │ │ │ │ - // always string, could be empty string │ │ │ │ - if (opacity) { │ │ │ │ - obj.opacity = opacity; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Size": function(node, obj) { │ │ │ │ - var size = this.readers.ogc._expression.call(this, node); │ │ │ │ - // always string, could be empty string │ │ │ │ - if (size) { │ │ │ │ - obj.size = size; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Rotation": function(node, obj) { │ │ │ │ - var rotation = this.readers.ogc._expression.call(this, node); │ │ │ │ - // always string, could be empty string │ │ │ │ - if (rotation) { │ │ │ │ - obj.rotation = rotation; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "OnlineResource": function(node, obj) { │ │ │ │ - obj.href = this.getAttributeNS( │ │ │ │ - node, this.namespaces.xlink, "href" │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - "Format": function(node, graphic) { │ │ │ │ - graphic.graphicFormat = this.getChildValue(node); │ │ │ │ - } │ │ │ │ + touchstart: function(evt) { │ │ │ │ + var propagate = true; │ │ │ │ + this.pinching = false; │ │ │ │ + if (OpenLayers.Event.isMultiTouch(evt)) { │ │ │ │ + this.started = true; │ │ │ │ + this.last = this.start = { │ │ │ │ + distance: this.getDistance(evt.touches), │ │ │ │ + delta: 0, │ │ │ │ + scale: 1 │ │ │ │ + }; │ │ │ │ + this.callback("start", [evt, this.start]); │ │ │ │ + propagate = !this.stopDown; │ │ │ │ + } else if (this.started) { │ │ │ │ + // Some webkit versions send fake single-touch events during │ │ │ │ + // multitouch, which cause the drag handler to trigger │ │ │ │ + return false; │ │ │ │ + } else { │ │ │ │ + this.started = false; │ │ │ │ + this.start = null; │ │ │ │ + this.last = null; │ │ │ │ } │ │ │ │ - }, OpenLayers.Format.Filter.v1_0_0.prototype.readers), │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: cssMap │ │ │ │ - * {Object} Object mapping supported css property names to OpenLayers │ │ │ │ - * symbolizer property names. │ │ │ │ - */ │ │ │ │ - cssMap: { │ │ │ │ - "stroke": "strokeColor", │ │ │ │ - "stroke-opacity": "strokeOpacity", │ │ │ │ - "stroke-width": "strokeWidth", │ │ │ │ - "stroke-linecap": "strokeLinecap", │ │ │ │ - "stroke-dasharray": "strokeDashstyle", │ │ │ │ - "fill": "fillColor", │ │ │ │ - "fill-opacity": "fillOpacity", │ │ │ │ - "font-family": "fontFamily", │ │ │ │ - "font-size": "fontSize", │ │ │ │ - "font-weight": "fontWeight", │ │ │ │ - "font-style": "fontStyle" │ │ │ │ + // prevent document dragging │ │ │ │ + OpenLayers.Event.preventDefault(evt); │ │ │ │ + return propagate; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getCssProperty │ │ │ │ - * Given a symbolizer property, get the corresponding CSS property │ │ │ │ - * from the <cssMap>. │ │ │ │ + * Method: touchmove │ │ │ │ + * Handle touchmove events │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * sym - {String} A symbolizer property name. │ │ │ │ + * evt - {Event} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A CSS property name or null if none found. │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ */ │ │ │ │ - getCssProperty: function(sym) { │ │ │ │ - var css = null; │ │ │ │ - for (var prop in this.cssMap) { │ │ │ │ - if (this.cssMap[prop] == sym) { │ │ │ │ - css = prop; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ + touchmove: function(evt) { │ │ │ │ + if (this.started && OpenLayers.Event.isMultiTouch(evt)) { │ │ │ │ + this.pinching = true; │ │ │ │ + var current = this.getPinchData(evt); │ │ │ │ + this.callback("move", [evt, current]); │ │ │ │ + this.last = current; │ │ │ │ + // prevent document dragging │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + } else if (this.started) { │ │ │ │ + // Some webkit versions send fake single-touch events during │ │ │ │ + // multitouch, which cause the drag handler to trigger │ │ │ │ + return false; │ │ │ │ } │ │ │ │ - return css; │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getGraphicFormat │ │ │ │ - * Given a href for an external graphic, try to determine the mime-type. │ │ │ │ - * This method doesn't try too hard, and will fall back to │ │ │ │ - * <defaultGraphicFormat> if one of the known <graphicFormats> is not │ │ │ │ - * the file extension of the provided href. │ │ │ │ + * Method: touchend │ │ │ │ + * Handle touchend events │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * href - {String} │ │ │ │ + * evt - {Event} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} The graphic format. │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ */ │ │ │ │ - getGraphicFormat: function(href) { │ │ │ │ - var format, regex; │ │ │ │ - for (var key in this.graphicFormats) { │ │ │ │ - if (this.graphicFormats[key].test(href)) { │ │ │ │ - format = key; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ + touchend: function(evt) { │ │ │ │ + if (this.started && !OpenLayers.Event.isMultiTouch(evt)) { │ │ │ │ + this.started = false; │ │ │ │ + this.pinching = false; │ │ │ │ + this.callback("done", [evt, this.start, this.last]); │ │ │ │ + this.start = null; │ │ │ │ + this.last = null; │ │ │ │ + return false; │ │ │ │ } │ │ │ │ - return format || this.defaultGraphicFormat; │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: defaultGraphicFormat │ │ │ │ - * {String} If none other can be determined from <getGraphicFormat>, this │ │ │ │ - * default will be returned. │ │ │ │ + * Method: activate │ │ │ │ + * Activate the handler. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The handler was successfully activated. │ │ │ │ */ │ │ │ │ - defaultGraphicFormat: "image/png", │ │ │ │ + activate: function() { │ │ │ │ + var activated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.pinching = false; │ │ │ │ + activated = true; │ │ │ │ + } │ │ │ │ + return activated; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: graphicFormats │ │ │ │ - * {Object} Mapping of image mime-types to regular extensions matching │ │ │ │ - * well-known file extensions. │ │ │ │ + * Method: deactivate │ │ │ │ + * Deactivate the handler. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The handler was successfully deactivated. │ │ │ │ */ │ │ │ │ - graphicFormats: { │ │ │ │ - "image/jpeg": /\.jpe?g$/i, │ │ │ │ - "image/gif": /\.gif$/i, │ │ │ │ - "image/png": /\.png$/i │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.started = false; │ │ │ │ + this.pinching = false; │ │ │ │ + this.start = null; │ │ │ │ + this.last = null; │ │ │ │ + deactivated = true; │ │ │ │ + } │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write │ │ │ │ + * Method: getDistance │ │ │ │ + * Get the distance in pixels between two touches. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * sld - {Object} An object representing the SLD. │ │ │ │ + * touches - {Array(Object)} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} The root of an SLD document. │ │ │ │ + * {Number} The distance in pixels. │ │ │ │ */ │ │ │ │ - write: function(sld) { │ │ │ │ - return this.writers.sld.StyledLayerDescriptor.apply(this, [sld]); │ │ │ │ + getDistance: function(touches) { │ │ │ │ + var t0 = touches[0]; │ │ │ │ + var t1 = touches[1]; │ │ │ │ + return Math.sqrt( │ │ │ │ + Math.pow(t0.olClientX - t1.olClientX, 2) + │ │ │ │ + Math.pow(t0.olClientY - t1.olClientY, 2) │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * 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: getPinchData │ │ │ │ + * Get informations about the pinch event. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} Object that contains data about the current pinch. │ │ │ │ */ │ │ │ │ - writers: OpenLayers.Util.applyDefaults({ │ │ │ │ - "sld": { │ │ │ │ - "_OGCExpression": function(nodeName, value) { │ │ │ │ - // only the simplest of ogc:expression handled │ │ │ │ - // {label: "some text and a ${propertyName}"} │ │ │ │ - var node = this.createElementNSPlus(nodeName); │ │ │ │ - var tokens = typeof value == "string" ? │ │ │ │ - value.split("${") : [value]; │ │ │ │ - node.appendChild(this.createTextNode(tokens[0])); │ │ │ │ - var item, last; │ │ │ │ - for (var i = 1, len = tokens.length; i < len; i++) { │ │ │ │ - item = tokens[i]; │ │ │ │ - last = item.indexOf("}"); │ │ │ │ - if (last > 0) { │ │ │ │ - this.writeNode( │ │ │ │ - "ogc:PropertyName", { │ │ │ │ - property: item.substring(0, last) │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - node.appendChild( │ │ │ │ - this.createTextNode(item.substring(++last)) │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - // no ending }, so this is a literal ${ │ │ │ │ - node.appendChild( │ │ │ │ - this.createTextNode("${" + item) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "StyledLayerDescriptor": function(sld) { │ │ │ │ - var root = this.createElementNSPlus( │ │ │ │ - "sld:StyledLayerDescriptor", { │ │ │ │ - attributes: { │ │ │ │ - "version": this.VERSION, │ │ │ │ - "xsi:schemaLocation": this.schemaLocation │ │ │ │ - } │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - │ │ │ │ - // For ArcGIS Server it is necessary to define this │ │ │ │ - // at the root level (see ticket:2166). │ │ │ │ - root.setAttribute("xmlns:ogc", this.namespaces.ogc); │ │ │ │ - root.setAttribute("xmlns:gml", this.namespaces.gml); │ │ │ │ - │ │ │ │ - // add in optional name │ │ │ │ - if (sld.name) { │ │ │ │ - this.writeNode("Name", sld.name, root); │ │ │ │ - } │ │ │ │ - // add in optional title │ │ │ │ - if (sld.title) { │ │ │ │ - this.writeNode("Title", sld.title, root); │ │ │ │ - } │ │ │ │ - // add in optional description │ │ │ │ - if (sld.description) { │ │ │ │ - this.writeNode("Abstract", sld.description, root); │ │ │ │ - } │ │ │ │ - // add in named layers │ │ │ │ - // allow namedLayers to be an array │ │ │ │ - if (OpenLayers.Util.isArray(sld.namedLayers)) { │ │ │ │ - for (var i = 0, len = sld.namedLayers.length; i < len; ++i) { │ │ │ │ - this.writeNode("NamedLayer", sld.namedLayers[i], root); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - for (var name in sld.namedLayers) { │ │ │ │ - this.writeNode("NamedLayer", sld.namedLayers[name], root); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return root; │ │ │ │ - }, │ │ │ │ - "Name": function(name) { │ │ │ │ - return this.createElementNSPlus("sld:Name", { │ │ │ │ - value: name │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "Title": function(title) { │ │ │ │ - return this.createElementNSPlus("sld:Title", { │ │ │ │ - value: title │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "Abstract": function(description) { │ │ │ │ - return this.createElementNSPlus( │ │ │ │ - "sld:Abstract", { │ │ │ │ - value: description │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - "NamedLayer": function(layer) { │ │ │ │ - var node = this.createElementNSPlus("sld:NamedLayer"); │ │ │ │ - │ │ │ │ - // add in required name │ │ │ │ - this.writeNode("Name", layer.name, node); │ │ │ │ - │ │ │ │ - // optional sld:LayerFeatureConstraints here │ │ │ │ - │ │ │ │ - // add in named styles │ │ │ │ - if (layer.namedStyles) { │ │ │ │ - for (var i = 0, len = layer.namedStyles.length; i < len; ++i) { │ │ │ │ - this.writeNode( │ │ │ │ - "NamedStyle", layer.namedStyles[i], node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - // add in user styles │ │ │ │ - if (layer.userStyles) { │ │ │ │ - for (var i = 0, len = layer.userStyles.length; i < len; ++i) { │ │ │ │ - this.writeNode( │ │ │ │ - "UserStyle", layer.userStyles[i], node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "NamedStyle": function(name) { │ │ │ │ - var node = this.createElementNSPlus("sld:NamedStyle"); │ │ │ │ - this.writeNode("Name", name, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "UserStyle": function(style) { │ │ │ │ - var node = this.createElementNSPlus("sld:UserStyle"); │ │ │ │ - │ │ │ │ - // add in optional name │ │ │ │ - if (style.name) { │ │ │ │ - this.writeNode("Name", style.name, node); │ │ │ │ - } │ │ │ │ - // add in optional title │ │ │ │ - if (style.title) { │ │ │ │ - this.writeNode("Title", style.title, node); │ │ │ │ - } │ │ │ │ - // add in optional description │ │ │ │ - if (style.description) { │ │ │ │ - this.writeNode("Abstract", style.description, node); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // add isdefault │ │ │ │ - if (style.isDefault) { │ │ │ │ - this.writeNode("IsDefault", style.isDefault, node); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // add FeatureTypeStyles │ │ │ │ - if (this.multipleSymbolizers && style.rules) { │ │ │ │ - // group style objects by symbolizer zIndex │ │ │ │ - var rulesByZ = { │ │ │ │ - 0: [] │ │ │ │ - }; │ │ │ │ - var zValues = [0]; │ │ │ │ - var rule, ruleMap, symbolizer, zIndex, clone; │ │ │ │ - for (var i = 0, ii = style.rules.length; i < ii; ++i) { │ │ │ │ - rule = style.rules[i]; │ │ │ │ - if (rule.symbolizers) { │ │ │ │ - ruleMap = {}; │ │ │ │ - for (var j = 0, jj = rule.symbolizers.length; j < jj; ++j) { │ │ │ │ - symbolizer = rule.symbolizers[j]; │ │ │ │ - zIndex = symbolizer.zIndex; │ │ │ │ - if (!(zIndex in ruleMap)) { │ │ │ │ - clone = rule.clone(); │ │ │ │ - clone.symbolizers = []; │ │ │ │ - ruleMap[zIndex] = clone; │ │ │ │ - } │ │ │ │ - ruleMap[zIndex].symbolizers.push(symbolizer.clone()); │ │ │ │ - } │ │ │ │ - for (zIndex in ruleMap) { │ │ │ │ - if (!(zIndex in rulesByZ)) { │ │ │ │ - zValues.push(zIndex); │ │ │ │ - rulesByZ[zIndex] = []; │ │ │ │ - } │ │ │ │ - rulesByZ[zIndex].push(ruleMap[zIndex]); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - // no symbolizers in rule │ │ │ │ - rulesByZ[0].push(rule.clone()); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - // write one FeatureTypeStyle per zIndex │ │ │ │ - zValues.sort(); │ │ │ │ - var rules; │ │ │ │ - for (var i = 0, ii = zValues.length; i < ii; ++i) { │ │ │ │ - rules = rulesByZ[zValues[i]]; │ │ │ │ - if (rules.length > 0) { │ │ │ │ - clone = style.clone(); │ │ │ │ - clone.rules = rulesByZ[zValues[i]]; │ │ │ │ - this.writeNode("FeatureTypeStyle", clone, node); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.writeNode("FeatureTypeStyle", style, node); │ │ │ │ - } │ │ │ │ - │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "IsDefault": function(bool) { │ │ │ │ - return this.createElementNSPlus( │ │ │ │ - "sld:IsDefault", { │ │ │ │ - value: (bool) ? "1" : "0" │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - "FeatureTypeStyle": function(style) { │ │ │ │ - var node = this.createElementNSPlus("sld:FeatureTypeStyle"); │ │ │ │ - │ │ │ │ - // OpenLayers currently stores no Name, Title, Abstract, │ │ │ │ - // FeatureTypeName, or SemanticTypeIdentifier information │ │ │ │ - // related to FeatureTypeStyle │ │ │ │ - │ │ │ │ - // add in rules │ │ │ │ - for (var i = 0, len = style.rules.length; i < len; ++i) { │ │ │ │ - this.writeNode("Rule", style.rules[i], node); │ │ │ │ - } │ │ │ │ - │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Rule": function(rule) { │ │ │ │ - var node = this.createElementNSPlus("sld:Rule"); │ │ │ │ - │ │ │ │ - // add in optional name │ │ │ │ - if (rule.name) { │ │ │ │ - this.writeNode("Name", rule.name, node); │ │ │ │ - } │ │ │ │ - // add in optional title │ │ │ │ - if (rule.title) { │ │ │ │ - this.writeNode("Title", rule.title, node); │ │ │ │ - } │ │ │ │ - // add in optional description │ │ │ │ - if (rule.description) { │ │ │ │ - this.writeNode("Abstract", rule.description, node); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // add in LegendGraphic here │ │ │ │ - │ │ │ │ - // add in optional filters │ │ │ │ - if (rule.elseFilter) { │ │ │ │ - this.writeNode("ElseFilter", null, node); │ │ │ │ - } else if (rule.filter) { │ │ │ │ - this.writeNode("ogc:Filter", rule.filter, node); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // add in scale limits │ │ │ │ - if (rule.minScaleDenominator != undefined) { │ │ │ │ - this.writeNode( │ │ │ │ - "MinScaleDenominator", rule.minScaleDenominator, node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (rule.maxScaleDenominator != undefined) { │ │ │ │ - this.writeNode( │ │ │ │ - "MaxScaleDenominator", rule.maxScaleDenominator, node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var type, symbolizer; │ │ │ │ - if (this.multipleSymbolizers && rule.symbolizers) { │ │ │ │ - var symbolizer; │ │ │ │ - for (var i = 0, ii = rule.symbolizers.length; i < ii; ++i) { │ │ │ │ - symbolizer = rule.symbolizers[i]; │ │ │ │ - type = symbolizer.CLASS_NAME.split(".").pop(); │ │ │ │ - this.writeNode( │ │ │ │ - type + "Symbolizer", symbolizer, node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - // add in symbolizers (relies on geometry type keys) │ │ │ │ - var types = OpenLayers.Style.SYMBOLIZER_PREFIXES; │ │ │ │ - for (var i = 0, len = types.length; i < len; ++i) { │ │ │ │ - type = types[i]; │ │ │ │ - symbolizer = rule.symbolizer[type]; │ │ │ │ - if (symbolizer) { │ │ │ │ - this.writeNode( │ │ │ │ - type + "Symbolizer", symbolizer, node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - │ │ │ │ - }, │ │ │ │ - "ElseFilter": function() { │ │ │ │ - return this.createElementNSPlus("sld:ElseFilter"); │ │ │ │ - }, │ │ │ │ - "MinScaleDenominator": function(scale) { │ │ │ │ - return this.createElementNSPlus( │ │ │ │ - "sld:MinScaleDenominator", { │ │ │ │ - value: scale │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - "MaxScaleDenominator": function(scale) { │ │ │ │ - return this.createElementNSPlus( │ │ │ │ - "sld:MaxScaleDenominator", { │ │ │ │ - value: scale │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - "LineSymbolizer": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:LineSymbolizer"); │ │ │ │ - this.writeNode("Stroke", symbolizer, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Stroke": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Stroke"); │ │ │ │ - │ │ │ │ - // GraphicFill here │ │ │ │ - // GraphicStroke here │ │ │ │ - │ │ │ │ - // add in CssParameters │ │ │ │ - if (symbolizer.strokeColor != undefined) { │ │ │ │ - this.writeNode( │ │ │ │ - "CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "strokeColor" │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (symbolizer.strokeOpacity != undefined) { │ │ │ │ - this.writeNode( │ │ │ │ - "CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "strokeOpacity" │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (symbolizer.strokeWidth != undefined) { │ │ │ │ - this.writeNode( │ │ │ │ - "CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "strokeWidth" │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (symbolizer.strokeDashstyle != undefined && symbolizer.strokeDashstyle !== "solid") { │ │ │ │ - // assumes valid stroke-dasharray value │ │ │ │ - this.writeNode( │ │ │ │ - "CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "strokeDashstyle" │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (symbolizer.strokeLinecap != undefined) { │ │ │ │ - this.writeNode( │ │ │ │ - "CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "strokeLinecap" │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "CssParameter": function(obj) { │ │ │ │ - // not handling ogc:expressions for now │ │ │ │ - return this.createElementNSPlus("sld:CssParameter", { │ │ │ │ - attributes: { │ │ │ │ - name: this.getCssProperty(obj.key) │ │ │ │ - }, │ │ │ │ - value: obj.symbolizer[obj.key] │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "TextSymbolizer": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:TextSymbolizer"); │ │ │ │ - // add in optional Label │ │ │ │ - if (symbolizer.label != null) { │ │ │ │ - this.writeNode("Label", symbolizer.label, node); │ │ │ │ - } │ │ │ │ - // add in optional Font │ │ │ │ - if (symbolizer.fontFamily != null || │ │ │ │ - symbolizer.fontSize != null || │ │ │ │ - symbolizer.fontWeight != null || │ │ │ │ - symbolizer.fontStyle != null) { │ │ │ │ - this.writeNode("Font", symbolizer, node); │ │ │ │ - } │ │ │ │ - // add in optional LabelPlacement │ │ │ │ - if (symbolizer.labelAnchorPointX != null || │ │ │ │ - symbolizer.labelAnchorPointY != null || │ │ │ │ - symbolizer.labelAlign != null || │ │ │ │ - symbolizer.labelXOffset != null || │ │ │ │ - symbolizer.labelYOffset != null || │ │ │ │ - symbolizer.labelRotation != null || │ │ │ │ - symbolizer.labelPerpendicularOffset != null) { │ │ │ │ - this.writeNode("LabelPlacement", symbolizer, node); │ │ │ │ - } │ │ │ │ - // add in optional Halo │ │ │ │ - if (symbolizer.haloRadius != null || │ │ │ │ - symbolizer.haloColor != null || │ │ │ │ - symbolizer.haloOpacity != null) { │ │ │ │ - this.writeNode("Halo", symbolizer, node); │ │ │ │ - } │ │ │ │ - // add in optional Fill │ │ │ │ - if (symbolizer.fontColor != null || │ │ │ │ - symbolizer.fontOpacity != null) { │ │ │ │ - this.writeNode("Fill", { │ │ │ │ - fillColor: symbolizer.fontColor, │ │ │ │ - fillOpacity: symbolizer.fontOpacity │ │ │ │ - }, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "LabelPlacement": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:LabelPlacement"); │ │ │ │ - // PointPlacement and LinePlacement are choices, so don't output both │ │ │ │ - if ((symbolizer.labelAnchorPointX != null || │ │ │ │ - symbolizer.labelAnchorPointY != null || │ │ │ │ - symbolizer.labelAlign != null || │ │ │ │ - symbolizer.labelXOffset != null || │ │ │ │ - symbolizer.labelYOffset != null || │ │ │ │ - symbolizer.labelRotation != null) && │ │ │ │ - symbolizer.labelPerpendicularOffset == null) { │ │ │ │ - this.writeNode("PointPlacement", symbolizer, node); │ │ │ │ - } │ │ │ │ - if (symbolizer.labelPerpendicularOffset != null) { │ │ │ │ - this.writeNode("LinePlacement", symbolizer, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "LinePlacement": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:LinePlacement"); │ │ │ │ - this.writeNode("PerpendicularOffset", symbolizer.labelPerpendicularOffset, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "PerpendicularOffset": function(value) { │ │ │ │ - return this.createElementNSPlus("sld:PerpendicularOffset", { │ │ │ │ - value: value │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "PointPlacement": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:PointPlacement"); │ │ │ │ - if (symbolizer.labelAnchorPointX != null || │ │ │ │ - symbolizer.labelAnchorPointY != null || │ │ │ │ - symbolizer.labelAlign != null) { │ │ │ │ - this.writeNode("AnchorPoint", symbolizer, node); │ │ │ │ - } │ │ │ │ - if (symbolizer.labelXOffset != null || │ │ │ │ - symbolizer.labelYOffset != null) { │ │ │ │ - this.writeNode("Displacement", symbolizer, node); │ │ │ │ - } │ │ │ │ - if (symbolizer.labelRotation != null) { │ │ │ │ - this.writeNode("Rotation", symbolizer.labelRotation, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "AnchorPoint": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:AnchorPoint"); │ │ │ │ - var x = symbolizer.labelAnchorPointX, │ │ │ │ - y = symbolizer.labelAnchorPointY; │ │ │ │ - if (x != null) { │ │ │ │ - this.writeNode("AnchorPointX", x, node); │ │ │ │ - } │ │ │ │ - if (y != null) { │ │ │ │ - this.writeNode("AnchorPointY", y, node); │ │ │ │ - } │ │ │ │ - if (x == null && y == null) { │ │ │ │ - var xAlign = symbolizer.labelAlign.substr(0, 1), │ │ │ │ - yAlign = symbolizer.labelAlign.substr(1, 1); │ │ │ │ - if (xAlign === "l") { │ │ │ │ - x = 0; │ │ │ │ - } else if (xAlign === "c") { │ │ │ │ - x = 0.5; │ │ │ │ - } else if (xAlign === "r") { │ │ │ │ - x = 1; │ │ │ │ - } │ │ │ │ - if (yAlign === "b") { │ │ │ │ - y = 0; │ │ │ │ - } else if (yAlign === "m") { │ │ │ │ - y = 0.5; │ │ │ │ - } else if (yAlign === "t") { │ │ │ │ - y = 1; │ │ │ │ - } │ │ │ │ - this.writeNode("AnchorPointX", x, node); │ │ │ │ - this.writeNode("AnchorPointY", y, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "AnchorPointX": function(value) { │ │ │ │ - return this.createElementNSPlus("sld:AnchorPointX", { │ │ │ │ - value: value │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "AnchorPointY": function(value) { │ │ │ │ - return this.createElementNSPlus("sld:AnchorPointY", { │ │ │ │ - value: value │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "Displacement": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Displacement"); │ │ │ │ - if (symbolizer.labelXOffset != null) { │ │ │ │ - this.writeNode("DisplacementX", symbolizer.labelXOffset, node); │ │ │ │ - } │ │ │ │ - if (symbolizer.labelYOffset != null) { │ │ │ │ - this.writeNode("DisplacementY", symbolizer.labelYOffset, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "DisplacementX": function(value) { │ │ │ │ - return this.createElementNSPlus("sld:DisplacementX", { │ │ │ │ - value: value │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "DisplacementY": function(value) { │ │ │ │ - return this.createElementNSPlus("sld:DisplacementY", { │ │ │ │ - value: value │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "Font": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Font"); │ │ │ │ - // add in CssParameters │ │ │ │ - if (symbolizer.fontFamily) { │ │ │ │ - this.writeNode( │ │ │ │ - "CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "fontFamily" │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (symbolizer.fontSize) { │ │ │ │ - this.writeNode( │ │ │ │ - "CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "fontSize" │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (symbolizer.fontWeight) { │ │ │ │ - this.writeNode( │ │ │ │ - "CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "fontWeight" │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (symbolizer.fontStyle) { │ │ │ │ - this.writeNode( │ │ │ │ - "CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "fontStyle" │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Label": function(label) { │ │ │ │ - return this.writers.sld._OGCExpression.call( │ │ │ │ - this, "sld:Label", label │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - "Halo": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Halo"); │ │ │ │ - if (symbolizer.haloRadius) { │ │ │ │ - this.writeNode("Radius", symbolizer.haloRadius, node); │ │ │ │ - } │ │ │ │ - if (symbolizer.haloColor || symbolizer.haloOpacity) { │ │ │ │ - this.writeNode("Fill", { │ │ │ │ - fillColor: symbolizer.haloColor, │ │ │ │ - fillOpacity: symbolizer.haloOpacity │ │ │ │ - }, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Radius": function(value) { │ │ │ │ - return this.createElementNSPlus("sld:Radius", { │ │ │ │ - value: value │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "RasterSymbolizer": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:RasterSymbolizer"); │ │ │ │ - if (symbolizer.geometry) { │ │ │ │ - this.writeNode("Geometry", symbolizer.geometry, node); │ │ │ │ - } │ │ │ │ - if (symbolizer.opacity) { │ │ │ │ - this.writeNode("Opacity", symbolizer.opacity, node); │ │ │ │ - } │ │ │ │ - if (symbolizer.colorMap) { │ │ │ │ - this.writeNode("ColorMap", symbolizer.colorMap, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Geometry": function(geometry) { │ │ │ │ - var node = this.createElementNSPlus("sld:Geometry"); │ │ │ │ - if (geometry.property) { │ │ │ │ - this.writeNode("ogc:PropertyName", geometry, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "ColorMap": function(colorMap) { │ │ │ │ - var node = this.createElementNSPlus("sld:ColorMap"); │ │ │ │ - for (var i = 0, len = colorMap.length; i < len; ++i) { │ │ │ │ - this.writeNode("ColorMapEntry", colorMap[i], node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "ColorMapEntry": function(colorMapEntry) { │ │ │ │ - var node = this.createElementNSPlus("sld:ColorMapEntry"); │ │ │ │ - var a = colorMapEntry; │ │ │ │ - node.setAttribute("color", a.color); │ │ │ │ - a.opacity !== undefined && node.setAttribute("opacity", │ │ │ │ - parseFloat(a.opacity)); │ │ │ │ - a.quantity !== undefined && node.setAttribute("quantity", │ │ │ │ - parseFloat(a.quantity)); │ │ │ │ - a.label !== undefined && node.setAttribute("label", a.label); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "PolygonSymbolizer": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:PolygonSymbolizer"); │ │ │ │ - if (symbolizer.fill !== false) { │ │ │ │ - this.writeNode("Fill", symbolizer, node); │ │ │ │ - } │ │ │ │ - if (symbolizer.stroke !== false) { │ │ │ │ - this.writeNode("Stroke", symbolizer, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Fill": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Fill"); │ │ │ │ - │ │ │ │ - // GraphicFill here │ │ │ │ - │ │ │ │ - // add in CssParameters │ │ │ │ - if (symbolizer.fillColor) { │ │ │ │ - this.writeNode( │ │ │ │ - "CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "fillColor" │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (symbolizer.fillOpacity != null) { │ │ │ │ - this.writeNode( │ │ │ │ - "CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "fillOpacity" │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "PointSymbolizer": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:PointSymbolizer"); │ │ │ │ - this.writeNode("Graphic", symbolizer, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Graphic": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Graphic"); │ │ │ │ - if (symbolizer.externalGraphic != undefined) { │ │ │ │ - this.writeNode("ExternalGraphic", symbolizer, node); │ │ │ │ - } else { │ │ │ │ - this.writeNode("Mark", symbolizer, node); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (symbolizer.graphicOpacity != undefined) { │ │ │ │ - this.writeNode("Opacity", symbolizer.graphicOpacity, node); │ │ │ │ - } │ │ │ │ - if (symbolizer.pointRadius != undefined) { │ │ │ │ - this.writeNode("Size", symbolizer.pointRadius * 2, node); │ │ │ │ - } else if (symbolizer.graphicWidth != undefined) { │ │ │ │ - this.writeNode("Size", symbolizer.graphicWidth, node); │ │ │ │ - } │ │ │ │ - if (symbolizer.rotation != undefined) { │ │ │ │ - this.writeNode("Rotation", symbolizer.rotation, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "ExternalGraphic": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:ExternalGraphic"); │ │ │ │ - this.writeNode( │ │ │ │ - "OnlineResource", symbolizer.externalGraphic, node │ │ │ │ - ); │ │ │ │ - var format = symbolizer.graphicFormat || │ │ │ │ - this.getGraphicFormat(symbolizer.externalGraphic); │ │ │ │ - this.writeNode("Format", format, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Mark": function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Mark"); │ │ │ │ - if (symbolizer.graphicName) { │ │ │ │ - this.writeNode("WellKnownName", symbolizer.graphicName, node); │ │ │ │ - } │ │ │ │ - if (symbolizer.fill !== false) { │ │ │ │ - this.writeNode("Fill", symbolizer, node); │ │ │ │ - } │ │ │ │ - if (symbolizer.stroke !== false) { │ │ │ │ - this.writeNode("Stroke", symbolizer, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "WellKnownName": function(name) { │ │ │ │ - return this.createElementNSPlus("sld:WellKnownName", { │ │ │ │ - value: name │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "Opacity": function(value) { │ │ │ │ - return this.createElementNSPlus("sld:Opacity", { │ │ │ │ - value: value │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "Size": function(value) { │ │ │ │ - return this.writers.sld._OGCExpression.call( │ │ │ │ - this, "sld:Size", value │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - "Rotation": function(value) { │ │ │ │ - return this.createElementNSPlus("sld:Rotation", { │ │ │ │ - value: value │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "OnlineResource": function(href) { │ │ │ │ - return this.createElementNSPlus("sld:OnlineResource", { │ │ │ │ - attributes: { │ │ │ │ - "xlink:type": "simple", │ │ │ │ - "xlink:href": href │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "Format": function(format) { │ │ │ │ - return this.createElementNSPlus("sld:Format", { │ │ │ │ - value: format │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.Filter.v1_0_0.prototype.writers), │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.SLD.v1" │ │ │ │ + getPinchData: function(evt) { │ │ │ │ + var distance = this.getDistance(evt.touches); │ │ │ │ + var scale = distance / this.start.distance; │ │ │ │ + return { │ │ │ │ + distance: distance, │ │ │ │ + delta: this.last.distance - distance, │ │ │ │ + scale: scale │ │ │ │ + }; │ │ │ │ + }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Pinch" │ │ │ │ }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/SLD/v1_0_0.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/SLD/v1.js │ │ │ │ - * @requires OpenLayers/Format/Filter/v1_0_0.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.SLD.v1_0_0 │ │ │ │ - * Write SLD version 1.0.0. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.SLD.v1> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.SLD.v1_0_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.SLD.v1, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: VERSION │ │ │ │ - * {String} 1.0.0 │ │ │ │ - */ │ │ │ │ - VERSION: "1.0.0", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} http://www.opengis.net/sld │ │ │ │ - * http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd │ │ │ │ - */ │ │ │ │ - schemaLocation: "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.SLD.v1_0_0 │ │ │ │ - * Instances of this class are not created directly. Use the │ │ │ │ - * <OpenLayers.Format.SLD> constructor instead. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.SLD.v1_0_0" │ │ │ │ │ │ │ │ - }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/SLDSelect.js │ │ │ │ + OpenLayers/Handler/Box.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Layer/WMS.js │ │ │ │ - * @requires OpenLayers/Handler/RegularPolygon.js │ │ │ │ - * @requires OpenLayers/Handler/Polygon.js │ │ │ │ - * @requires OpenLayers/Handler/Path.js │ │ │ │ - * @requires OpenLayers/Handler/Click.js │ │ │ │ - * @requires OpenLayers/Filter/Spatial.js │ │ │ │ - * @requires OpenLayers/Format/SLD/v1_0_0.js │ │ │ │ + * @requires OpenLayers/Handler.js │ │ │ │ + * @requires OpenLayers/Handler/Drag.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.SLDSelect │ │ │ │ - * Perform selections on WMS layers using Styled Layer Descriptor (SLD) │ │ │ │ + * Class: OpenLayers.Handler.Box │ │ │ │ + * Handler for dragging a rectangle across the map. Box is displayed │ │ │ │ + * on mouse down, moves on mouse move, and is finished on mouse up. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Handler> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ +OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * selected - Triggered when a selection occurs. Listeners receive an │ │ │ │ - * event with *filters* and *layer* properties. Filters will be an │ │ │ │ - * array of OpenLayers.Filter objects created in order to perform │ │ │ │ - * the particular selection. │ │ │ │ + * Property: dragHandler │ │ │ │ + * {<OpenLayers.Handler.Drag>} │ │ │ │ */ │ │ │ │ + dragHandler: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: clearOnDeactivate │ │ │ │ - * {Boolean} Should the selection be cleared when the control is │ │ │ │ - * deactivated. Default value is false. │ │ │ │ + * APIProperty: boxDivClassName │ │ │ │ + * {String} The CSS class to use for drawing the box. Default is │ │ │ │ + * olHandlerBoxZoomBox │ │ │ │ */ │ │ │ │ - clearOnDeactivate: false, │ │ │ │ + boxDivClassName: 'olHandlerBoxZoomBox', │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: layers │ │ │ │ - * {Array(<OpenLayers.Layer.WMS>)} The WMS layers this control will work │ │ │ │ - * on. │ │ │ │ + * Property: boxOffsets │ │ │ │ + * {Object} Caches box offsets from css. This is used by the getBoxOffsets │ │ │ │ + * method. │ │ │ │ */ │ │ │ │ - layers: null, │ │ │ │ + boxOffsets: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: callbacks │ │ │ │ - * {Object} The functions that are sent to the handler for callback │ │ │ │ + * Constructor: OpenLayers.Handler.Box │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * control - {<OpenLayers.Control>} │ │ │ │ + * callbacks - {Object} An object with a properties whose values are │ │ │ │ + * functions. Various callbacks described below. │ │ │ │ + * options - {Object} │ │ │ │ + * │ │ │ │ + * Named callbacks: │ │ │ │ + * start - Called when the box drag operation starts. │ │ │ │ + * done - Called when the box drag operation is finished. │ │ │ │ + * The callback should expect to receive a single argument, the box │ │ │ │ + * bounds or a pixel. If the box dragging didn't span more than a 5 │ │ │ │ + * pixel distance, a pixel will be returned instead of a bounds object. │ │ │ │ */ │ │ │ │ - callbacks: null, │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ + this.dragHandler = new OpenLayers.Handler.Drag( │ │ │ │ + this, { │ │ │ │ + down: this.startBox, │ │ │ │ + move: this.moveBox, │ │ │ │ + out: this.removeBox, │ │ │ │ + up: this.endBox │ │ │ │ + }, { │ │ │ │ + keyMask: this.keyMask │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: selectionSymbolizer │ │ │ │ - * {Object} Determines the styling of the selected objects. Default is │ │ │ │ - * a selection in red. │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - selectionSymbolizer: { │ │ │ │ - 'Polygon': { │ │ │ │ - fillColor: '#FF0000', │ │ │ │ - stroke: false │ │ │ │ - }, │ │ │ │ - 'Line': { │ │ │ │ - strokeColor: '#FF0000', │ │ │ │ - strokeWidth: 2 │ │ │ │ - }, │ │ │ │ - 'Point': { │ │ │ │ - graphicName: 'square', │ │ │ │ - fillColor: '#FF0000', │ │ │ │ - pointRadius: 5 │ │ │ │ + destroy: function() { │ │ │ │ + OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ + if (this.dragHandler) { │ │ │ │ + this.dragHandler.destroy(); │ │ │ │ + this.dragHandler = null; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: layerOptions │ │ │ │ - * {Object} The options to apply to the selection layer, by default the │ │ │ │ - * selection layer will be kept out of the layer switcher. │ │ │ │ + * Method: setMap │ │ │ │ */ │ │ │ │ - layerOptions: null, │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Handler.prototype.setMap.apply(this, arguments); │ │ │ │ + if (this.dragHandler) { │ │ │ │ + this.dragHandler.setMap(map); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: handlerOptions │ │ │ │ - * {Object} Used to set non-default properties on the control's handler │ │ │ │ + * Method: startBox │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * xy - {<OpenLayers.Pixel>} │ │ │ │ */ │ │ │ │ + startBox: function(xy) { │ │ │ │ + this.callback("start", []); │ │ │ │ + this.zoomBox = OpenLayers.Util.createDiv('zoomBox', { │ │ │ │ + x: -9999, │ │ │ │ + y: -9999 │ │ │ │ + }); │ │ │ │ + this.zoomBox.className = this.boxDivClassName; │ │ │ │ + this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: sketchStyle │ │ │ │ - * {<OpenLayers.Style>|Object} Style or symbolizer to use for the sketch │ │ │ │ - * handler. The recommended way of styling the sketch layer, however, is │ │ │ │ - * to configure an <OpenLayers.StyleMap> in the layerOptions of the │ │ │ │ - * <handlerOptions>: │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Path, { │ │ │ │ - * handlerOptions: { │ │ │ │ - * layerOptions: { │ │ │ │ - * styleMap: new OpenLayers.StyleMap({ │ │ │ │ - * "default": {strokeColor: "yellow"} │ │ │ │ - * }) │ │ │ │ - * } │ │ │ │ - * } │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ - sketchStyle: null, │ │ │ │ + this.map.viewPortDiv.appendChild(this.zoomBox); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: wfsCache │ │ │ │ - * {Object} Cache to use for storing parsed results from │ │ │ │ - * <OpenLayers.Format.WFSDescribeFeatureType.read>. If not provided, │ │ │ │ - * these will be cached on the prototype. │ │ │ │ - */ │ │ │ │ - wfsCache: {}, │ │ │ │ + OpenLayers.Element.addClass( │ │ │ │ + this.map.viewPortDiv, "olDrawBox" │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: layerCache │ │ │ │ - * {Object} Cache to use for storing references to the selection layers. │ │ │ │ - * Normally each source layer will have exactly 1 selection layer of │ │ │ │ - * type OpenLayers.Layer.WMS. If not provided, layers will │ │ │ │ - * be cached on the prototype. Note that if <clearOnDeactivate> is │ │ │ │ - * true, the layer will no longer be cached after deactivating the │ │ │ │ - * control. │ │ │ │ + * Method: moveBox │ │ │ │ */ │ │ │ │ - layerCache: {}, │ │ │ │ + moveBox: function(xy) { │ │ │ │ + var startX = this.dragHandler.start.x; │ │ │ │ + var startY = this.dragHandler.start.y; │ │ │ │ + var deltaX = Math.abs(startX - xy.x); │ │ │ │ + var deltaY = Math.abs(startY - xy.y); │ │ │ │ + │ │ │ │ + var offset = this.getBoxOffsets(); │ │ │ │ + this.zoomBox.style.width = (deltaX + offset.width + 1) + "px"; │ │ │ │ + this.zoomBox.style.height = (deltaY + offset.height + 1) + "px"; │ │ │ │ + this.zoomBox.style.left = (xy.x < startX ? │ │ │ │ + startX - deltaX - offset.left : startX - offset.left) + "px"; │ │ │ │ + this.zoomBox.style.top = (xy.y < startY ? │ │ │ │ + startY - deltaY - offset.top : startY - offset.top) + "px"; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.SLDSelect │ │ │ │ - * Create a new control for selecting features in WMS layers using │ │ │ │ - * Styled Layer Descriptor (SLD). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * handler - {<OpenLayers.Class>} A sketch handler class. This determines │ │ │ │ - * the type of selection, e.g. box (<OpenLayers.Handler.Box>), point │ │ │ │ - * (<OpenLayers.Handler.Point>), path (<OpenLayers.Handler.Path>) or │ │ │ │ - * polygon (<OpenLayers.Handler.Polygon>) selection. To use circle │ │ │ │ - * type selection, use <OpenLayers.Handler.RegularPolygon> and pass │ │ │ │ - * the number of desired sides (e.g. 40) as "sides" property to the │ │ │ │ - * <handlerOptions>. │ │ │ │ - * options - {Object} An object containing all configuration properties for │ │ │ │ - * the control. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * layers - Array({<OpenLayers.Layer.WMS>}) The layers to perform the │ │ │ │ - * selection on. │ │ │ │ + * Method: endBox │ │ │ │ */ │ │ │ │ - initialize: function(handler, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - │ │ │ │ - this.callbacks = OpenLayers.Util.extend({ │ │ │ │ - done: this.select, │ │ │ │ - click: this.select │ │ │ │ - }, this.callbacks); │ │ │ │ - this.handlerOptions = this.handlerOptions || {}; │ │ │ │ - this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, { │ │ │ │ - displayInLayerSwitcher: false, │ │ │ │ - tileOptions: { │ │ │ │ - maxGetUrlLength: 2048 │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - if (this.sketchStyle) { │ │ │ │ - this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( │ │ │ │ - this.handlerOptions.layerOptions, { │ │ │ │ - styleMap: new OpenLayers.StyleMap({ │ │ │ │ - "default": this.sketchStyle │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - ); │ │ │ │ + endBox: function(end) { │ │ │ │ + var result; │ │ │ │ + if (Math.abs(this.dragHandler.start.x - end.x) > 5 || │ │ │ │ + Math.abs(this.dragHandler.start.y - end.y) > 5) { │ │ │ │ + var start = this.dragHandler.start; │ │ │ │ + var top = Math.min(start.y, end.y); │ │ │ │ + var bottom = Math.max(start.y, end.y); │ │ │ │ + var left = Math.min(start.x, end.x); │ │ │ │ + var right = Math.max(start.x, end.x); │ │ │ │ + result = new OpenLayers.Bounds(left, bottom, right, top); │ │ │ │ + } else { │ │ │ │ + result = this.dragHandler.start.clone(); // i.e. OL.Pixel │ │ │ │ } │ │ │ │ - this.handler = new handler(this, this.callbacks, this.handlerOptions); │ │ │ │ + this.removeBox(); │ │ │ │ + │ │ │ │ + this.callback("done", [result]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Take care of things that are not handled in superclass. │ │ │ │ + * Method: removeBox │ │ │ │ + * Remove the zoombox from the screen and nullify our reference to it. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - for (var key in this.layerCache) { │ │ │ │ - delete this.layerCache[key]; │ │ │ │ - } │ │ │ │ - for (var key in this.wfsCache) { │ │ │ │ - delete this.wfsCache[key]; │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + removeBox: function() { │ │ │ │ + this.map.viewPortDiv.removeChild(this.zoomBox); │ │ │ │ + this.zoomBox = null; │ │ │ │ + this.boxOffsets = null; │ │ │ │ + OpenLayers.Element.removeClass( │ │ │ │ + this.map.viewPortDiv, "olDrawBox" │ │ │ │ + ); │ │ │ │ + │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: coupleLayerVisiblity │ │ │ │ - * Couple the selection layer and the source layer with respect to │ │ │ │ - * layer visibility. So if the source layer is turned off, the │ │ │ │ - * selection layer is also turned off. │ │ │ │ - * │ │ │ │ - * Context: │ │ │ │ - * - {<OpenLayers.Layer>} │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} │ │ │ │ + * Method: activate │ │ │ │ */ │ │ │ │ - coupleLayerVisiblity: function(evt) { │ │ │ │ - this.setVisibility(evt.object.getVisibility()); │ │ │ │ + activate: function() { │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.dragHandler.activate(); │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createSelectionLayer │ │ │ │ - * Creates a "clone" from the source layer in which the selection can │ │ │ │ - * be drawn. This ensures both the source layer and the selection are │ │ │ │ - * visible and not only the selection. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * source - {<OpenLayers.Layer.WMS>} The source layer on which the selection │ │ │ │ - * is performed. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.WMS>} A WMS layer with maxGetUrlLength configured to 2048 │ │ │ │ - * since SLD selections can easily get quite long. │ │ │ │ + * Method: deactivate │ │ │ │ */ │ │ │ │ - createSelectionLayer: function(source) { │ │ │ │ - // check if we already have a selection layer for the source layer │ │ │ │ - var selectionLayer; │ │ │ │ - if (!this.layerCache[source.id]) { │ │ │ │ - selectionLayer = new OpenLayers.Layer.WMS(source.name, │ │ │ │ - source.url, source.params, │ │ │ │ - OpenLayers.Util.applyDefaults( │ │ │ │ - this.layerOptions, │ │ │ │ - source.getOptions()) │ │ │ │ - ); │ │ │ │ - this.layerCache[source.id] = selectionLayer; │ │ │ │ - // make sure the layers are coupled wrt visibility, but only │ │ │ │ - // if they are not displayed in the layer switcher, because in │ │ │ │ - // that case the user cannot control visibility. │ │ │ │ - if (this.layerOptions.displayInLayerSwitcher === false) { │ │ │ │ - source.events.on({ │ │ │ │ - "visibilitychanged": this.coupleLayerVisiblity, │ │ │ │ - scope: selectionLayer │ │ │ │ - }); │ │ │ │ + deactivate: function() { │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + if (this.dragHandler.deactivate()) { │ │ │ │ + if (this.zoomBox) { │ │ │ │ + this.removeBox(); │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.map.addLayer(selectionLayer); │ │ │ │ + return true; │ │ │ │ } else { │ │ │ │ - selectionLayer = this.layerCache[source.id]; │ │ │ │ + return false; │ │ │ │ } │ │ │ │ - return selectionLayer; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createSLD │ │ │ │ - * Create the SLD document for the layer using the supplied filters. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.WMS>} │ │ │ │ - * filters - Array({<OpenLayers.Filter>}) The filters to be applied. │ │ │ │ - * geometryAttributes - Array({Object}) The geometry attributes of the │ │ │ │ - * layer. │ │ │ │ - * │ │ │ │ + * Method: getBoxOffsets │ │ │ │ + * Determines border offsets for a box, according to the box model. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} The SLD document generated as a string. │ │ │ │ + * {Object} an object with the following offsets: │ │ │ │ + * - left │ │ │ │ + * - right │ │ │ │ + * - top │ │ │ │ + * - bottom │ │ │ │ + * - width │ │ │ │ + * - height │ │ │ │ */ │ │ │ │ - createSLD: function(layer, filters, geometryAttributes) { │ │ │ │ - var sld = { │ │ │ │ - version: "1.0.0", │ │ │ │ - namedLayers: {} │ │ │ │ - }; │ │ │ │ - var layerNames = [layer.params.LAYERS].join(",").split(","); │ │ │ │ - for (var i = 0, len = layerNames.length; i < len; i++) { │ │ │ │ - var name = layerNames[i]; │ │ │ │ - sld.namedLayers[name] = { │ │ │ │ - name: name, │ │ │ │ - userStyles: [] │ │ │ │ + getBoxOffsets: function() { │ │ │ │ + if (!this.boxOffsets) { │ │ │ │ + // Determine the box model. If the testDiv's clientWidth is 3, then │ │ │ │ + // the borders are outside and we are dealing with the w3c box │ │ │ │ + // model. Otherwise, the browser uses the traditional box model and │ │ │ │ + // the borders are inside the box bounds, leaving us with a │ │ │ │ + // clientWidth of 1. │ │ │ │ + var testDiv = document.createElement("div"); │ │ │ │ + //testDiv.style.visibility = "hidden"; │ │ │ │ + testDiv.style.position = "absolute"; │ │ │ │ + testDiv.style.border = "1px solid black"; │ │ │ │ + testDiv.style.width = "3px"; │ │ │ │ + document.body.appendChild(testDiv); │ │ │ │ + var w3cBoxModel = testDiv.clientWidth == 3; │ │ │ │ + document.body.removeChild(testDiv); │ │ │ │ + │ │ │ │ + var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, │ │ │ │ + "border-left-width")); │ │ │ │ + var right = parseInt(OpenLayers.Element.getStyle( │ │ │ │ + this.zoomBox, "border-right-width")); │ │ │ │ + var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, │ │ │ │ + "border-top-width")); │ │ │ │ + var bottom = parseInt(OpenLayers.Element.getStyle( │ │ │ │ + this.zoomBox, "border-bottom-width")); │ │ │ │ + this.boxOffsets = { │ │ │ │ + left: left, │ │ │ │ + right: right, │ │ │ │ + top: top, │ │ │ │ + bottom: bottom, │ │ │ │ + width: w3cBoxModel === false ? left + right : 0, │ │ │ │ + height: w3cBoxModel === false ? top + bottom : 0 │ │ │ │ }; │ │ │ │ - var symbolizer = this.selectionSymbolizer; │ │ │ │ - var geometryAttribute = geometryAttributes[i]; │ │ │ │ - if (geometryAttribute.type.indexOf('Polygon') >= 0) { │ │ │ │ - symbolizer = { │ │ │ │ - Polygon: this.selectionSymbolizer['Polygon'] │ │ │ │ - }; │ │ │ │ - } else if (geometryAttribute.type.indexOf('LineString') >= 0) { │ │ │ │ - symbolizer = { │ │ │ │ - Line: this.selectionSymbolizer['Line'] │ │ │ │ - }; │ │ │ │ - } else if (geometryAttribute.type.indexOf('Point') >= 0) { │ │ │ │ - symbolizer = { │ │ │ │ - Point: this.selectionSymbolizer['Point'] │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - var filter = filters[i]; │ │ │ │ - sld.namedLayers[name].userStyles.push({ │ │ │ │ - name: 'default', │ │ │ │ - rules: [ │ │ │ │ - new OpenLayers.Rule({ │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - filter: filter, │ │ │ │ - maxScaleDenominator: layer.options.minScale │ │ │ │ - }) │ │ │ │ - ] │ │ │ │ - }); │ │ │ │ } │ │ │ │ - return new OpenLayers.Format.SLD({ │ │ │ │ - srsName: this.map.getProjection() │ │ │ │ - }).write(sld); │ │ │ │ + return this.boxOffsets; │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Box" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Handler/Hover.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Handler.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Handler.Hover │ │ │ │ + * The hover handler is to be used to emulate mouseovers on objects │ │ │ │ + * on the map that aren't DOM elements. For example one can use │ │ │ │ + * this handler to send WMS/GetFeatureInfo requests as the user │ │ │ │ + * moves the mouve over the map. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Handler> │ │ │ │ + */ │ │ │ │ +OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: parseDescribeLayer │ │ │ │ - * Parse the SLD WMS DescribeLayer response and issue the corresponding │ │ │ │ - * WFS DescribeFeatureType request │ │ │ │ - * │ │ │ │ - * request - {XMLHttpRequest} The request object. │ │ │ │ + * APIProperty: delay │ │ │ │ + * {Integer} - Number of milliseconds between mousemoves before │ │ │ │ + * the event is considered a hover. Default is 500. │ │ │ │ */ │ │ │ │ - parseDescribeLayer: function(request) { │ │ │ │ - var format = new OpenLayers.Format.WMSDescribeLayer(); │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText; │ │ │ │ - } │ │ │ │ - var describeLayer = format.read(doc); │ │ │ │ - var typeNames = []; │ │ │ │ - var url = null; │ │ │ │ - for (var i = 0, len = describeLayer.length; i < len; i++) { │ │ │ │ - // perform a WFS DescribeFeatureType request │ │ │ │ - if (describeLayer[i].owsType == "WFS") { │ │ │ │ - typeNames.push(describeLayer[i].typeName); │ │ │ │ - url = describeLayer[i].owsURL; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var options = { │ │ │ │ - url: url, │ │ │ │ - params: { │ │ │ │ - SERVICE: "WFS", │ │ │ │ - TYPENAME: typeNames.toString(), │ │ │ │ - REQUEST: "DescribeFeatureType", │ │ │ │ - VERSION: "1.0.0" │ │ │ │ - }, │ │ │ │ - callback: function(request) { │ │ │ │ - var format = new OpenLayers.Format.WFSDescribeFeatureType(); │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText; │ │ │ │ - } │ │ │ │ - var describeFeatureType = format.read(doc); │ │ │ │ - this.control.wfsCache[this.layer.id] = describeFeatureType; │ │ │ │ - this.control._queue && this.control.applySelection(); │ │ │ │ - }, │ │ │ │ - scope: this │ │ │ │ - }; │ │ │ │ - OpenLayers.Request.GET(options); │ │ │ │ - }, │ │ │ │ + delay: 500, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getGeometryAttributes │ │ │ │ - * Look up the geometry attributes from the WFS DescribeFeatureType response │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.WMS>} The layer for which to look up the │ │ │ │ - * geometry attributes. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * Array({Object}) Array of geometry attributes │ │ │ │ + * APIProperty: pixelTolerance │ │ │ │ + * {Integer} - Maximum number of pixels between mousemoves for │ │ │ │ + * an event to be considered a hover. Default is null. │ │ │ │ */ │ │ │ │ - getGeometryAttributes: function(layer) { │ │ │ │ - var result = []; │ │ │ │ - var cache = this.wfsCache[layer.id]; │ │ │ │ - for (var i = 0, len = cache.featureTypes.length; i < len; i++) { │ │ │ │ - var typeName = cache.featureTypes[i]; │ │ │ │ - var properties = typeName.properties; │ │ │ │ - for (var j = 0, lenj = properties.length; j < lenj; j++) { │ │ │ │ - var property = properties[j]; │ │ │ │ - var type = property.type; │ │ │ │ - if ((type.indexOf('LineString') >= 0) || │ │ │ │ - (type.indexOf('GeometryAssociationType') >= 0) || │ │ │ │ - (type.indexOf('GeometryPropertyType') >= 0) || │ │ │ │ - (type.indexOf('Point') >= 0) || │ │ │ │ - (type.indexOf('Polygon') >= 0)) { │ │ │ │ - result.push(property); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return result; │ │ │ │ - }, │ │ │ │ + pixelTolerance: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Activate the control. Activating the control will perform a SLD WMS │ │ │ │ - * DescribeLayer request followed by a WFS DescribeFeatureType request │ │ │ │ - * so that the proper symbolizers can be chosen based on the geometry │ │ │ │ - * type. │ │ │ │ + * APIProperty: stopMove │ │ │ │ + * {Boolean} - Stop other listeners from being notified on mousemoves. │ │ │ │ + * Default is false. │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Control.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ - var layer = this.layers[i]; │ │ │ │ - if (layer && !this.wfsCache[layer.id]) { │ │ │ │ - var options = { │ │ │ │ - url: layer.url, │ │ │ │ - params: { │ │ │ │ - SERVICE: "WMS", │ │ │ │ - VERSION: layer.params.VERSION, │ │ │ │ - LAYERS: layer.params.LAYERS, │ │ │ │ - REQUEST: "DescribeLayer" │ │ │ │ - }, │ │ │ │ - callback: this.parseDescribeLayer, │ │ │ │ - scope: { │ │ │ │ - layer: layer, │ │ │ │ - control: this │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - OpenLayers.Request.GET(options); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return activated; │ │ │ │ - }, │ │ │ │ + stopMove: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the control. If clearOnDeactivate is true, remove the │ │ │ │ - * selection layer(s). │ │ │ │ + * Property: px │ │ │ │ + * {<OpenLayers.Pixel>} - The location of the last mousemove, expressed │ │ │ │ + * in pixels. │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Control.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ - var layer = this.layers[i]; │ │ │ │ - if (layer && this.clearOnDeactivate === true) { │ │ │ │ - var layerCache = this.layerCache; │ │ │ │ - var selectionLayer = layerCache[layer.id]; │ │ │ │ - if (selectionLayer) { │ │ │ │ - layer.events.un({ │ │ │ │ - "visibilitychanged": this.coupleLayerVisiblity, │ │ │ │ - scope: selectionLayer │ │ │ │ - }); │ │ │ │ - selectionLayer.destroy(); │ │ │ │ - delete layerCache[layer.id]; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + px: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: timerId │ │ │ │ + * {Number} - The id of the timer. │ │ │ │ + */ │ │ │ │ + timerId: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Handler.Hover │ │ │ │ + * Construct a hover handler. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * control - {<OpenLayers.Control>} The control that initialized this │ │ │ │ + * handler. The control is assumed to have a valid map property; that │ │ │ │ + * map is used in the handler's own setMap method. │ │ │ │ + * callbacks - {Object} An object with keys corresponding to callbacks │ │ │ │ + * that will be called by the handler. The callbacks should │ │ │ │ + * expect to receive a single argument, the event. Callbacks for │ │ │ │ + * 'move', the mouse is moving, and 'pause', the mouse is pausing, │ │ │ │ + * are supported. │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * the handler. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: mousemove │ │ │ │ + * Called when the mouse moves on the map. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Continue propagating this event. │ │ │ │ + */ │ │ │ │ + mousemove: function(evt) { │ │ │ │ + if (this.passesTolerance(evt.xy)) { │ │ │ │ + this.clearTimer(); │ │ │ │ + this.callback('move', [evt]); │ │ │ │ + this.px = evt.xy; │ │ │ │ + // clone the evt so original properties can be accessed even │ │ │ │ + // if the browser deletes them during the delay │ │ │ │ + evt = OpenLayers.Util.extend({}, evt); │ │ │ │ + this.timerId = window.setTimeout( │ │ │ │ + OpenLayers.Function.bind(this.delayedCall, this, evt), │ │ │ │ + this.delay │ │ │ │ + ); │ │ │ │ } │ │ │ │ - return deactivated; │ │ │ │ + return !this.stopMove; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setLayers │ │ │ │ - * Set the layers on which the selection should be performed. Call the │ │ │ │ - * setLayers method if the layer(s) to be used change and the same │ │ │ │ - * control should be used on a new set of layers. │ │ │ │ - * If the control is already active, it will be active after the new │ │ │ │ - * set of layers is set. │ │ │ │ + * Method: mouseout │ │ │ │ + * Called when the mouse goes out of the map. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * layers - {Array(<OpenLayers.Layer.WMS>)} The new set of layers on which │ │ │ │ - * the selection should be performed. │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Continue propagating this event. │ │ │ │ */ │ │ │ │ - setLayers: function(layers) { │ │ │ │ - if (this.active) { │ │ │ │ - this.deactivate(); │ │ │ │ - this.layers = layers; │ │ │ │ - this.activate(); │ │ │ │ - } else { │ │ │ │ - this.layers = layers; │ │ │ │ + mouseout: function(evt) { │ │ │ │ + if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) { │ │ │ │ + this.clearTimer(); │ │ │ │ + this.callback('move', [evt]); │ │ │ │ } │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: createFilter │ │ │ │ - * Create the filter to be used in the SLD. │ │ │ │ + * Method: passesTolerance │ │ │ │ + * Determine whether the mouse move is within the optional pixel tolerance. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometryAttribute - {Object} Used to get the name of the geometry │ │ │ │ - * attribute which is needed for constructing the spatial filter. │ │ │ │ - * geometry - {<OpenLayers.Geometry>} The geometry to use. │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Filter.Spatial>} The spatial filter created. │ │ │ │ + * {Boolean} The mouse move is within the pixel tolerance. │ │ │ │ */ │ │ │ │ - createFilter: function(geometryAttribute, geometry) { │ │ │ │ - var filter = null; │ │ │ │ - if (this.handler instanceof OpenLayers.Handler.RegularPolygon) { │ │ │ │ - // box │ │ │ │ - if (this.handler.irregular === true) { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - value: geometry.getBounds() │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - value: geometry │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } else if (this.handler instanceof OpenLayers.Handler.Polygon) { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - value: geometry │ │ │ │ - }); │ │ │ │ - } else if (this.handler instanceof OpenLayers.Handler.Path) { │ │ │ │ - // if source layer is point based, use DWITHIN instead │ │ │ │ - if (geometryAttribute.type.indexOf('Point') >= 0) { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.DWITHIN, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - distance: this.map.getExtent().getWidth() * 0.01, │ │ │ │ - distanceUnits: this.map.getUnits(), │ │ │ │ - value: geometry │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - value: geometry │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } else if (this.handler instanceof OpenLayers.Handler.Click) { │ │ │ │ - if (geometryAttribute.type.indexOf('Polygon') >= 0) { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - value: geometry │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.DWITHIN, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - distance: this.map.getExtent().getWidth() * 0.01, │ │ │ │ - distanceUnits: this.map.getUnits(), │ │ │ │ - value: geometry │ │ │ │ - }); │ │ │ │ + passesTolerance: function(px) { │ │ │ │ + var passes = true; │ │ │ │ + if (this.pixelTolerance && this.px) { │ │ │ │ + var dpx = Math.sqrt( │ │ │ │ + Math.pow(this.px.x - px.x, 2) + │ │ │ │ + Math.pow(this.px.y - px.y, 2) │ │ │ │ + ); │ │ │ │ + if (dpx < this.pixelTolerance) { │ │ │ │ + passes = false; │ │ │ │ } │ │ │ │ } │ │ │ │ - return filter; │ │ │ │ + return passes; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: select │ │ │ │ - * When the handler is done, use SLD_BODY on the selection layer to │ │ │ │ - * display the selection in the map. │ │ │ │ + * Method: clearTimer │ │ │ │ + * Clear the timer and set <timerId> to null. │ │ │ │ + */ │ │ │ │ + clearTimer: function() { │ │ │ │ + if (this.timerId != null) { │ │ │ │ + window.clearTimeout(this.timerId); │ │ │ │ + this.timerId = null; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: delayedCall │ │ │ │ + * Triggers pause callback. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {Object} or {<OpenLayers.Geometry>} │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ */ │ │ │ │ - select: function(geometry) { │ │ │ │ - this._queue = function() { │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ - var layer = this.layers[i]; │ │ │ │ - var geometryAttributes = this.getGeometryAttributes(layer); │ │ │ │ - var filters = []; │ │ │ │ - for (var j = 0, lenj = geometryAttributes.length; j < lenj; j++) { │ │ │ │ - var geometryAttribute = geometryAttributes[j]; │ │ │ │ - if (geometryAttribute !== null) { │ │ │ │ - // from the click handler we will not get an actual │ │ │ │ - // geometry so transform │ │ │ │ - if (!(geometry instanceof OpenLayers.Geometry)) { │ │ │ │ - var point = this.map.getLonLatFromPixel( │ │ │ │ - geometry.xy); │ │ │ │ - geometry = new OpenLayers.Geometry.Point( │ │ │ │ - point.lon, point.lat); │ │ │ │ - } │ │ │ │ - var filter = this.createFilter(geometryAttribute, │ │ │ │ - geometry); │ │ │ │ - if (filter !== null) { │ │ │ │ - filters.push(filter); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - var selectionLayer = this.createSelectionLayer(layer); │ │ │ │ - │ │ │ │ - this.events.triggerEvent("selected", { │ │ │ │ - layer: layer, │ │ │ │ - filters: filters │ │ │ │ - }); │ │ │ │ - │ │ │ │ - var sld = this.createSLD(layer, filters, geometryAttributes); │ │ │ │ - │ │ │ │ - selectionLayer.mergeNewParams({ │ │ │ │ - SLD_BODY: sld │ │ │ │ - }); │ │ │ │ - delete this._queue; │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - this.applySelection(); │ │ │ │ + delayedCall: function(evt) { │ │ │ │ + this.callback('pause', [evt]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: applySelection │ │ │ │ - * Checks if all required wfs data is cached, and applies the selection │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivate the handler. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The handler was successfully deactivated. │ │ │ │ */ │ │ │ │ - applySelection: function() { │ │ │ │ - var canApply = true; │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ - if (!this.wfsCache[this.layers[i].id]) { │ │ │ │ - canApply = false; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.clearTimer(); │ │ │ │ + deactivated = true; │ │ │ │ } │ │ │ │ - canApply && this._queue.call(this); │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.SLDSelect" │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Hover" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/Button.js │ │ │ │ + OpenLayers/Protocol/WFS.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Protocol.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.Button │ │ │ │ - * The Button control is a very simple push-button, for use with │ │ │ │ - * <OpenLayers.Control.Panel>. │ │ │ │ - * When clicked, the function trigger() is executed. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * Class: OpenLayers.Protocol.WFS │ │ │ │ + * Used to create a versioned WFS protocol. Default version is 1.0.0. │ │ │ │ * │ │ │ │ - * Use: │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol>} A WFS protocol of the given version. │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ * (code) │ │ │ │ - * var button = new OpenLayers.Control.Button({ │ │ │ │ - * displayClass: "MyButton", trigger: myFunction │ │ │ │ - * }); │ │ │ │ - * panel.addControls([button]); │ │ │ │ + * var protocol = new OpenLayers.Protocol.WFS({ │ │ │ │ + * version: "1.1.0", │ │ │ │ + * url: "http://demo.opengeo.org/geoserver/wfs", │ │ │ │ + * featureType: "tasmania_roads", │ │ │ │ + * featureNS: "http://www.openplans.org/topp", │ │ │ │ + * geometryName: "the_geom" │ │ │ │ + * }); │ │ │ │ * (end) │ │ │ │ + * │ │ │ │ + * See the protocols for specific WFS versions for more detail. │ │ │ │ + */ │ │ │ │ +OpenLayers.Protocol.WFS = function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults( │ │ │ │ + options, OpenLayers.Protocol.WFS.DEFAULTS │ │ │ │ + ); │ │ │ │ + var cls = OpenLayers.Protocol.WFS["v" + options.version.replace(/\./g, "_")]; │ │ │ │ + if (!cls) { │ │ │ │ + throw "Unsupported WFS version: " + options.version; │ │ │ │ + } │ │ │ │ + return new cls(options); │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Function: fromWMSLayer │ │ │ │ + * Convenience function to create a WFS protocol from a WMS layer. This makes │ │ │ │ + * the assumption that a WFS requests can be issued at the same URL as │ │ │ │ + * WMS requests and that a WFS featureType exists with the same name as the │ │ │ │ + * WMS layer. │ │ │ │ + * │ │ │ │ + * This function is designed to auto-configure <url>, <featureType>, │ │ │ │ + * <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that │ │ │ │ + * srsName matching with the WMS layer will not work with WFS 1.0.0. │ │ │ │ * │ │ │ │ - * Will create a button with CSS class MyButtonItemInactive, that │ │ │ │ - * will call the function MyFunction() when clicked. │ │ │ │ + * Parameters: │ │ │ │ + * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS │ │ │ │ + * FeatureType at the same server url with the same typename. │ │ │ │ + * options - {Object} Default properties to be set on the protocol. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.WFS>} │ │ │ │ */ │ │ │ │ -OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - /** │ │ │ │ - * Property: type │ │ │ │ - * {Integer} OpenLayers.Control.TYPE_BUTTON. │ │ │ │ - */ │ │ │ │ - type: OpenLayers.Control.TYPE_BUTTON, │ │ │ │ +OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) { │ │ │ │ + var typeName, featurePrefix; │ │ │ │ + var param = layer.params["LAYERS"]; │ │ │ │ + var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(":"); │ │ │ │ + if (parts.length > 1) { │ │ │ │ + featurePrefix = parts[0]; │ │ │ │ + } │ │ │ │ + typeName = parts.pop(); │ │ │ │ + var protocolOptions = { │ │ │ │ + url: layer.url, │ │ │ │ + featureType: typeName, │ │ │ │ + featurePrefix: featurePrefix, │ │ │ │ + srsName: layer.projection && layer.projection.getCode() || │ │ │ │ + layer.map && layer.map.getProjectionObject().getCode(), │ │ │ │ + version: "1.1.0" │ │ │ │ + }; │ │ │ │ + return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults( │ │ │ │ + options, protocolOptions │ │ │ │ + )); │ │ │ │ +}; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: trigger │ │ │ │ - * Called by a control panel when the button is clicked. │ │ │ │ - */ │ │ │ │ - trigger: function() {}, │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Protocol.WFS.DEFAULTS │ │ │ │ + */ │ │ │ │ +OpenLayers.Protocol.WFS.DEFAULTS = { │ │ │ │ + "version": "1.0.0" │ │ │ │ +}; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Protocol/CSW.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Button" │ │ │ │ -}); │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Protocol.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Protocol.CSW │ │ │ │ + * Used to create a versioned CSW protocol. Default version is 2.0.2. │ │ │ │ + */ │ │ │ │ +OpenLayers.Protocol.CSW = function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults( │ │ │ │ + options, OpenLayers.Protocol.CSW.DEFAULTS │ │ │ │ + ); │ │ │ │ + var cls = OpenLayers.Protocol.CSW["v" + options.version.replace(/\./g, "_")]; │ │ │ │ + if (!cls) { │ │ │ │ + throw "Unsupported CSW version: " + options.version; │ │ │ │ + } │ │ │ │ + return new cls(options); │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Protocol.CSW.DEFAULTS │ │ │ │ + */ │ │ │ │ +OpenLayers.Protocol.CSW.DEFAULTS = { │ │ │ │ + "version": "2.0.2" │ │ │ │ +}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/NavigationHistory.js │ │ │ │ + OpenLayers/Protocol/HTTP.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Control/Button.js │ │ │ │ + * @requires OpenLayers/Protocol.js │ │ │ │ + * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.NavigationHistory │ │ │ │ - * A navigation history control. This is a meta-control, that creates two │ │ │ │ - * dependent controls: <previous> and <next>. Call the trigger method │ │ │ │ - * on the <previous> and <next> controls to restore previous and next │ │ │ │ - * history states. The previous and next controls will become active │ │ │ │ - * when there are available states to restore and will become deactive │ │ │ │ - * when there are no states to restore. │ │ │ │ + * if application uses the query string, for example, for BBOX parameters, │ │ │ │ + * OpenLayers/Format/QueryStringFilter.js should be included in the build config file │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Protocol.HTTP │ │ │ │ + * A basic HTTP protocol for vector layers. Create a new instance with the │ │ │ │ + * <OpenLayers.Protocol.HTTP> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Protocol> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ +OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: type │ │ │ │ - * {String} Note that this control is not intended to be added directly │ │ │ │ - * to a control panel. Instead, add the sub-controls previous and │ │ │ │ - * next. These sub-controls are button type controls that activate │ │ │ │ - * and deactivate themselves. If this parent control is added to │ │ │ │ - * a panel, it will act as a toggle. │ │ │ │ + * Property: url │ │ │ │ + * {String} Service URL, read-only, set through the options │ │ │ │ + * passed to constructor. │ │ │ │ */ │ │ │ │ - type: OpenLayers.Control.TYPE_TOGGLE, │ │ │ │ + url: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: previous │ │ │ │ - * {<OpenLayers.Control>} A button type control whose trigger method restores │ │ │ │ - * the previous state managed by this control. │ │ │ │ + * Property: headers │ │ │ │ + * {Object} HTTP request headers, read-only, set through the options │ │ │ │ + * passed to the constructor, │ │ │ │ + * Example: {'Content-Type': 'plain/text'} │ │ │ │ */ │ │ │ │ - previous: null, │ │ │ │ + headers: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: previousOptions │ │ │ │ - * {Object} Set this property on the options argument of the constructor │ │ │ │ - * to set optional properties on the <previous> control. │ │ │ │ + * Property: params │ │ │ │ + * {Object} Parameters of GET requests, read-only, set through the options │ │ │ │ + * passed to the constructor, │ │ │ │ + * Example: {'bbox': '5,5,5,5'} │ │ │ │ */ │ │ │ │ - previousOptions: null, │ │ │ │ + params: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: next │ │ │ │ - * {<OpenLayers.Control>} A button type control whose trigger method restores │ │ │ │ - * the next state managed by this control. │ │ │ │ + * Property: callback │ │ │ │ + * {Object} Function to be called when the <read>, <create>, │ │ │ │ + * <update>, <delete> or <commit> operation completes, read-only, │ │ │ │ + * set through the options passed to the constructor. │ │ │ │ */ │ │ │ │ - next: null, │ │ │ │ + callback: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: nextOptions │ │ │ │ - * {Object} Set this property on the options argument of the constructor │ │ │ │ - * to set optional properties on the <next> control. │ │ │ │ + * Property: scope │ │ │ │ + * {Object} Callback execution scope, read-only, set through the │ │ │ │ + * options passed to the constructor. │ │ │ │ */ │ │ │ │ - nextOptions: null, │ │ │ │ + scope: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: limit │ │ │ │ - * {Integer} Optional limit on the number of history items to retain. If │ │ │ │ - * null, there is no limit. Default is 50. │ │ │ │ + * APIProperty: readWithPOST │ │ │ │ + * {Boolean} true if read operations are done with POST requests │ │ │ │ + * instead of GET, defaults to false. │ │ │ │ */ │ │ │ │ - limit: 50, │ │ │ │ + readWithPOST: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * true. │ │ │ │ + * APIProperty: updateWithPOST │ │ │ │ + * {Boolean} true if update operations are done with POST requests │ │ │ │ + * defaults to false. │ │ │ │ */ │ │ │ │ - autoActivate: true, │ │ │ │ + updateWithPOST: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: clearOnDeactivate │ │ │ │ - * {Boolean} Clear the history when the control is deactivated. Default │ │ │ │ - * is false. │ │ │ │ + * APIProperty: deleteWithPOST │ │ │ │ + * {Boolean} true if delete operations are done with POST requests │ │ │ │ + * defaults to false. │ │ │ │ + * if true, POST data is set to output of format.write(). │ │ │ │ */ │ │ │ │ - clearOnDeactivate: false, │ │ │ │ + deleteWithPOST: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: registry │ │ │ │ - * {Object} An object with keys corresponding to event types. Values │ │ │ │ - * are functions that return an object representing the current state. │ │ │ │ + * Property: wildcarded. │ │ │ │ + * {Boolean} If true percent signs are added around values │ │ │ │ + * read from LIKE filters, for example if the protocol │ │ │ │ + * read method is passed a LIKE filter whose property │ │ │ │ + * is "foo" and whose value is "bar" the string │ │ │ │ + * "foo__ilike=%bar%" will be sent in the query string; │ │ │ │ + * defaults to false. │ │ │ │ */ │ │ │ │ - registry: null, │ │ │ │ + wildcarded: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: nextStack │ │ │ │ - * {Array} Array of items in the history. │ │ │ │ + * APIProperty: srsInBBOX │ │ │ │ + * {Boolean} Include the SRS identifier in BBOX query string parameter. │ │ │ │ + * Default is false. If true and the layer has a projection object set, │ │ │ │ + * any BBOX filter will be serialized with a fifth item identifying the │ │ │ │ + * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913 │ │ │ │ */ │ │ │ │ - nextStack: null, │ │ │ │ + srsInBBOX: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: previousStack │ │ │ │ - * {Array} List of items in the history. First item represents the current │ │ │ │ - * state. │ │ │ │ + * Constructor: OpenLayers.Protocol.HTTP │ │ │ │ + * A class for giving layers generic HTTP protocol. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ + * │ │ │ │ + * Valid options include: │ │ │ │ + * url - {String} │ │ │ │ + * headers - {Object} │ │ │ │ + * params - {Object} URL parameters for GET requests │ │ │ │ + * format - {<OpenLayers.Format>} │ │ │ │ + * callback - {Function} │ │ │ │ + * scope - {Object} │ │ │ │ */ │ │ │ │ - previousStack: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + this.params = {}; │ │ │ │ + this.headers = {}; │ │ │ │ + OpenLayers.Protocol.prototype.initialize.apply(this, arguments); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: listeners │ │ │ │ - * {Object} An object containing properties corresponding to event types. │ │ │ │ - * This object is used to configure the control and is modified on │ │ │ │ - * construction. │ │ │ │ - */ │ │ │ │ - listeners: null, │ │ │ │ + if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { │ │ │ │ + var format = new OpenLayers.Format.QueryStringFilter({ │ │ │ │ + wildcarded: this.wildcarded, │ │ │ │ + srsInBBOX: this.srsInBBOX │ │ │ │ + }); │ │ │ │ + this.filterToParams = function(filter, params) { │ │ │ │ + return format.write(filter, params); │ │ │ │ + }; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: restoring │ │ │ │ - * {Boolean} Currently restoring a history state. This is set to true │ │ │ │ - * before calling restore and set to false after restore returns. │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up the protocol. │ │ │ │ */ │ │ │ │ - restoring: false, │ │ │ │ + destroy: function() { │ │ │ │ + this.params = null; │ │ │ │ + this.headers = null; │ │ │ │ + OpenLayers.Protocol.prototype.destroy.apply(this); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.NavigationHistory │ │ │ │ - * │ │ │ │ + * APIMethod: filterToParams │ │ │ │ + * Optional method to translate an <OpenLayers.Filter> object into an object │ │ │ │ + * that can be serialized as request query string provided. If a custom │ │ │ │ + * method is not provided, the filter will be serialized using the │ │ │ │ + * <OpenLayers.Format.QueryStringFilter> class. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be used │ │ │ │ - * to extend the control. │ │ │ │ + * filter - {<OpenLayers.Filter>} filter to convert. │ │ │ │ + * params - {Object} The parameters object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} The resulting parameters object. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - │ │ │ │ - this.registry = OpenLayers.Util.extend({ │ │ │ │ - "moveend": this.getState │ │ │ │ - }, this.registry); │ │ │ │ - │ │ │ │ - var previousOptions = { │ │ │ │ - trigger: OpenLayers.Function.bind(this.previousTrigger, this), │ │ │ │ - displayClass: this.displayClass + " " + this.displayClass + "Previous" │ │ │ │ - }; │ │ │ │ - OpenLayers.Util.extend(previousOptions, this.previousOptions); │ │ │ │ - this.previous = new OpenLayers.Control.Button(previousOptions); │ │ │ │ - │ │ │ │ - var nextOptions = { │ │ │ │ - trigger: OpenLayers.Function.bind(this.nextTrigger, this), │ │ │ │ - displayClass: this.displayClass + " " + this.displayClass + "Next" │ │ │ │ - }; │ │ │ │ - OpenLayers.Util.extend(nextOptions, this.nextOptions); │ │ │ │ - this.next = new OpenLayers.Control.Button(nextOptions); │ │ │ │ - │ │ │ │ - this.clear(); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onPreviousChange │ │ │ │ - * Called when the previous history stack changes. │ │ │ │ + * APIMethod: read │ │ │ │ + * Construct a request for reading new features. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * state - {Object} An object representing the state to be restored │ │ │ │ - * if previous is triggered again or null if no previous states remain. │ │ │ │ - * length - {Integer} The number of remaining previous states that can │ │ │ │ - * be restored. │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * url - {String} Url for the request. │ │ │ │ + * params - {Object} Parameters to get serialized as a query string. │ │ │ │ + * headers - {Object} Headers to be set on the request. │ │ │ │ + * filter - {<OpenLayers.Filter>} Filter to get serialized as a │ │ │ │ + * query string. │ │ │ │ + * readWithPOST - {Boolean} If the request should be done with POST. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property │ │ │ │ + * references the HTTP request, this object is also passed to the │ │ │ │ + * callback function when the request completes, its "features" property │ │ │ │ + * is then populated with the features received from the server. │ │ │ │ */ │ │ │ │ - onPreviousChange: function(state, length) { │ │ │ │ - if (state && !this.previous.active) { │ │ │ │ - this.previous.activate(); │ │ │ │ - } else if (!state && this.previous.active) { │ │ │ │ - this.previous.deactivate(); │ │ │ │ + read: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ + options = options || {}; │ │ │ │ + options.params = OpenLayers.Util.applyDefaults( │ │ │ │ + options.params, this.options.params); │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + if (options.filter && this.filterToParams) { │ │ │ │ + options.params = this.filterToParams( │ │ │ │ + options.filter, options.params │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + var readWithPOST = (options.readWithPOST !== undefined) ? │ │ │ │ + options.readWithPOST : this.readWithPOST; │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "read" │ │ │ │ + }); │ │ │ │ + if (readWithPOST) { │ │ │ │ + var headers = options.headers || {}; │ │ │ │ + headers["Content-Type"] = "application/x-www-form-urlencoded"; │ │ │ │ + resp.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ + data: OpenLayers.Util.getParameterString(options.params), │ │ │ │ + headers: headers │ │ │ │ + }); │ │ │ │ + } else { │ │ │ │ + resp.priv = OpenLayers.Request.GET({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ + params: options.params, │ │ │ │ + headers: options.headers │ │ │ │ + }); │ │ │ │ } │ │ │ │ + return resp; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onNextChange │ │ │ │ - * Called when the next history stack changes. │ │ │ │ + * Method: handleRead │ │ │ │ + * Individual callbacks are created for read, create and update, should │ │ │ │ + * a subclass need to override each one separately. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * state - {Object} An object representing the state to be restored │ │ │ │ - * if next is triggered again or null if no next states remain. │ │ │ │ - * length - {Integer} The number of remaining next states that can │ │ │ │ - * be restored. │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ + * the user callback. │ │ │ │ + * options - {Object} The user options passed to the read call. │ │ │ │ */ │ │ │ │ - onNextChange: function(state, length) { │ │ │ │ - if (state && !this.next.active) { │ │ │ │ - this.next.activate(); │ │ │ │ - } else if (!state && this.next.active) { │ │ │ │ - this.next.deactivate(); │ │ │ │ - } │ │ │ │ + handleRead: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Destroy the control. │ │ │ │ + * APIMethod: create │ │ │ │ + * Construct a request for writing newly created features. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ + * {<OpenLayers.Feature.Vector>} │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ + * object, whose "priv" property references the HTTP request, this │ │ │ │ + * object is also passed to the callback function when the request │ │ │ │ + * completes, its "features" property is then populated with the │ │ │ │ + * the features received from the server. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this); │ │ │ │ - this.previous.destroy(); │ │ │ │ - this.next.destroy(); │ │ │ │ - this.deactivate(); │ │ │ │ - for (var prop in this) { │ │ │ │ - this[prop] = null; │ │ │ │ - } │ │ │ │ + create: function(features, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: features, │ │ │ │ + requestType: "create" │ │ │ │ + }); │ │ │ │ + │ │ │ │ + resp.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleCreate, resp, options), │ │ │ │ + headers: options.headers, │ │ │ │ + data: this.format.write(features) │ │ │ │ + }); │ │ │ │ + │ │ │ │ + return resp; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the control and <previous> and <next> child │ │ │ │ - * controls. │ │ │ │ + /** │ │ │ │ + * Method: handleCreate │ │ │ │ + * Called the the request issued by <create> is complete. May be overridden │ │ │ │ + * by subclasses. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ + * any user callback. │ │ │ │ + * options - {Object} The user options passed to the create call. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - this.map = map; │ │ │ │ - this.next.setMap(map); │ │ │ │ - this.previous.setMap(map); │ │ │ │ + handleCreate: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ - * Called when the control is added to the map. │ │ │ │ + * APIMethod: update │ │ │ │ + * Construct a request updating modified feature. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ + * object, whose "priv" property references the HTTP request, this │ │ │ │ + * object is also passed to the callback function when the request │ │ │ │ + * completes, its "features" property is then populated with the │ │ │ │ + * the feature received from the server. │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - this.next.draw(); │ │ │ │ - this.previous.draw(); │ │ │ │ + update: function(feature, options) { │ │ │ │ + options = options || {}; │ │ │ │ + var url = options.url || │ │ │ │ + feature.url || │ │ │ │ + this.options.url + "/" + feature.fid; │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: feature, │ │ │ │ + requestType: "update" │ │ │ │ + }); │ │ │ │ + │ │ │ │ + var method = this.updateWithPOST ? "POST" : "PUT"; │ │ │ │ + resp.priv = OpenLayers.Request[method]({ │ │ │ │ + url: url, │ │ │ │ + callback: this.createCallback(this.handleUpdate, resp, options), │ │ │ │ + headers: options.headers, │ │ │ │ + data: this.format.write(feature) │ │ │ │ + }); │ │ │ │ + │ │ │ │ + return resp; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: previousTrigger │ │ │ │ - * Restore the previous state. If no items are in the previous history │ │ │ │ - * stack, this has no effect. │ │ │ │ + * Method: handleUpdate │ │ │ │ + * Called the the request issued by <update> is complete. May be overridden │ │ │ │ + * by subclasses. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Item representing state that was restored. Undefined if no │ │ │ │ - * items are in the previous history stack. │ │ │ │ + * Parameters: │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ + * any user callback. │ │ │ │ + * options - {Object} The user options passed to the update call. │ │ │ │ */ │ │ │ │ - previousTrigger: function() { │ │ │ │ - var current = this.previousStack.shift(); │ │ │ │ - var state = this.previousStack.shift(); │ │ │ │ - if (state != undefined) { │ │ │ │ - this.nextStack.unshift(current); │ │ │ │ - this.previousStack.unshift(state); │ │ │ │ - this.restoring = true; │ │ │ │ - this.restore(state); │ │ │ │ - this.restoring = false; │ │ │ │ - this.onNextChange(this.nextStack[0], this.nextStack.length); │ │ │ │ - this.onPreviousChange( │ │ │ │ - this.previousStack[1], this.previousStack.length - 1 │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - this.previousStack.unshift(current); │ │ │ │ - } │ │ │ │ - return state; │ │ │ │ + handleUpdate: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: nextTrigger │ │ │ │ - * Restore the next state. If no items are in the next history │ │ │ │ - * stack, this has no effect. The next history stack is populated │ │ │ │ - * as states are restored from the previous history stack. │ │ │ │ + * APIMethod: delete │ │ │ │ + * Construct a request deleting a removed feature. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} Item representing state that was restored. Undefined if no │ │ │ │ - * items are in the next history stack. │ │ │ │ + * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ + * object, whose "priv" property references the HTTP request, this │ │ │ │ + * object is also passed to the callback function when the request │ │ │ │ + * completes. │ │ │ │ */ │ │ │ │ - nextTrigger: function() { │ │ │ │ - var state = this.nextStack.shift(); │ │ │ │ - if (state != undefined) { │ │ │ │ - this.previousStack.unshift(state); │ │ │ │ - this.restoring = true; │ │ │ │ - this.restore(state); │ │ │ │ - this.restoring = false; │ │ │ │ - this.onNextChange(this.nextStack[0], this.nextStack.length); │ │ │ │ - this.onPreviousChange( │ │ │ │ - this.previousStack[1], this.previousStack.length - 1 │ │ │ │ - ); │ │ │ │ + "delete": function(feature, options) { │ │ │ │ + options = options || {}; │ │ │ │ + var url = options.url || │ │ │ │ + feature.url || │ │ │ │ + this.options.url + "/" + feature.fid; │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: feature, │ │ │ │ + requestType: "delete" │ │ │ │ + }); │ │ │ │ + │ │ │ │ + var method = this.deleteWithPOST ? "POST" : "DELETE"; │ │ │ │ + var requestOptions = { │ │ │ │ + url: url, │ │ │ │ + callback: this.createCallback(this.handleDelete, resp, options), │ │ │ │ + headers: options.headers │ │ │ │ + }; │ │ │ │ + if (this.deleteWithPOST) { │ │ │ │ + requestOptions.data = this.format.write(feature); │ │ │ │ } │ │ │ │ - return state; │ │ │ │ - }, │ │ │ │ + resp.priv = OpenLayers.Request[method](requestOptions); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: clear │ │ │ │ - * Clear history. │ │ │ │ - */ │ │ │ │ - clear: function() { │ │ │ │ - this.previousStack = []; │ │ │ │ - this.previous.deactivate(); │ │ │ │ - this.nextStack = []; │ │ │ │ - this.next.deactivate(); │ │ │ │ + return resp; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getState │ │ │ │ - * Get the current state and return it. │ │ │ │ + * Method: handleDelete │ │ │ │ + * Called the the request issued by <delete> is complete. May be overridden │ │ │ │ + * by subclasses. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object representing the current state. │ │ │ │ + * Parameters: │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ + * any user callback. │ │ │ │ + * options - {Object} The user options passed to the delete call. │ │ │ │ */ │ │ │ │ - getState: function() { │ │ │ │ - return { │ │ │ │ - center: this.map.getCenter(), │ │ │ │ - resolution: this.map.getResolution(), │ │ │ │ - projection: this.map.getProjectionObject(), │ │ │ │ - units: this.map.getProjectionObject().getUnits() || │ │ │ │ - this.map.units || this.map.baseLayer.units │ │ │ │ - }; │ │ │ │ + handleDelete: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: restore │ │ │ │ - * Update the state with the given object. │ │ │ │ + * Method: handleResponse │ │ │ │ + * Called by CRUD specific handlers. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * state - {Object} An object representing the state to restore. │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ + * any user callback. │ │ │ │ + * options - {Object} The user options passed to the create, read, update, │ │ │ │ + * or delete call. │ │ │ │ */ │ │ │ │ - restore: function(state) { │ │ │ │ - var center, zoom; │ │ │ │ - if (this.map.getProjectionObject() == state.projection) { │ │ │ │ - zoom = this.map.getZoomForResolution(state.resolution); │ │ │ │ - center = state.center; │ │ │ │ - } else { │ │ │ │ - center = state.center.clone(); │ │ │ │ - center.transform(state.projection, this.map.getProjectionObject()); │ │ │ │ - var sourceUnits = state.units; │ │ │ │ - var targetUnits = this.map.getProjectionObject().getUnits() || │ │ │ │ - this.map.units || this.map.baseLayer.units; │ │ │ │ - var resolutionFactor = sourceUnits && targetUnits ? │ │ │ │ - OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1; │ │ │ │ - zoom = this.map.getZoomForResolution(resolutionFactor * state.resolution); │ │ │ │ + handleResponse: function(resp, options) { │ │ │ │ + var request = resp.priv; │ │ │ │ + if (options.callback) { │ │ │ │ + if (request.status >= 200 && request.status < 300) { │ │ │ │ + // success │ │ │ │ + if (resp.requestType != "delete") { │ │ │ │ + resp.features = this.parseFeatures(request); │ │ │ │ + } │ │ │ │ + resp.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ + } else { │ │ │ │ + // failure │ │ │ │ + resp.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + } │ │ │ │ + options.callback.call(options.scope, resp); │ │ │ │ } │ │ │ │ - this.map.setCenter(center, zoom); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setListeners │ │ │ │ - * Sets functions to be registered in the listeners object. │ │ │ │ + * Method: parseFeatures │ │ │ │ + * Read HTTP response body and return features. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * request - {XMLHttpRequest} The request object │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ + * {<OpenLayers.Feature.Vector>} Array of features or a single feature. │ │ │ │ */ │ │ │ │ - setListeners: function() { │ │ │ │ - this.listeners = {}; │ │ │ │ - for (var type in this.registry) { │ │ │ │ - this.listeners[type] = OpenLayers.Function.bind(function() { │ │ │ │ - if (!this.restoring) { │ │ │ │ - var state = this.registry[type].apply(this, arguments); │ │ │ │ - this.previousStack.unshift(state); │ │ │ │ - if (this.previousStack.length > 1) { │ │ │ │ - this.onPreviousChange( │ │ │ │ - this.previousStack[1], this.previousStack.length - 1 │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (this.previousStack.length > (this.limit + 1)) { │ │ │ │ - this.previousStack.pop(); │ │ │ │ - } │ │ │ │ - if (this.nextStack.length > 0) { │ │ │ │ - this.nextStack = []; │ │ │ │ - this.onNextChange(null, 0); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return true; │ │ │ │ - }, this); │ │ │ │ + parseFeatures: function(request) { │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText; │ │ │ │ + } │ │ │ │ + if (!doc || doc.length <= 0) { │ │ │ │ + return null; │ │ │ │ } │ │ │ │ + return this.format.read(doc); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Activate the control. This registers any listeners. │ │ │ │ + * APIMethod: commit │ │ │ │ + * Iterate over each feature and take action based on the feature state. │ │ │ │ + * Possible actions are create, update and delete. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array({<OpenLayers.Feature.Vector>})} │ │ │ │ + * options - {Object} Optional object for setting up intermediate commit │ │ │ │ + * callbacks. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * create - {Object} Optional object to be passed to the <create> method. │ │ │ │ + * update - {Object} Optional object to be passed to the <update> method. │ │ │ │ + * delete - {Object} Optional object to be passed to the <delete> method. │ │ │ │ + * callback - {Function} Optional function to be called when the commit │ │ │ │ + * is complete. │ │ │ │ + * scope - {Object} Optional object to be set as the scope of the callback. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Control successfully activated. │ │ │ │ + * {Array(<OpenLayers.Protocol.Response>)} An array of response objects, │ │ │ │ + * one per request made to the server, each object's "priv" property │ │ │ │ + * references the corresponding HTTP request. │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = false; │ │ │ │ - if (this.map) { │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this)) { │ │ │ │ - if (this.listeners == null) { │ │ │ │ - this.setListeners(); │ │ │ │ - } │ │ │ │ - for (var type in this.listeners) { │ │ │ │ - this.map.events.register(type, this, this.listeners[type]); │ │ │ │ - } │ │ │ │ - activated = true; │ │ │ │ - if (this.previousStack.length == 0) { │ │ │ │ - this.initStack(); │ │ │ │ + commit: function(features, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var resp = [], │ │ │ │ + nResponses = 0; │ │ │ │ + │ │ │ │ + // Divide up features before issuing any requests. This properly │ │ │ │ + // counts requests in the event that any responses come in before │ │ │ │ + // all requests have been issued. │ │ │ │ + var types = {}; │ │ │ │ + types[OpenLayers.State.INSERT] = []; │ │ │ │ + types[OpenLayers.State.UPDATE] = []; │ │ │ │ + types[OpenLayers.State.DELETE] = []; │ │ │ │ + var feature, list, requestFeatures = []; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + feature = features[i]; │ │ │ │ + list = types[feature.state]; │ │ │ │ + if (list) { │ │ │ │ + list.push(feature); │ │ │ │ + requestFeatures.push(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + // tally up number of requests │ │ │ │ + var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + │ │ │ │ + types[OpenLayers.State.UPDATE].length + │ │ │ │ + types[OpenLayers.State.DELETE].length; │ │ │ │ + │ │ │ │ + // This response will be sent to the final callback after all the others │ │ │ │ + // have been fired. │ │ │ │ + var success = true; │ │ │ │ + var finalResponse = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: requestFeatures │ │ │ │ + }); │ │ │ │ + │ │ │ │ + function insertCallback(response) { │ │ │ │ + var len = response.features ? response.features.length : 0; │ │ │ │ + var fids = new Array(len); │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ + fids[i] = response.features[i].fid; │ │ │ │ + } │ │ │ │ + finalResponse.insertIds = fids; │ │ │ │ + callback.apply(this, [response]); │ │ │ │ + } │ │ │ │ + │ │ │ │ + function callback(response) { │ │ │ │ + this.callUserCallback(response, options); │ │ │ │ + success = success && response.success(); │ │ │ │ + nResponses++; │ │ │ │ + if (nResponses >= nRequests) { │ │ │ │ + if (options.callback) { │ │ │ │ + finalResponse.code = success ? │ │ │ │ + OpenLayers.Protocol.Response.SUCCESS : │ │ │ │ + OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + options.callback.apply(options.scope, [finalResponse]); │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ - return activated; │ │ │ │ + │ │ │ │ + // start issuing requests │ │ │ │ + var queue = types[OpenLayers.State.INSERT]; │ │ │ │ + if (queue.length > 0) { │ │ │ │ + resp.push(this.create( │ │ │ │ + queue, OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: insertCallback, │ │ │ │ + scope: this │ │ │ │ + }, options.create) │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + queue = types[OpenLayers.State.UPDATE]; │ │ │ │ + for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ + resp.push(this.update( │ │ │ │ + queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: callback, │ │ │ │ + scope: this │ │ │ │ + }, options.update))); │ │ │ │ + } │ │ │ │ + queue = types[OpenLayers.State.DELETE]; │ │ │ │ + for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ + resp.push(this["delete"]( │ │ │ │ + queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: callback, │ │ │ │ + scope: this │ │ │ │ + }, options["delete"]))); │ │ │ │ + } │ │ │ │ + return resp; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: initStack │ │ │ │ - * Called after the control is activated if the previous history stack is │ │ │ │ - * empty. │ │ │ │ + * APIMethod: abort │ │ │ │ + * Abort an ongoing request, the response object passed to │ │ │ │ + * this method must come from this HTTP protocol (as a result │ │ │ │ + * of a create, read, update, delete or commit operation). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} │ │ │ │ */ │ │ │ │ - initStack: function() { │ │ │ │ - if (this.map.getCenter()) { │ │ │ │ - this.listeners.moveend(); │ │ │ │ + abort: function(response) { │ │ │ │ + if (response) { │ │ │ │ + response.priv.abort(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the control. This unregisters any listeners. │ │ │ │ + * Method: callUserCallback │ │ │ │ + * This method is used from within the commit method each time an │ │ │ │ + * an HTTP response is received from the server, it is responsible │ │ │ │ + * for calling the user-supplied callbacks. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Control successfully deactivated. │ │ │ │ + * Parameters: │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} │ │ │ │ + * options - {Object} The map of options passed to the commit call. │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (this.map) { │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this)) { │ │ │ │ - for (var type in this.listeners) { │ │ │ │ - this.map.events.unregister( │ │ │ │ - type, this, this.listeners[type] │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (this.clearOnDeactivate) { │ │ │ │ - this.clear(); │ │ │ │ - } │ │ │ │ - deactivated = true; │ │ │ │ - } │ │ │ │ + callUserCallback: function(resp, options) { │ │ │ │ + var opt = options[resp.requestType]; │ │ │ │ + if (opt && opt.callback) { │ │ │ │ + opt.callback.call(opt.scope, resp); │ │ │ │ } │ │ │ │ - return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.NavigationHistory" │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.HTTP" │ │ │ │ }); │ │ │ │ - │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/Geolocate.js │ │ │ │ + OpenLayers/Protocol/Script.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Geometry/Point.js │ │ │ │ - * @requires OpenLayers/Projection.js │ │ │ │ + * @requires OpenLayers/Protocol.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Format/GeoJSON.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.Geolocate │ │ │ │ - * The Geolocate control wraps w3c geolocation API into control that can be │ │ │ │ - * bound to a map, and generate events on location update │ │ │ │ - * │ │ │ │ - * To use this control requires to load the proj4js library if the projection │ │ │ │ - * of the map is not EPSG:4326 or EPSG:900913. │ │ │ │ + * if application uses the query string, for example, for BBOX parameters, │ │ │ │ + * OpenLayers/Format/QueryStringFilter.js should be included in the build config file │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Protocol.Script │ │ │ │ + * A basic Script protocol for vector layers. Create a new instance with the │ │ │ │ + * <OpenLayers.Protocol.Script> constructor. A script protocol is used to │ │ │ │ + * get around the same origin policy. It works with services that return │ │ │ │ + * JSONP - that is, JSON wrapped in a client-specified callback. The │ │ │ │ + * protocol handles fetching and parsing of feature data and sends parsed │ │ │ │ + * features to the <callback> configured with the protocol. The protocol │ │ │ │ + * expects features serialized as GeoJSON by default, but can be configured │ │ │ │ + * to work with other formats by setting the <format> property. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Protocol> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.Geolocate = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ +OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * locationupdated - Triggered when browser return a new position. Listeners will │ │ │ │ - * receive an object with a 'position' property which is the browser.geolocation.position │ │ │ │ - * native object, as well as a 'point' property which is the location transformed in the │ │ │ │ - * current map projection. │ │ │ │ - * locationfailed - Triggered when geolocation has failed │ │ │ │ - * locationuncapable - Triggered when control is activated on a browser │ │ │ │ - * which doesn't support geolocation │ │ │ │ + /** │ │ │ │ + * APIProperty: url │ │ │ │ + * {String} Service URL. The service is expected to return serialized │ │ │ │ + * features wrapped in a named callback (where the callback name is │ │ │ │ + * generated by this protocol). │ │ │ │ + * Read-only, set through the options passed to the constructor. │ │ │ │ */ │ │ │ │ + url: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: geolocation │ │ │ │ - * {Object} The geolocation engine, as a property to be possibly mocked. │ │ │ │ - * This is set lazily to avoid a memory leak in IE9. │ │ │ │ + * APIProperty: params │ │ │ │ + * {Object} Query string parameters to be appended to the URL. │ │ │ │ + * Read-only, set through the options passed to the constructor. │ │ │ │ + * Example: {maxFeatures: 50} │ │ │ │ */ │ │ │ │ - geolocation: null, │ │ │ │ + params: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: available │ │ │ │ - * {Boolean} The navigator.geolocation object is available. │ │ │ │ + * APIProperty: callback │ │ │ │ + * {Object} Function to be called when the <read> operation completes. │ │ │ │ */ │ │ │ │ - available: ('geolocation' in navigator), │ │ │ │ + callback: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: bind │ │ │ │ - * {Boolean} If true, map center will be set on location update. │ │ │ │ + * APIProperty: callbackTemplate │ │ │ │ + * {String} Template for creating a unique callback function name │ │ │ │ + * for the registry. Should include ${id}. The ${id} variable will be │ │ │ │ + * replaced with a string identifier prefixed with a "c" (e.g. c1, c2). │ │ │ │ + * Default is "OpenLayers.Protocol.Script.registry.${id}". │ │ │ │ */ │ │ │ │ - bind: true, │ │ │ │ + callbackTemplate: "OpenLayers.Protocol.Script.registry.${id}", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: watch │ │ │ │ - * {Boolean} If true, position will be update regularly. │ │ │ │ + * APIProperty: callbackKey │ │ │ │ + * {String} The name of the query string parameter that the service │ │ │ │ + * recognizes as the callback identifier. Default is "callback". │ │ │ │ + * This key is used to generate the URL for the script. For example │ │ │ │ + * setting <callbackKey> to "myCallback" would result in a URL like │ │ │ │ + * http://example.com/?myCallback=... │ │ │ │ */ │ │ │ │ - watch: false, │ │ │ │ + callbackKey: "callback", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: geolocationOptions │ │ │ │ - * {Object} Options to pass to the navigator's geolocation API. See │ │ │ │ - * <http://dev.w3.org/geo/api/spec-source.html>. No specific │ │ │ │ - * option is passed to the geolocation API by default. │ │ │ │ + * APIProperty: callbackPrefix │ │ │ │ + * {String} Where a service requires that the callback query string │ │ │ │ + * parameter value is prefixed by some string, this value may be set. │ │ │ │ + * For example, setting <callbackPrefix> to "foo:" would result in a │ │ │ │ + * URL like http://example.com/?callback=foo:... Default is "". │ │ │ │ */ │ │ │ │ - geolocationOptions: null, │ │ │ │ + callbackPrefix: "", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.Geolocate │ │ │ │ - * Create a new control to deal with browser geolocation API │ │ │ │ - * │ │ │ │ + * APIProperty: scope │ │ │ │ + * {Object} Optional ``this`` object for the callback. Read-only, set │ │ │ │ + * through the options passed to the constructor. │ │ │ │ */ │ │ │ │ + scope: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ + * APIProperty: format │ │ │ │ + * {<OpenLayers.Format>} Format for parsing features. Default is an │ │ │ │ + * <OpenLayers.Format.GeoJSON> format. If an alternative is provided, │ │ │ │ + * the format's read method must take an object and return an array │ │ │ │ + * of features. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + format: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: activate │ │ │ │ - * Activates the control. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The control was effectively activated. │ │ │ │ + * Property: pendingRequests │ │ │ │ + * {Object} References all pending requests. Property names are script │ │ │ │ + * identifiers and property values are script elements. │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - if (this.available && !this.geolocation) { │ │ │ │ - // set lazily to avoid IE9 memory leak │ │ │ │ - this.geolocation = navigator.geolocation; │ │ │ │ - } │ │ │ │ - if (!this.geolocation) { │ │ │ │ - this.events.triggerEvent("locationuncapable"); │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - if (this.watch) { │ │ │ │ - this.watchId = this.geolocation.watchPosition( │ │ │ │ - OpenLayers.Function.bind(this.geolocate, this), │ │ │ │ - OpenLayers.Function.bind(this.failure, this), │ │ │ │ - this.geolocationOptions │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - this.getCurrentLocation(); │ │ │ │ - } │ │ │ │ - return true; │ │ │ │ - } │ │ │ │ - return false; │ │ │ │ - }, │ │ │ │ + pendingRequests: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Deactivates the control. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The control was effectively deactivated. │ │ │ │ + * APIProperty: srsInBBOX │ │ │ │ + * {Boolean} Include the SRS identifier in BBOX query string parameter. │ │ │ │ + * Setting this property has no effect if a custom filterToParams method │ │ │ │ + * is provided. Default is false. If true and the layer has a │ │ │ │ + * projection object set, any BBOX filter will be serialized with a │ │ │ │ + * fifth item identifying the projection. │ │ │ │ + * E.g. bbox=-1000,-1000,1000,1000,EPSG:900913 │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active && this.watchId !== null) { │ │ │ │ - this.geolocation.clearWatch(this.watchId); │ │ │ │ - } │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply( │ │ │ │ - this, arguments │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ + srsInBBOX: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: geolocate │ │ │ │ - * Activates the control. │ │ │ │ + * Constructor: OpenLayers.Protocol.Script │ │ │ │ + * A class for giving layers generic Script protocol. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ * │ │ │ │ + * Valid options include: │ │ │ │ + * url - {String} │ │ │ │ + * params - {Object} │ │ │ │ + * callback - {Function} │ │ │ │ + * scope - {Object} │ │ │ │ */ │ │ │ │ - geolocate: function(position) { │ │ │ │ - var center = new OpenLayers.LonLat( │ │ │ │ - position.coords.longitude, │ │ │ │ - position.coords.latitude │ │ │ │ - ).transform( │ │ │ │ - new OpenLayers.Projection("EPSG:4326"), │ │ │ │ - this.map.getProjectionObject() │ │ │ │ - ); │ │ │ │ - if (this.bind) { │ │ │ │ - this.map.setCenter(center); │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + this.params = {}; │ │ │ │ + this.pendingRequests = {}; │ │ │ │ + OpenLayers.Protocol.prototype.initialize.apply(this, arguments); │ │ │ │ + if (!this.format) { │ │ │ │ + this.format = new OpenLayers.Format.GeoJSON(); │ │ │ │ } │ │ │ │ - this.events.triggerEvent("locationupdated", { │ │ │ │ - position: position, │ │ │ │ - point: new OpenLayers.Geometry.Point( │ │ │ │ - center.lon, center.lat │ │ │ │ - ) │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getCurrentLocation │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Returns true if a event will be fired (successfull │ │ │ │ - * registration) │ │ │ │ - */ │ │ │ │ - getCurrentLocation: function() { │ │ │ │ - if (!this.active || this.watch) { │ │ │ │ - return false; │ │ │ │ + if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { │ │ │ │ + var format = new OpenLayers.Format.QueryStringFilter({ │ │ │ │ + srsInBBOX: this.srsInBBOX │ │ │ │ + }); │ │ │ │ + this.filterToParams = function(filter, params) { │ │ │ │ + return format.write(filter, params); │ │ │ │ + }; │ │ │ │ } │ │ │ │ - this.geolocation.getCurrentPosition( │ │ │ │ - OpenLayers.Function.bind(this.geolocate, this), │ │ │ │ - OpenLayers.Function.bind(this.failure, this), │ │ │ │ - this.geolocationOptions │ │ │ │ - ); │ │ │ │ - return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: failure │ │ │ │ - * method called on browser's geolocation failure │ │ │ │ + * APIMethod: read │ │ │ │ + * Construct a request for reading new features. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * url - {String} Url for the request. │ │ │ │ + * params - {Object} Parameters to get serialized as a query string. │ │ │ │ + * filter - {<OpenLayers.Filter>} Filter to get serialized as a │ │ │ │ + * query string. │ │ │ │ * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property │ │ │ │ + * references the injected script. This object is also passed to the │ │ │ │ + * callback function when the request completes, its "features" property │ │ │ │ + * is then populated with the features received from the server. │ │ │ │ */ │ │ │ │ - failure: function(error) { │ │ │ │ - this.events.triggerEvent("locationfailed", { │ │ │ │ - error: error │ │ │ │ + read: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + options.params = OpenLayers.Util.applyDefaults( │ │ │ │ + options.params, this.options.params │ │ │ │ + ); │ │ │ │ + if (options.filter && this.filterToParams) { │ │ │ │ + options.params = this.filterToParams( │ │ │ │ + options.filter, options.params │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + var response = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "read" │ │ │ │ }); │ │ │ │ + var request = this.createRequest( │ │ │ │ + options.url, │ │ │ │ + options.params, │ │ │ │ + OpenLayers.Function.bind(function(data) { │ │ │ │ + response.data = data; │ │ │ │ + this.handleRead(response, options); │ │ │ │ + }, this) │ │ │ │ + ); │ │ │ │ + response.priv = request; │ │ │ │ + return response; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Geolocate" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/PanZoom.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Events/buttonclick.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.PanZoom │ │ │ │ - * The PanZoom is a visible control, composed of a │ │ │ │ - * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By │ │ │ │ - * default it is drawn in the upper left corner of the map. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: slideFactor │ │ │ │ - * {Integer} Number of pixels by which we'll pan the map in any direction │ │ │ │ - * on clicking the arrow buttons. If you want to pan by some ratio │ │ │ │ - * of the map dimensions, use <slideRatio> instead. │ │ │ │ - */ │ │ │ │ - slideFactor: 50, │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIProperty: slideRatio │ │ │ │ - * {Number} The fraction of map width/height by which we'll pan the map │ │ │ │ - * on clicking the arrow buttons. Default is null. If set, will │ │ │ │ - * override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up │ │ │ │ - * button will pan up half the map height. │ │ │ │ + * APIMethod: filterToParams │ │ │ │ + * Optional method to translate an <OpenLayers.Filter> object into an object │ │ │ │ + * that can be serialized as request query string provided. If a custom │ │ │ │ + * method is not provided, any filter will not be serialized. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * filter - {<OpenLayers.Filter>} filter to convert. │ │ │ │ + * params - {Object} The parameters object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} The resulting parameters object. │ │ │ │ */ │ │ │ │ - slideRatio: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: buttons │ │ │ │ - * {Array(DOMElement)} Array of Button Divs │ │ │ │ + * Method: createRequest │ │ │ │ + * Issues a request for features by creating injecting a script in the │ │ │ │ + * document head. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * url - {String} Service URL. │ │ │ │ + * params - {Object} Query string parameters. │ │ │ │ + * callback - {Function} Callback to be called with resulting data. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {HTMLScriptElement} The script pending execution. │ │ │ │ */ │ │ │ │ - buttons: null, │ │ │ │ + createRequest: function(url, params, callback) { │ │ │ │ + var id = OpenLayers.Protocol.Script.register(callback); │ │ │ │ + var name = OpenLayers.String.format(this.callbackTemplate, { │ │ │ │ + id: id │ │ │ │ + }); │ │ │ │ + params = OpenLayers.Util.extend({}, params); │ │ │ │ + params[this.callbackKey] = this.callbackPrefix + name; │ │ │ │ + url = OpenLayers.Util.urlAppend( │ │ │ │ + url, OpenLayers.Util.getParameterString(params) │ │ │ │ + ); │ │ │ │ + var script = document.createElement("script"); │ │ │ │ + script.type = "text/javascript"; │ │ │ │ + script.src = url; │ │ │ │ + script.id = "OpenLayers_Protocol_Script_" + id; │ │ │ │ + this.pendingRequests[script.id] = script; │ │ │ │ + var head = document.getElementsByTagName("head")[0]; │ │ │ │ + head.appendChild(script); │ │ │ │ + return script; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: position │ │ │ │ - * {<OpenLayers.Pixel>} │ │ │ │ + * Method: destroyRequest │ │ │ │ + * Remove a script node associated with a response from the document. Also │ │ │ │ + * unregisters the callback and removes the script from the │ │ │ │ + * <pendingRequests> object. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * script - {HTMLScriptElement} │ │ │ │ */ │ │ │ │ - position: null, │ │ │ │ + destroyRequest: function(script) { │ │ │ │ + OpenLayers.Protocol.Script.unregister(script.id.split("_").pop()); │ │ │ │ + delete this.pendingRequests[script.id]; │ │ │ │ + if (script.parentNode) { │ │ │ │ + script.parentNode.removeChild(script); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.PanZoom │ │ │ │ - * │ │ │ │ + * Method: handleRead │ │ │ │ + * Individual callbacks are created for read, create and update, should │ │ │ │ + * a subclass need to override each one separately. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ + * the user callback. │ │ │ │ + * options - {Object} The user options passed to the read call. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X, │ │ │ │ - OpenLayers.Control.PanZoom.Y); │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ + handleRead: function(response, options) { │ │ │ │ + this.handleResponse(response, options); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ + * Method: handleResponse │ │ │ │ + * Called by CRUD specific handlers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ + * any user callback. │ │ │ │ + * options - {Object} The user options passed to the create, read, update, │ │ │ │ + * or delete call. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ + handleResponse: function(response, options) { │ │ │ │ + if (options.callback) { │ │ │ │ + if (response.data) { │ │ │ │ + response.features = this.parseFeatures(response.data); │ │ │ │ + response.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ + } else { │ │ │ │ + response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + } │ │ │ │ + this.destroyRequest(response.priv); │ │ │ │ + options.callback.call(options.scope, response); │ │ │ │ } │ │ │ │ - this.removeButtons(); │ │ │ │ - this.buttons = null; │ │ │ │ - this.position = null; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ + /** │ │ │ │ + * Method: parseFeatures │ │ │ │ + * Read Script response body and return features. │ │ │ │ * │ │ │ │ - * Properties: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * Parameters: │ │ │ │ + * data - {Object} The data sent to the callback function by the server. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ + * {<OpenLayers.Feature.Vector>} Array of features or a single feature. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - this.map.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ + parseFeatures: function(data) { │ │ │ │ + return this.format.read(data); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ + * APIMethod: abort │ │ │ │ + * Abort an ongoing request. If no response is provided, all pending │ │ │ │ + * requests will be aborted. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A reference to the container div for the PanZoom control. │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} The response object returned │ │ │ │ + * from a <read> request. │ │ │ │ */ │ │ │ │ - draw: function(px) { │ │ │ │ - // initialize our internal div │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - px = this.position; │ │ │ │ + abort: function(response) { │ │ │ │ + if (response) { │ │ │ │ + this.destroyRequest(response.priv); │ │ │ │ + } else { │ │ │ │ + for (var key in this.pendingRequests) { │ │ │ │ + this.destroyRequest(this.pendingRequests[key]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // place the controls │ │ │ │ - this.buttons = []; │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up the protocol. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.abort(); │ │ │ │ + delete this.params; │ │ │ │ + delete this.format; │ │ │ │ + OpenLayers.Protocol.prototype.destroy.apply(this); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var sz = { │ │ │ │ - w: 18, │ │ │ │ - h: 18 │ │ │ │ - }; │ │ │ │ - var centered = new OpenLayers.Pixel(px.x + sz.w / 2, px.y); │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.Script" │ │ │ │ +}); │ │ │ │ │ │ │ │ - this._addButton("panup", "north-mini.png", centered, sz); │ │ │ │ - px.y = centered.y + sz.h; │ │ │ │ - this._addButton("panleft", "west-mini.png", px, sz); │ │ │ │ - this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz); │ │ │ │ - this._addButton("pandown", "south-mini.png", │ │ │ │ - centered.add(0, sz.h * 2), sz); │ │ │ │ - this._addButton("zoomin", "zoom-plus-mini.png", │ │ │ │ - centered.add(0, sz.h * 3 + 5), sz); │ │ │ │ - this._addButton("zoomworld", "zoom-world-mini.png", │ │ │ │ - centered.add(0, sz.h * 4 + 5), sz); │ │ │ │ - this._addButton("zoomout", "zoom-minus-mini.png", │ │ │ │ - centered.add(0, sz.h * 5 + 5), sz); │ │ │ │ - return this.div; │ │ │ │ - }, │ │ │ │ +(function() { │ │ │ │ + var o = OpenLayers.Protocol.Script; │ │ │ │ + var counter = 0; │ │ │ │ + o.registry = {}; │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: _addButton │ │ │ │ - * │ │ │ │ + * Function: OpenLayers.Protocol.Script.register │ │ │ │ + * Register a callback for a newly created script. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * id - {String} │ │ │ │ - * img - {String} │ │ │ │ - * xy - {<OpenLayers.Pixel>} │ │ │ │ - * sz - {<OpenLayers.Size>} │ │ │ │ - * │ │ │ │ + * callback - {Function} The callback to be executed when the newly added │ │ │ │ + * script loads. This callback will be called with a single argument │ │ │ │ + * that is the JSON returned by the service. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the │ │ │ │ - * image of the button, and has all the proper event handlers set. │ │ │ │ - */ │ │ │ │ - _addButton: function(id, img, xy, sz) { │ │ │ │ - var imgLocation = OpenLayers.Util.getImageLocation(img); │ │ │ │ - var btn = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ - this.id + "_" + id, │ │ │ │ - xy, sz, imgLocation, "absolute"); │ │ │ │ - btn.style.cursor = "pointer"; │ │ │ │ - //we want to add the outer div │ │ │ │ - this.div.appendChild(btn); │ │ │ │ - btn.action = id; │ │ │ │ - btn.className = "olButton"; │ │ │ │ - │ │ │ │ - //we want to remember/reference the outer div │ │ │ │ - this.buttons.push(btn); │ │ │ │ - return btn; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: _removeButton │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * btn - {Object} │ │ │ │ - */ │ │ │ │ - _removeButton: function(btn) { │ │ │ │ - this.div.removeChild(btn); │ │ │ │ - OpenLayers.Util.removeItem(this.buttons, btn); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: removeButtons │ │ │ │ + * {Number} An identifier for retrieving the registered callback. │ │ │ │ */ │ │ │ │ - removeButtons: function() { │ │ │ │ - for (var i = this.buttons.length - 1; i >= 0; --i) { │ │ │ │ - this._removeButton(this.buttons[i]); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + o.register = function(callback) { │ │ │ │ + var id = "c" + (++counter); │ │ │ │ + o.registry[id] = function() { │ │ │ │ + callback.apply(this, arguments); │ │ │ │ + }; │ │ │ │ + return id; │ │ │ │ + }; │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onButtonClick │ │ │ │ + * Function: OpenLayers.Protocol.Script.unregister │ │ │ │ + * Unregister a callback previously registered with the register function. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * id - {Number} The identifer returned by the register function. │ │ │ │ */ │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - var btn = evt.buttonElement; │ │ │ │ - switch (btn.action) { │ │ │ │ - case "panup": │ │ │ │ - this.map.pan(0, -this.getSlideFactor("h")); │ │ │ │ - break; │ │ │ │ - case "pandown": │ │ │ │ - this.map.pan(0, this.getSlideFactor("h")); │ │ │ │ - break; │ │ │ │ - case "panleft": │ │ │ │ - this.map.pan(-this.getSlideFactor("w"), 0); │ │ │ │ - break; │ │ │ │ - case "panright": │ │ │ │ - this.map.pan(this.getSlideFactor("w"), 0); │ │ │ │ - break; │ │ │ │ - case "zoomin": │ │ │ │ - this.map.zoomIn(); │ │ │ │ - break; │ │ │ │ - case "zoomout": │ │ │ │ - this.map.zoomOut(); │ │ │ │ - break; │ │ │ │ - case "zoomworld": │ │ │ │ - this.map.zoomToMaxExtent(); │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + o.unregister = function(id) { │ │ │ │ + delete o.registry[id]; │ │ │ │ + }; │ │ │ │ +})(); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Protocol/SOS.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getSlideFactor │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * dim - {String} "w" or "h" (for width or height). │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Number} The slide factor for panning in the requested direction. │ │ │ │ - */ │ │ │ │ - getSlideFactor: function(dim) { │ │ │ │ - return this.slideRatio ? │ │ │ │ - this.map.getSize()[dim] * this.slideRatio : │ │ │ │ - this.slideFactor; │ │ │ │ - }, │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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.Control.PanZoom" │ │ │ │ -}); │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Protocol.js │ │ │ │ + */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: X │ │ │ │ - * {Integer} │ │ │ │ + * Function: OpenLayers.Protocol.SOS │ │ │ │ + * Used to create a versioned SOS protocol. Default version is 1.0.0. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol>} An SOS protocol for the given version. │ │ │ │ */ │ │ │ │ -OpenLayers.Control.PanZoom.X = 4; │ │ │ │ +OpenLayers.Protocol.SOS = function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults( │ │ │ │ + options, OpenLayers.Protocol.SOS.DEFAULTS │ │ │ │ + ); │ │ │ │ + var cls = OpenLayers.Protocol.SOS["v" + options.version.replace(/\./g, "_")]; │ │ │ │ + if (!cls) { │ │ │ │ + throw "Unsupported SOS version: " + options.version; │ │ │ │ + } │ │ │ │ + return new cls(options); │ │ │ │ +}; │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: Y │ │ │ │ - * {Integer} │ │ │ │ + * Constant: OpenLayers.Protocol.SOS.DEFAULTS │ │ │ │ */ │ │ │ │ -OpenLayers.Control.PanZoom.Y = 4; │ │ │ │ +OpenLayers.Protocol.SOS.DEFAULTS = { │ │ │ │ + "version": "1.0.0" │ │ │ │ +}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/PanZoomBar.js │ │ │ │ + OpenLayers/Protocol/WFS/v1.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control/PanZoom.js │ │ │ │ + * @requires OpenLayers/Protocol/WFS.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.PanZoomBar │ │ │ │ - * The PanZoomBar is a visible control composed of a │ │ │ │ - * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomBar>. │ │ │ │ - * By default it is displayed in the upper left corner of the map as 4 │ │ │ │ - * directional arrows above a vertical slider. │ │ │ │ + * Class: OpenLayers.Protocol.WFS.v1 │ │ │ │ + * Abstract class for for v1.0.0 and v1.1.0 protocol. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control.PanZoom> │ │ │ │ + * - <OpenLayers.Protocol> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, { │ │ │ │ +OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomStopWidth │ │ │ │ + /** │ │ │ │ + * Property: version │ │ │ │ + * {String} WFS version number. │ │ │ │ */ │ │ │ │ - zoomStopWidth: 18, │ │ │ │ + version: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomStopHeight │ │ │ │ + /** │ │ │ │ + * Property: srsName │ │ │ │ + * {String} Name of spatial reference system. Default is "EPSG:4326". │ │ │ │ */ │ │ │ │ - zoomStopHeight: 11, │ │ │ │ + srsName: "EPSG:4326", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: slider │ │ │ │ + /** │ │ │ │ + * Property: featureType │ │ │ │ + * {String} Local feature typeName. │ │ │ │ */ │ │ │ │ - slider: null, │ │ │ │ + featureType: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: sliderEvents │ │ │ │ - * {<OpenLayers.Events>} │ │ │ │ + /** │ │ │ │ + * Property: featureNS │ │ │ │ + * {String} Feature namespace. │ │ │ │ */ │ │ │ │ - sliderEvents: null, │ │ │ │ + featureNS: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: zoombarDiv │ │ │ │ - * {DOMElement} │ │ │ │ + /** │ │ │ │ + * Property: geometryName │ │ │ │ + * {String} Name of the geometry attribute for features. Default is │ │ │ │ + * "the_geom" for WFS <version> 1.0, and null for higher versions. │ │ │ │ */ │ │ │ │ - zoombarDiv: null, │ │ │ │ + geometryName: "the_geom", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomWorldIcon │ │ │ │ - * {Boolean} │ │ │ │ + /** │ │ │ │ + * Property: maxFeatures │ │ │ │ + * {Integer} Optional maximum number of features to retrieve. │ │ │ │ */ │ │ │ │ - zoomWorldIcon: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: panIcons │ │ │ │ - * {Boolean} Set this property to false not to display the pan icons. If │ │ │ │ - * false the zoom world icon is placed under the zoom bar. Defaults to │ │ │ │ - * true. │ │ │ │ + * Property: schema │ │ │ │ + * {String} Optional schema location that will be included in the │ │ │ │ + * schemaLocation attribute value. Note that the feature type schema │ │ │ │ + * is required for a strict XML validator (on transactions with an │ │ │ │ + * insert for example), but is *not* required by the WFS specification │ │ │ │ + * (since the server is supposed to know about feature type schemas). │ │ │ │ */ │ │ │ │ - panIcons: true, │ │ │ │ + schema: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: forceFixedZoomLevel │ │ │ │ - * {Boolean} Force a fixed zoom level even though the map has │ │ │ │ - * fractionalZoom │ │ │ │ + * Property: featurePrefix │ │ │ │ + * {String} Namespace alias for feature type. Default is "feature". │ │ │ │ */ │ │ │ │ - forceFixedZoomLevel: false, │ │ │ │ + featurePrefix: "feature", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: mouseDragStart │ │ │ │ - * {<OpenLayers.Pixel>} │ │ │ │ + * Property: formatOptions │ │ │ │ + * {Object} Optional options for the format. If a format is not provided, │ │ │ │ + * this property can be used to extend the default format options. │ │ │ │ */ │ │ │ │ - mouseDragStart: null, │ │ │ │ + formatOptions: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: deltaY │ │ │ │ - * {Number} The cumulative vertical pixel offset during a zoom bar drag. │ │ │ │ + /** │ │ │ │ + * Property: readFormat │ │ │ │ + * {<OpenLayers.Format>} For WFS requests it is possible to get a │ │ │ │ + * different output format than GML. In that case, we cannot parse │ │ │ │ + * the response with the default format (WFST) and we need a different │ │ │ │ + * format for reading. │ │ │ │ */ │ │ │ │ - deltaY: null, │ │ │ │ + readFormat: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: zoomStart │ │ │ │ - * {<OpenLayers.Pixel>} │ │ │ │ + * Property: readOptions │ │ │ │ + * {Object} Optional object to pass to format's read. │ │ │ │ */ │ │ │ │ - zoomStart: null, │ │ │ │ + readOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.PanZoomBar │ │ │ │ + * Constructor: OpenLayers.Protocol.WFS │ │ │ │ + * A class for giving layers WFS protocol. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ + * │ │ │ │ + * Valid options properties: │ │ │ │ + * url - {String} URL to send requests to (required). │ │ │ │ + * featureType - {String} Local (without prefix) feature typeName (required). │ │ │ │ + * featureNS - {String} Feature namespace (required, but can be autodetected │ │ │ │ + * during the first query if GML is used as readFormat and │ │ │ │ + * featurePrefix is provided and matches the prefix used by the server │ │ │ │ + * for this featureType). │ │ │ │ + * featurePrefix - {String} Feature namespace alias (optional - only used │ │ │ │ + * for writing if featureNS is provided). Default is 'feature'. │ │ │ │ + * geometryName - {String} Name of geometry attribute. The default is │ │ │ │ + * 'the_geom' for WFS <version> 1.0, and null for higher versions. If │ │ │ │ + * null, it will be set to the name of the first geometry found in the │ │ │ │ + * first read operation. │ │ │ │ + * multi - {Boolean} If set to true, geometries will be casted to Multi │ │ │ │ + * geometries before they are written in a transaction. No casting will │ │ │ │ + * be done when reading features. │ │ │ │ */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.initialize.apply(this, [options]); │ │ │ │ + if (!options.format) { │ │ │ │ + this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({ │ │ │ │ + version: this.version, │ │ │ │ + featureType: this.featureType, │ │ │ │ + featureNS: this.featureNS, │ │ │ │ + featurePrefix: this.featurePrefix, │ │ │ │ + geometryName: this.geometryName, │ │ │ │ + srsName: this.srsName, │ │ │ │ + schema: this.schema │ │ │ │ + }, this.formatOptions)); │ │ │ │ + } │ │ │ │ + if (!options.geometryName && parseFloat(this.format.version) > 1.0) { │ │ │ │ + this.setGeometryName(null); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ * APIMethod: destroy │ │ │ │ + * Clean up the protocol. │ │ │ │ */ │ │ │ │ destroy: function() { │ │ │ │ + if (this.options && !this.options.format) { │ │ │ │ + this.format.destroy(); │ │ │ │ + } │ │ │ │ + this.format = null; │ │ │ │ + OpenLayers.Protocol.prototype.destroy.apply(this); │ │ │ │ + }, │ │ │ │ │ │ │ │ - this._removeZoomBar(); │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Construct a request for reading new features. Since WFS splits the │ │ │ │ + * basic CRUD operations into GetFeature requests (for read) and │ │ │ │ + * Transactions (for all others), this method does not make use of the │ │ │ │ + * format's read method (that is only about reading transaction │ │ │ │ + * responses). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Options for the read operation, in addition to the │ │ │ │ + * options set on the instance (options set here will take precedence). │ │ │ │ + * │ │ │ │ + * To use a configured protocol to get e.g. a WFS hit count, applications │ │ │ │ + * could do the following: │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * protocol.read({ │ │ │ │ + * readOptions: {output: "object"}, │ │ │ │ + * resultType: "hits", │ │ │ │ + * maxFeatures: null, │ │ │ │ + * callback: function(resp) { │ │ │ │ + * // process resp.numberOfFeatures here │ │ │ │ + * } │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * To use a configured protocol to use WFS paging (if supported by the │ │ │ │ + * server), applications could do the following: │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * protocol.read({ │ │ │ │ + * startIndex: 0, │ │ │ │ + * count: 50 │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * To limit the attributes returned by the GetFeature request, applications │ │ │ │ + * can use the propertyNames option to specify the properties to include in │ │ │ │ + * the response: │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * protocol.read({ │ │ │ │ + * propertyNames: ["DURATION", "INTENSITY"] │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ + */ │ │ │ │ + read: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.options || {}); │ │ │ │ + var response = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "read" │ │ │ │ + }); │ │ │ │ │ │ │ │ - this.map.events.un({ │ │ │ │ - "changebaselayer": this.redraw, │ │ │ │ - "updatesize": this.redraw, │ │ │ │ - scope: this │ │ │ │ + var data = OpenLayers.Format.XML.prototype.write.apply( │ │ │ │ + this.format, [this.format.writeNode("wfs:GetFeature", options)] │ │ │ │ + ); │ │ │ │ + │ │ │ │ + response.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, response, options), │ │ │ │ + params: options.params, │ │ │ │ + headers: options.headers, │ │ │ │ + data: data │ │ │ │ }); │ │ │ │ │ │ │ │ - OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments); │ │ │ │ + return response; │ │ │ │ + }, │ │ │ │ │ │ │ │ - delete this.mouseDragStart; │ │ │ │ - delete this.zoomStart; │ │ │ │ + /** │ │ │ │ + * APIMethod: setFeatureType │ │ │ │ + * Change the feature type on the fly. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * featureType - {String} Local (without prefix) feature typeName. │ │ │ │ + */ │ │ │ │ + setFeatureType: function(featureType) { │ │ │ │ + this.featureType = featureType; │ │ │ │ + this.format.featureType = featureType; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setMap │ │ │ │ - * │ │ │ │ + * APIMethod: setGeometryName │ │ │ │ + * Sets the geometryName option after instantiation. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * geometryName - {String} Name of geometry attribute. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments); │ │ │ │ - this.map.events.on({ │ │ │ │ - "changebaselayer": this.redraw, │ │ │ │ - "updatesize": this.redraw, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + setGeometryName: function(geometryName) { │ │ │ │ + this.geometryName = geometryName; │ │ │ │ + this.format.geometryName = geometryName; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: redraw │ │ │ │ - * clear the div and start over. │ │ │ │ + /** │ │ │ │ + * Method: handleRead │ │ │ │ + * Deal with response from the read request. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} The response object to pass │ │ │ │ + * to the user callback. │ │ │ │ + * options - {Object} The user options passed to the read call. │ │ │ │ */ │ │ │ │ - redraw: function() { │ │ │ │ - if (this.div != null) { │ │ │ │ - this.removeButtons(); │ │ │ │ - this._removeZoomBar(); │ │ │ │ + handleRead: function(response, options) { │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + │ │ │ │ + if (options.callback) { │ │ │ │ + var request = response.priv; │ │ │ │ + if (request.status >= 200 && request.status < 300) { │ │ │ │ + // success │ │ │ │ + var result = this.parseResponse(request, options.readOptions); │ │ │ │ + if (result && result.success !== false) { │ │ │ │ + if (options.readOptions && options.readOptions.output == "object") { │ │ │ │ + OpenLayers.Util.extend(response, result); │ │ │ │ + } else { │ │ │ │ + response.features = result; │ │ │ │ + } │ │ │ │ + response.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ + } else { │ │ │ │ + // failure (service exception) │ │ │ │ + response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + response.error = result; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + // failure │ │ │ │ + response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + } │ │ │ │ + options.callback.call(options.scope, response); │ │ │ │ } │ │ │ │ - this.draw(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ + * Method: parseResponse │ │ │ │ + * Read HTTP response body and return features │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ + * request - {XMLHttpRequest} The request object │ │ │ │ + * options - {Object} Optional object to pass to format's read │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} or {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ + * {<OpenLayers.Feature.Vector>} │ │ │ │ + * An object with a features property, an array of features or a single │ │ │ │ + * feature. │ │ │ │ */ │ │ │ │ - draw: function(px) { │ │ │ │ - // initialize our internal div │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - px = this.position.clone(); │ │ │ │ + parseResponse: function(request, options) { │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText; │ │ │ │ + } │ │ │ │ + if (!doc || doc.length <= 0) { │ │ │ │ + return null; │ │ │ │ + } │ │ │ │ + var result = (this.readFormat !== null) ? this.readFormat.read(doc) : │ │ │ │ + this.format.read(doc, options); │ │ │ │ + if (!this.featureNS) { │ │ │ │ + var format = this.readFormat || this.format; │ │ │ │ + this.featureNS = format.featureNS; │ │ │ │ + // no need to auto-configure again on subsequent reads │ │ │ │ + format.autoConfig = false; │ │ │ │ + if (!this.geometryName) { │ │ │ │ + this.setGeometryName(format.geometryName); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return result; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // place the controls │ │ │ │ - this.buttons = []; │ │ │ │ + /** │ │ │ │ + * Method: commit │ │ │ │ + * Given a list of feature, assemble a batch request for update, create, │ │ │ │ + * and delete transactions. A commit call on the prototype amounts │ │ │ │ + * to writing a WFS transaction - so the write method on the format │ │ │ │ + * is used. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + * options - {Object} │ │ │ │ + * │ │ │ │ + * Valid options properties: │ │ │ │ + * nativeElements - {Array({Object})} Array of objects with information for writing │ │ │ │ + * out <Native> elements, these objects have vendorId, safeToIgnore and │ │ │ │ + * value properties. The <Native> element is intended to allow access to │ │ │ │ + * vendor specific capabilities of any particular web feature server or │ │ │ │ + * datastore. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.Response>} A response object with a features │ │ │ │ + * property containing any insertIds and a priv property referencing │ │ │ │ + * the XMLHttpRequest object. │ │ │ │ + */ │ │ │ │ + commit: function(features, options) { │ │ │ │ │ │ │ │ - var sz = { │ │ │ │ - w: 18, │ │ │ │ - h: 18 │ │ │ │ - }; │ │ │ │ - if (this.panIcons) { │ │ │ │ - var centered = new OpenLayers.Pixel(px.x + sz.w / 2, px.y); │ │ │ │ - var wposition = sz.w; │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ │ │ │ │ - if (this.zoomWorldIcon) { │ │ │ │ - centered = new OpenLayers.Pixel(px.x + sz.w, px.y); │ │ │ │ - } │ │ │ │ + var response = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "commit", │ │ │ │ + reqFeatures: features │ │ │ │ + }); │ │ │ │ + response.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + headers: options.headers, │ │ │ │ + data: this.format.write(features, options), │ │ │ │ + callback: this.createCallback(this.handleCommit, response, options) │ │ │ │ + }); │ │ │ │ │ │ │ │ - this._addButton("panup", "north-mini.png", centered, sz); │ │ │ │ - px.y = centered.y + sz.h; │ │ │ │ - this._addButton("panleft", "west-mini.png", px, sz); │ │ │ │ - if (this.zoomWorldIcon) { │ │ │ │ - this._addButton("zoomworld", "zoom-world-mini.png", px.add(sz.w, 0), sz); │ │ │ │ + return response; │ │ │ │ + }, │ │ │ │ │ │ │ │ - wposition *= 2; │ │ │ │ + /** │ │ │ │ + * Method: handleCommit │ │ │ │ + * Called when the commit request returns. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} The response object to pass │ │ │ │ + * to the user callback. │ │ │ │ + * options - {Object} The user options passed to the commit call. │ │ │ │ + */ │ │ │ │ + handleCommit: function(response, options) { │ │ │ │ + if (options.callback) { │ │ │ │ + var request = response.priv; │ │ │ │ + │ │ │ │ + // ensure that we have an xml doc │ │ │ │ + var data = request.responseXML; │ │ │ │ + if (!data || !data.documentElement) { │ │ │ │ + data = request.responseText; │ │ │ │ } │ │ │ │ - this._addButton("panright", "east-mini.png", px.add(wposition, 0), sz); │ │ │ │ - this._addButton("pandown", "south-mini.png", centered.add(0, sz.h * 2), sz); │ │ │ │ - this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h * 3 + 5), sz); │ │ │ │ - centered = this._addZoomBar(centered.add(0, sz.h * 4 + 5)); │ │ │ │ - this._addButton("zoomout", "zoom-minus-mini.png", centered, sz); │ │ │ │ - } else { │ │ │ │ - this._addButton("zoomin", "zoom-plus-mini.png", px, sz); │ │ │ │ - centered = this._addZoomBar(px.add(0, sz.h)); │ │ │ │ - this._addButton("zoomout", "zoom-minus-mini.png", centered, sz); │ │ │ │ - if (this.zoomWorldIcon) { │ │ │ │ - centered = centered.add(0, sz.h + 3); │ │ │ │ - this._addButton("zoomworld", "zoom-world-mini.png", centered, sz); │ │ │ │ + │ │ │ │ + var obj = this.format.read(data) || {}; │ │ │ │ + │ │ │ │ + response.insertIds = obj.insertIds || []; │ │ │ │ + if (obj.success) { │ │ │ │ + response.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ + } else { │ │ │ │ + response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + response.error = obj; │ │ │ │ } │ │ │ │ + options.callback.call(options.scope, response); │ │ │ │ } │ │ │ │ - return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: _addZoomBar │ │ │ │ + /** │ │ │ │ + * Method: filterDelete │ │ │ │ + * Send a request that deletes all features by their filter. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * centered - {<OpenLayers.Pixel>} where zoombar drawing is to start. │ │ │ │ + * filter - {<OpenLayers.Filter>} filter │ │ │ │ */ │ │ │ │ - _addZoomBar: function(centered) { │ │ │ │ - var imgLocation = OpenLayers.Util.getImageLocation("slider.png"); │ │ │ │ - var id = this.id + "_" + this.map.id; │ │ │ │ - var minZoom = this.map.getMinZoom(); │ │ │ │ - var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom(); │ │ │ │ - var slider = OpenLayers.Util.createAlphaImageDiv(id, │ │ │ │ - centered.add(-1, zoomsToEnd * this.zoomStopHeight), { │ │ │ │ - w: 20, │ │ │ │ - h: 9 │ │ │ │ - }, │ │ │ │ - imgLocation, │ │ │ │ - "absolute"); │ │ │ │ - slider.style.cursor = "move"; │ │ │ │ - this.slider = slider; │ │ │ │ + filterDelete: function(filter, options) { │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ │ │ │ │ - this.sliderEvents = new OpenLayers.Events(this, slider, null, true, { │ │ │ │ - includeXY: true │ │ │ │ + var response = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "commit" │ │ │ │ }); │ │ │ │ - this.sliderEvents.on({ │ │ │ │ - "touchstart": this.zoomBarDown, │ │ │ │ - "touchmove": this.zoomBarDrag, │ │ │ │ - "touchend": this.zoomBarUp, │ │ │ │ - "mousedown": this.zoomBarDown, │ │ │ │ - "mousemove": this.zoomBarDrag, │ │ │ │ - "mouseup": this.zoomBarUp │ │ │ │ + │ │ │ │ + var root = this.format.createElementNSPlus("wfs:Transaction", { │ │ │ │ + attributes: { │ │ │ │ + service: "WFS", │ │ │ │ + version: this.version │ │ │ │ + } │ │ │ │ }); │ │ │ │ │ │ │ │ - var sz = { │ │ │ │ - w: this.zoomStopWidth, │ │ │ │ - h: this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom) │ │ │ │ - }; │ │ │ │ - var imgLocation = OpenLayers.Util.getImageLocation("zoombar.png"); │ │ │ │ - var div = null; │ │ │ │ + var deleteNode = this.format.createElementNSPlus("wfs:Delete", { │ │ │ │ + attributes: { │ │ │ │ + typeName: (options.featureNS ? this.featurePrefix + ":" : "") + │ │ │ │ + options.featureType │ │ │ │ + } │ │ │ │ + }); │ │ │ │ │ │ │ │ - if (OpenLayers.Util.alphaHack()) { │ │ │ │ - var id = this.id + "_" + this.map.id; │ │ │ │ - div = OpenLayers.Util.createAlphaImageDiv(id, centered, { │ │ │ │ - w: sz.w, │ │ │ │ - h: this.zoomStopHeight │ │ │ │ - }, │ │ │ │ - imgLocation, │ │ │ │ - "absolute", null, "crop"); │ │ │ │ - div.style.height = sz.h + "px"; │ │ │ │ - } else { │ │ │ │ - div = OpenLayers.Util.createDiv( │ │ │ │ - 'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id, │ │ │ │ - centered, │ │ │ │ - sz, │ │ │ │ - imgLocation); │ │ │ │ + if (options.featureNS) { │ │ │ │ + deleteNode.setAttribute("xmlns:" + this.featurePrefix, options.featureNS); │ │ │ │ } │ │ │ │ - div.style.cursor = "pointer"; │ │ │ │ - div.className = "olButton"; │ │ │ │ - this.zoombarDiv = div; │ │ │ │ - │ │ │ │ - this.div.appendChild(div); │ │ │ │ + var filterNode = this.format.writeNode("ogc:Filter", filter); │ │ │ │ │ │ │ │ - this.startTop = parseInt(div.style.top); │ │ │ │ - this.div.appendChild(slider); │ │ │ │ + deleteNode.appendChild(filterNode); │ │ │ │ │ │ │ │ - this.map.events.register("zoomend", this, this.moveZoomBar); │ │ │ │ + root.appendChild(deleteNode); │ │ │ │ │ │ │ │ - centered = centered.add(0, │ │ │ │ - this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom)); │ │ │ │ - return centered; │ │ │ │ - }, │ │ │ │ + var data = OpenLayers.Format.XML.prototype.write.apply( │ │ │ │ + this.format, [root] │ │ │ │ + ); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: _removeZoomBar │ │ │ │ - */ │ │ │ │ - _removeZoomBar: function() { │ │ │ │ - this.sliderEvents.un({ │ │ │ │ - "touchstart": this.zoomBarDown, │ │ │ │ - "touchmove": this.zoomBarDrag, │ │ │ │ - "touchend": this.zoomBarUp, │ │ │ │ - "mousedown": this.zoomBarDown, │ │ │ │ - "mousemove": this.zoomBarDrag, │ │ │ │ - "mouseup": this.zoomBarUp │ │ │ │ + return OpenLayers.Request.POST({ │ │ │ │ + url: this.url, │ │ │ │ + callback: options.callback || function() {}, │ │ │ │ + data: data │ │ │ │ }); │ │ │ │ - this.sliderEvents.destroy(); │ │ │ │ - │ │ │ │ - this.div.removeChild(this.zoombarDiv); │ │ │ │ - this.zoombarDiv = null; │ │ │ │ - this.div.removeChild(this.slider); │ │ │ │ - this.slider = null; │ │ │ │ │ │ │ │ - this.map.events.unregister("zoomend", this, this.moveZoomBar); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onButtonClick │ │ │ │ + * Method: abort │ │ │ │ + * Abort an ongoing request, the response object passed to │ │ │ │ + * this method must come from this protocol (as a result │ │ │ │ + * of a read, or commit operation). │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} │ │ │ │ */ │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - OpenLayers.Control.PanZoom.prototype.onButtonClick.apply(this, arguments); │ │ │ │ - if (evt.buttonElement === this.zoombarDiv) { │ │ │ │ - var levels = evt.buttonXY.y / this.zoomStopHeight; │ │ │ │ - if (this.forceFixedZoomLevel || !this.map.fractionalZoom) { │ │ │ │ - levels = Math.floor(levels); │ │ │ │ - } │ │ │ │ - var zoom = (this.map.getNumZoomLevels() - 1) - levels; │ │ │ │ - zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1); │ │ │ │ - this.map.zoomTo(zoom); │ │ │ │ + abort: function(response) { │ │ │ │ + if (response) { │ │ │ │ + response.priv.abort(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.WFS.v1" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/GML/v2.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/GML/Base.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.GML.v2 │ │ │ │ + * Parses GML version 2. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.GML.Base> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, { │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: passEventToSlider │ │ │ │ - * This function is used to pass events that happen on the div, or the map, │ │ │ │ - * through to the slider, which then does its moving thing. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} Schema location for a particular minor version. │ │ │ │ */ │ │ │ │ - passEventToSlider: function(evt) { │ │ │ │ - this.sliderEvents.handleBrowserEvent(evt); │ │ │ │ - }, │ │ │ │ + schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd", │ │ │ │ │ │ │ │ - /* │ │ │ │ - * Method: zoomBarDown │ │ │ │ - * event listener for clicks on the slider │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.GML.v2 │ │ │ │ + * Create a parser for GML v2. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - zoomBarDown: function(evt) { │ │ │ │ - if (!OpenLayers.Event.isLeftClick(evt) && !OpenLayers.Event.isSingleTouch(evt)) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - this.map.events.on({ │ │ │ │ - "touchmove": this.passEventToSlider, │ │ │ │ - "mousemove": this.passEventToSlider, │ │ │ │ - "mouseup": this.passEventToSlider, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.mouseDragStart = evt.xy.clone(); │ │ │ │ - this.zoomStart = evt.xy.clone(); │ │ │ │ - this.div.style.cursor = "move"; │ │ │ │ - // reset the div offsets just in case the div moved │ │ │ │ - this.zoombarDiv.offsets = null; │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]); │ │ │ │ }, │ │ │ │ │ │ │ │ - /* │ │ │ │ - * Method: zoomBarDrag │ │ │ │ - * This is what happens when a click has occurred, and the client is │ │ │ │ - * dragging. Here we must ensure that the slider doesn't go beyond the │ │ │ │ - * bottom/top of the zoombar div, as well as moving the slider to its new │ │ │ │ - * visual location │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - zoomBarDrag: function(evt) { │ │ │ │ - if (this.mouseDragStart != null) { │ │ │ │ - var deltaY = this.mouseDragStart.y - evt.xy.y; │ │ │ │ - var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv); │ │ │ │ - if ((evt.clientY - offsets[1]) > 0 && │ │ │ │ - (evt.clientY - offsets[1]) < parseInt(this.zoombarDiv.style.height) - 2) { │ │ │ │ - var newTop = parseInt(this.slider.style.top) - deltaY; │ │ │ │ - this.slider.style.top = newTop + "px"; │ │ │ │ - this.mouseDragStart = evt.xy.clone(); │ │ │ │ + readers: { │ │ │ │ + "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ + "outerBoundaryIs": function(node, container) { │ │ │ │ + var obj = {}; │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + container.outer = obj.components[0]; │ │ │ │ + }, │ │ │ │ + "innerBoundaryIs": function(node, container) { │ │ │ │ + var obj = {}; │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + container.inner.push(obj.components[0]); │ │ │ │ + }, │ │ │ │ + "Box": function(node, container) { │ │ │ │ + var obj = {}; │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + if (!container.components) { │ │ │ │ + container.components = []; │ │ │ │ + } │ │ │ │ + var min = obj.points[0]; │ │ │ │ + var max = obj.points[1]; │ │ │ │ + container.components.push( │ │ │ │ + new OpenLayers.Bounds(min.x, min.y, max.x, max.y) │ │ │ │ + ); │ │ │ │ } │ │ │ │ - // set cumulative displacement │ │ │ │ - this.deltaY = this.zoomStart.y - evt.xy.y; │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - } │ │ │ │ + }, OpenLayers.Format.GML.Base.prototype.readers["gml"]), │ │ │ │ + "feature": OpenLayers.Format.GML.Base.prototype.readers["feature"], │ │ │ │ + "wfs": OpenLayers.Format.GML.Base.prototype.readers["wfs"] │ │ │ │ }, │ │ │ │ │ │ │ │ - /* │ │ │ │ - * Method: zoomBarUp │ │ │ │ - * Perform cleanup when a mouseup event is received -- discover new zoom │ │ │ │ - * level and switch to it. │ │ │ │ + /** │ │ │ │ + * Method: write │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>) | OpenLayers.Feature.Vector} │ │ │ │ + * An array of features or a single feature. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} Given an array of features, a doc with a gml:featureMembers │ │ │ │ + * element will be returned. Given a single feature, a doc with a │ │ │ │ + * gml:featureMember element will be returned. │ │ │ │ */ │ │ │ │ - zoomBarUp: function(evt) { │ │ │ │ - if (!OpenLayers.Event.isLeftClick(evt) && evt.type !== "touchend") { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (this.mouseDragStart) { │ │ │ │ - this.div.style.cursor = ""; │ │ │ │ - this.map.events.un({ │ │ │ │ - "touchmove": this.passEventToSlider, │ │ │ │ - "mouseup": this.passEventToSlider, │ │ │ │ - "mousemove": this.passEventToSlider, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - var zoomLevel = this.map.zoom; │ │ │ │ - if (!this.forceFixedZoomLevel && this.map.fractionalZoom) { │ │ │ │ - zoomLevel += this.deltaY / this.zoomStopHeight; │ │ │ │ - zoomLevel = Math.min(Math.max(zoomLevel, 0), │ │ │ │ - this.map.getNumZoomLevels() - 1); │ │ │ │ - } else { │ │ │ │ - zoomLevel += this.deltaY / this.zoomStopHeight; │ │ │ │ - zoomLevel = Math.max(Math.round(zoomLevel), 0); │ │ │ │ - } │ │ │ │ - this.map.zoomTo(zoomLevel); │ │ │ │ - this.mouseDragStart = null; │ │ │ │ - this.zoomStart = null; │ │ │ │ - this.deltaY = 0; │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ + write: function(features) { │ │ │ │ + var name; │ │ │ │ + if (OpenLayers.Util.isArray(features)) { │ │ │ │ + // GML2 only has abstract feature collections │ │ │ │ + // wfs provides a feature collection from a well-known schema │ │ │ │ + name = "wfs:FeatureCollection"; │ │ │ │ + } else { │ │ │ │ + name = "gml:featureMember"; │ │ │ │ } │ │ │ │ + var root = this.writeNode(name, features); │ │ │ │ + this.setAttributeNS( │ │ │ │ + root, this.namespaces["xsi"], │ │ │ │ + "xsi:schemaLocation", this.schemaLocation │ │ │ │ + ); │ │ │ │ + │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [root]); │ │ │ │ }, │ │ │ │ │ │ │ │ - /* │ │ │ │ - * Method: moveZoomBar │ │ │ │ - * Change the location of the slider to match the current zoom level. │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - moveZoomBar: function() { │ │ │ │ - var newTop = │ │ │ │ - ((this.map.getNumZoomLevels() - 1) - this.map.getZoom()) * │ │ │ │ - this.zoomStopHeight + this.startTop + 1; │ │ │ │ - this.slider.style.top = newTop + "px"; │ │ │ │ + writers: { │ │ │ │ + "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ + "Point": function(geometry) { │ │ │ │ + var node = this.createElementNSPlus("gml:Point"); │ │ │ │ + this.writeNode("coordinates", [geometry], node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "coordinates": function(points) { │ │ │ │ + var numPoints = points.length; │ │ │ │ + var parts = new Array(numPoints); │ │ │ │ + var point; │ │ │ │ + for (var i = 0; i < numPoints; ++i) { │ │ │ │ + point = points[i]; │ │ │ │ + if (this.xy) { │ │ │ │ + parts[i] = point.x + "," + point.y; │ │ │ │ + } else { │ │ │ │ + parts[i] = point.y + "," + point.x; │ │ │ │ + } │ │ │ │ + if (point.z != undefined) { // allow null or undefined │ │ │ │ + parts[i] += "," + point.z; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return this.createElementNSPlus("gml:coordinates", { │ │ │ │ + attributes: { │ │ │ │ + decimal: ".", │ │ │ │ + cs: ",", │ │ │ │ + ts: " " │ │ │ │ + }, │ │ │ │ + value: (numPoints == 1) ? parts[0] : parts.join(" ") │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "LineString": function(geometry) { │ │ │ │ + var node = this.createElementNSPlus("gml:LineString"); │ │ │ │ + this.writeNode("coordinates", geometry.components, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Polygon": function(geometry) { │ │ │ │ + var node = this.createElementNSPlus("gml:Polygon"); │ │ │ │ + this.writeNode("outerBoundaryIs", geometry.components[0], node); │ │ │ │ + for (var i = 1; i < geometry.components.length; ++i) { │ │ │ │ + this.writeNode( │ │ │ │ + "innerBoundaryIs", geometry.components[i], node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "outerBoundaryIs": function(ring) { │ │ │ │ + var node = this.createElementNSPlus("gml:outerBoundaryIs"); │ │ │ │ + this.writeNode("LinearRing", ring, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "innerBoundaryIs": function(ring) { │ │ │ │ + var node = this.createElementNSPlus("gml:innerBoundaryIs"); │ │ │ │ + this.writeNode("LinearRing", ring, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "LinearRing": function(ring) { │ │ │ │ + var node = this.createElementNSPlus("gml:LinearRing"); │ │ │ │ + this.writeNode("coordinates", ring.components, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Box": function(bounds) { │ │ │ │ + var node = this.createElementNSPlus("gml:Box"); │ │ │ │ + this.writeNode("coordinates", [{ │ │ │ │ + x: bounds.left, │ │ │ │ + y: bounds.bottom │ │ │ │ + }, { │ │ │ │ + x: bounds.right, │ │ │ │ + y: bounds.top │ │ │ │ + }], node); │ │ │ │ + // srsName attribute is optional for gml:Box │ │ │ │ + if (this.srsName) { │ │ │ │ + node.setAttribute("srsName", this.srsName); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.GML.Base.prototype.writers["gml"]), │ │ │ │ + "feature": OpenLayers.Format.GML.Base.prototype.writers["feature"], │ │ │ │ + "wfs": OpenLayers.Format.GML.Base.prototype.writers["wfs"] │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.PanZoomBar" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.GML.v2" │ │ │ │ + │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Handler/Pinch.js │ │ │ │ + OpenLayers/Format/Filter/v1_0_0.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Handler.js │ │ │ │ + * @requires OpenLayers/Format/GML/v2.js │ │ │ │ + * @requires OpenLayers/Format/Filter/v1.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Handler.Pinch │ │ │ │ - * The pinch handler is used to deal with sequences of browser events related │ │ │ │ - * to pinch gestures. The handler is used by controls that want to know │ │ │ │ - * when a pinch sequence begins, when a pinch is happening, and when it has │ │ │ │ - * finished. │ │ │ │ - * │ │ │ │ - * Controls that use the pinch handler typically construct it with callbacks │ │ │ │ - * for 'start', 'move', and 'done'. Callbacks for these keys are │ │ │ │ - * called when the pinch begins, with each change, and when the pinch is │ │ │ │ - * done. │ │ │ │ - * │ │ │ │ - * Create a new pinch handler with the <OpenLayers.Handler.Pinch> constructor. │ │ │ │ + * Class: OpenLayers.Format.Filter.v1_0_0 │ │ │ │ + * Write ogc:Filter version 1.0.0. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.GML.v2> │ │ │ │ + * - <OpenLayers.Format.Filter.v1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.Filter.v1_0_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.GML.v2, OpenLayers.Format.Filter.v1, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: VERSION │ │ │ │ + * {String} 1.0.0 │ │ │ │ + */ │ │ │ │ + VERSION: "1.0.0", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} http://www.opengis.net/ogc/filter/1.0.0/filter.xsd │ │ │ │ + */ │ │ │ │ + schemaLocation: "http://www.opengis.net/ogc/filter/1.0.0/filter.xsd", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.Filter.v1_0_0 │ │ │ │ + * Instances of this class are not created directly. Use the │ │ │ │ + * <OpenLayers.Format.Filter> constructor instead. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.GML.v2.prototype.initialize.apply( │ │ │ │ + this, [options] │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "ogc": OpenLayers.Util.applyDefaults({ │ │ │ │ + "PropertyIsEqualTo": function(node, obj) { │ │ │ │ + var filter = new OpenLayers.Filter.Comparison({ │ │ │ │ + type: OpenLayers.Filter.Comparison.EQUAL_TO │ │ │ │ + }); │ │ │ │ + this.readChildNodes(node, filter); │ │ │ │ + obj.filters.push(filter); │ │ │ │ + }, │ │ │ │ + "PropertyIsNotEqualTo": function(node, obj) { │ │ │ │ + var filter = new OpenLayers.Filter.Comparison({ │ │ │ │ + type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO │ │ │ │ + }); │ │ │ │ + this.readChildNodes(node, filter); │ │ │ │ + obj.filters.push(filter); │ │ │ │ + }, │ │ │ │ + "PropertyIsLike": function(node, obj) { │ │ │ │ + var filter = new OpenLayers.Filter.Comparison({ │ │ │ │ + type: OpenLayers.Filter.Comparison.LIKE │ │ │ │ + }); │ │ │ │ + this.readChildNodes(node, filter); │ │ │ │ + var wildCard = node.getAttribute("wildCard"); │ │ │ │ + var singleChar = node.getAttribute("singleChar"); │ │ │ │ + var esc = node.getAttribute("escape"); │ │ │ │ + filter.value2regex(wildCard, singleChar, esc); │ │ │ │ + obj.filters.push(filter); │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.Filter.v1.prototype.readers["ogc"]), │ │ │ │ + "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"], │ │ │ │ + "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"] │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: writers │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ + * node names they produce. │ │ │ │ + */ │ │ │ │ + writers: { │ │ │ │ + "ogc": OpenLayers.Util.applyDefaults({ │ │ │ │ + "PropertyIsEqualTo": function(filter) { │ │ │ │ + var node = this.createElementNSPlus("ogc:PropertyIsEqualTo"); │ │ │ │ + // no ogc:expression handling for PropertyName for now │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ + // handle Literals or Functions for now │ │ │ │ + this.writeOgcExpression(filter.value, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "PropertyIsNotEqualTo": function(filter) { │ │ │ │ + var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo"); │ │ │ │ + // no ogc:expression handling for PropertyName for now │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ + // handle Literals or Functions for now │ │ │ │ + this.writeOgcExpression(filter.value, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "PropertyIsLike": function(filter) { │ │ │ │ + var node = this.createElementNSPlus("ogc:PropertyIsLike", { │ │ │ │ + attributes: { │ │ │ │ + wildCard: "*", │ │ │ │ + singleChar: ".", │ │ │ │ + escape: "!" │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + // no ogc:expression handling for now │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ + // convert regex string to ogc string │ │ │ │ + this.writeNode("Literal", filter.regex2value(), node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "BBOX": function(filter) { │ │ │ │ + var node = this.createElementNSPlus("ogc:BBOX"); │ │ │ │ + // PropertyName is mandatory in 1.0.0, but e.g. GeoServer also │ │ │ │ + // accepts filters without it. When this is used with │ │ │ │ + // OpenLayers.Protocol.WFS, OpenLayers.Format.WFST will set a │ │ │ │ + // missing filter.property to the geometryName that is │ │ │ │ + // configured with the protocol, which defaults to "the_geom". │ │ │ │ + // So the only way to omit this mandatory property is to not │ │ │ │ + // set the property on the filter and to set the geometryName │ │ │ │ + // on the WFS protocol to null. The latter also happens when │ │ │ │ + // the protocol is configured without a geometryName and a │ │ │ │ + // featureNS. │ │ │ │ + filter.property && this.writeNode("PropertyName", filter, node); │ │ │ │ + var box = this.writeNode("gml:Box", filter.value, node); │ │ │ │ + if (filter.projection) { │ │ │ │ + box.setAttribute("srsName", filter.projection); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.Filter.v1.prototype.writers["ogc"]), │ │ │ │ + "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"], │ │ │ │ + "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"] │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: writeSpatial │ │ │ │ + * │ │ │ │ + * Read a {<OpenLayers.Filter.Spatial>} filter and converts it into XML. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * filter - {<OpenLayers.Filter.Spatial>} The filter. │ │ │ │ + * name - {String} Name of the generated XML element. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The created XML element. │ │ │ │ + */ │ │ │ │ + writeSpatial: function(filter, name) { │ │ │ │ + var node = this.createElementNSPlus("ogc:" + name); │ │ │ │ + this.writeNode("PropertyName", filter, node); │ │ │ │ + if (filter.value instanceof OpenLayers.Filter.Function) { │ │ │ │ + this.writeNode("Function", filter.value, node); │ │ │ │ + } else { │ │ │ │ + var child; │ │ │ │ + if (filter.value instanceof OpenLayers.Geometry) { │ │ │ │ + child = this.writeNode("feature:_geometry", filter.value).firstChild; │ │ │ │ + } else { │ │ │ │ + child = this.writeNode("gml:Box", filter.value); │ │ │ │ + } │ │ │ │ + if (filter.projection) { │ │ │ │ + child.setAttribute("srsName", filter.projection); │ │ │ │ + } │ │ │ │ + node.appendChild(child); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WFST/v1_0_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WFST/v1.js │ │ │ │ + * @requires OpenLayers/Format/Filter/v1_0_0.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WFST.v1_0_0 │ │ │ │ + * A format for creating WFS v1.0.0 transactions. Create a new instance with the │ │ │ │ + * <OpenLayers.Format.WFST.v1_0_0> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Handler> │ │ │ │ + * - <OpenLayers.Format.Filter.v1_0_0> │ │ │ │ + * - <OpenLayers.Format.WFST.v1> │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ +OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: started │ │ │ │ - * {Boolean} When a touchstart event is received, we want to record it, │ │ │ │ - * but not set 'pinching' until the touchmove get started after │ │ │ │ - * starting. │ │ │ │ - */ │ │ │ │ - started: false, │ │ │ │ + /** │ │ │ │ + * Property: version │ │ │ │ + * {String} WFS version number. │ │ │ │ + */ │ │ │ │ + version: "1.0.0", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: stopDown │ │ │ │ - * {Boolean} Stop propagation of touchstart events from getting to │ │ │ │ - * listeners on the same element. Default is false. │ │ │ │ - */ │ │ │ │ - stopDown: false, │ │ │ │ + /** │ │ │ │ + * APIProperty: srsNameInQuery │ │ │ │ + * {Boolean} If true the reference system is passed in Query requests │ │ │ │ + * via the "srsName" attribute to the "wfs:Query" element, this │ │ │ │ + * property defaults to false as it isn't WFS 1.0.0 compliant. │ │ │ │ + */ │ │ │ │ + srsNameInQuery: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: pinching │ │ │ │ - * {Boolean} │ │ │ │ - */ │ │ │ │ - pinching: false, │ │ │ │ + /** │ │ │ │ + * Property: schemaLocations │ │ │ │ + * {Object} Properties are namespace aliases, values are schema locations. │ │ │ │ + */ │ │ │ │ + schemaLocations: { │ │ │ │ + "wfs": "http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd" │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: last │ │ │ │ - * {Object} Object that store informations related to pinch last touch. │ │ │ │ - */ │ │ │ │ - last: null, │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WFST.v1_0_0 │ │ │ │ + * A class for parsing and generating WFS v1.0.0 transactions. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ + * │ │ │ │ + * Valid options properties: │ │ │ │ + * featureType - {String} Local (without prefix) feature typeName (required). │ │ │ │ + * featureNS - {String} Feature namespace (optional). │ │ │ │ + * featurePrefix - {String} Feature namespace alias (optional - only used │ │ │ │ + * if featureNS is provided). Default is 'feature'. │ │ │ │ + * geometryName - {String} Name of geometry attribute. Default is 'the_geom'. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]); │ │ │ │ + OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: start │ │ │ │ - * {Object} Object that store informations related to pinch touchstart. │ │ │ │ - */ │ │ │ │ - start: 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.v2. We need this because we don't want to get readNode │ │ │ │ + // from the superclass's superclass, which is OpenLayers.Format.XML. │ │ │ │ + return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Handler.Pinch │ │ │ │ - * Returns OpenLayers.Handler.Pinch │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} The control that is making use of │ │ │ │ - * this handler. If a handler is being used without a control, the │ │ │ │ - * handlers setMap method must be overridden to deal properly with │ │ │ │ - * the map. │ │ │ │ - * callbacks - {Object} An object containing functions to be called when │ │ │ │ - * the pinch operation start, change, or is finished. The callbacks │ │ │ │ - * should expect to receive an object argument, which contains │ │ │ │ - * information about scale, distance, and position of touch points. │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wfs": OpenLayers.Util.applyDefaults({ │ │ │ │ + "WFS_TransactionResponse": function(node, obj) { │ │ │ │ + obj.insertIds = []; │ │ │ │ + obj.success = false; │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "InsertResult": function(node, container) { │ │ │ │ + var obj = { │ │ │ │ + fids: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + container.insertIds = container.insertIds.concat(obj.fids); │ │ │ │ + }, │ │ │ │ + "TransactionResult": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "Status": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "SUCCESS": function(node, obj) { │ │ │ │ + obj.success = true; │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]), │ │ │ │ + "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"], │ │ │ │ + "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"], │ │ │ │ + "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.readers["ogc"] │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: touchstart │ │ │ │ - * Handle touchstart events │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ - */ │ │ │ │ - touchstart: function(evt) { │ │ │ │ - var propagate = true; │ │ │ │ - this.pinching = false; │ │ │ │ - if (OpenLayers.Event.isMultiTouch(evt)) { │ │ │ │ - this.started = true; │ │ │ │ - this.last = this.start = { │ │ │ │ - distance: this.getDistance(evt.touches), │ │ │ │ - delta: 0, │ │ │ │ - scale: 1 │ │ │ │ - }; │ │ │ │ - this.callback("start", [evt, this.start]); │ │ │ │ - propagate = !this.stopDown; │ │ │ │ - } else if (this.started) { │ │ │ │ - // Some webkit versions send fake single-touch events during │ │ │ │ - // multitouch, which cause the drag handler to trigger │ │ │ │ - return false; │ │ │ │ - } else { │ │ │ │ - this.started = false; │ │ │ │ - this.start = null; │ │ │ │ - this.last = null; │ │ │ │ - } │ │ │ │ - // prevent document dragging │ │ │ │ - OpenLayers.Event.preventDefault(evt); │ │ │ │ - return propagate; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: writers │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ + * node names they produce. │ │ │ │ + */ │ │ │ │ + writers: { │ │ │ │ + "wfs": OpenLayers.Util.applyDefaults({ │ │ │ │ + "Query": function(options) { │ │ │ │ + options = OpenLayers.Util.extend({ │ │ │ │ + featureNS: this.featureNS, │ │ │ │ + featurePrefix: this.featurePrefix, │ │ │ │ + featureType: this.featureType, │ │ │ │ + srsName: this.srsName, │ │ │ │ + srsNameInQuery: this.srsNameInQuery │ │ │ │ + }, options); │ │ │ │ + var prefix = options.featurePrefix; │ │ │ │ + var node = this.createElementNSPlus("wfs:Query", { │ │ │ │ + attributes: { │ │ │ │ + typeName: (prefix ? prefix + ":" : "") + │ │ │ │ + options.featureType │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + if (options.srsNameInQuery && options.srsName) { │ │ │ │ + node.setAttribute("srsName", options.srsName); │ │ │ │ + } │ │ │ │ + if (options.featureNS) { │ │ │ │ + node.setAttribute("xmlns:" + prefix, options.featureNS); │ │ │ │ + } │ │ │ │ + if (options.propertyNames) { │ │ │ │ + for (var i = 0, len = options.propertyNames.length; i < len; i++) { │ │ │ │ + this.writeNode( │ │ │ │ + "ogc:PropertyName", { │ │ │ │ + property: options.propertyNames[i] │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (options.filter) { │ │ │ │ + this.setFilterProperty(options.filter); │ │ │ │ + this.writeNode("ogc:Filter", options.filter, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]), │ │ │ │ + "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"], │ │ │ │ + "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"], │ │ │ │ + "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.writers["ogc"] │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFST.v1_0_0" │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Protocol/WFS/v1_0_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Protocol/WFS/v1.js │ │ │ │ + * @requires OpenLayers/Format/WFST/v1_0_0.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Protocol.WFS.v1_0_0 │ │ │ │ + * A WFS v1.0.0 protocol for vector layers. Create a new instance with the │ │ │ │ + * <OpenLayers.Protocol.WFS.v1_0_0> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Protocol.WFS.v1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: touchmove │ │ │ │ - * Handle touchmove events │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ + * Property: version │ │ │ │ + * {String} WFS version number. │ │ │ │ */ │ │ │ │ - touchmove: function(evt) { │ │ │ │ - if (this.started && OpenLayers.Event.isMultiTouch(evt)) { │ │ │ │ - this.pinching = true; │ │ │ │ - var current = this.getPinchData(evt); │ │ │ │ - this.callback("move", [evt, current]); │ │ │ │ - this.last = current; │ │ │ │ - // prevent document dragging │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - } else if (this.started) { │ │ │ │ - // Some webkit versions send fake single-touch events during │ │ │ │ - // multitouch, which cause the drag handler to trigger │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ + version: "1.0.0", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: touchend │ │ │ │ - * Handle touchend events │ │ │ │ + * Constructor: OpenLayers.Protocol.WFS.v1_0_0 │ │ │ │ + * A class for giving layers WFS v1.0.0 protocol. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ + * 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'. │ │ │ │ */ │ │ │ │ - touchend: function(evt) { │ │ │ │ - if (this.started && !OpenLayers.Event.isMultiTouch(evt)) { │ │ │ │ - this.started = false; │ │ │ │ - this.pinching = false; │ │ │ │ - this.callback("done", [evt, this.start, this.last]); │ │ │ │ - this.start = null; │ │ │ │ - this.last = null; │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: activate │ │ │ │ - * Activate the handler. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The handler was successfully activated. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.pinching = false; │ │ │ │ - activated = true; │ │ │ │ - } │ │ │ │ - return activated; │ │ │ │ - }, │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.WFS.v1_0_0" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Protocol/WFS/v1_1_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Deactivate the handler. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The handler was successfully deactivated. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.started = false; │ │ │ │ - this.pinching = false; │ │ │ │ - this.start = null; │ │ │ │ - this.last = null; │ │ │ │ - deactivated = true; │ │ │ │ - } │ │ │ │ - return deactivated; │ │ │ │ - }, │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Protocol/WFS/v1.js │ │ │ │ + * @requires OpenLayers/Format/WFST/v1_1_0.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Protocol.WFS.v1_1_0 │ │ │ │ + * A WFS v1.1.0 protocol for vector layers. Create a new instance with the │ │ │ │ + * <OpenLayers.Protocol.WFS.v1_1_0> constructor. │ │ │ │ + * │ │ │ │ + * Differences from the v1.0.0 protocol: │ │ │ │ + * - uses Filter Encoding 1.1.0 instead of 1.0.0 │ │ │ │ + * - uses GML 3 instead of 2 if no format is provided │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Protocol.WFS.v1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Protocol.WFS.v1_1_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getDistance │ │ │ │ - * Get the distance in pixels between two touches. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * touches - {Array(Object)} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Number} The distance in pixels. │ │ │ │ + * Property: version │ │ │ │ + * {String} WFS version number. │ │ │ │ */ │ │ │ │ - getDistance: function(touches) { │ │ │ │ - var t0 = touches[0]; │ │ │ │ - var t1 = touches[1]; │ │ │ │ - return Math.sqrt( │ │ │ │ - Math.pow(t0.olClientX - t1.olClientX, 2) + │ │ │ │ - Math.pow(t0.olClientY - t1.olClientY, 2) │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - │ │ │ │ + version: "1.1.0", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getPinchData │ │ │ │ - * Get informations about the pinch event. │ │ │ │ + * Constructor: OpenLayers.Protocol.WFS.v1_1_0 │ │ │ │ + * A class for giving layers WFS v1.1.0 protocol. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Object that contains data about the current pinch. │ │ │ │ + * Valid options properties: │ │ │ │ + * featureType - {String} Local (without prefix) feature typeName (required). │ │ │ │ + * featureNS - {String} Feature namespace (optional). │ │ │ │ + * featurePrefix - {String} Feature namespace alias (optional - only used │ │ │ │ + * if featureNS is provided). Default is 'feature'. │ │ │ │ + * geometryName - {String} Name of geometry attribute. Default is 'the_geom'. │ │ │ │ + * outputFormat - {String} Optional output format to use for WFS GetFeature │ │ │ │ + * requests. This can be any format advertized by the WFS's │ │ │ │ + * GetCapabilities response. If set, an appropriate readFormat also │ │ │ │ + * has to be provided, unless outputFormat is GML3, GML2 or JSON. │ │ │ │ + * readFormat - {<OpenLayers.Format>} An appropriate format parser if │ │ │ │ + * outputFormat is none of GML3, GML2 or JSON. │ │ │ │ */ │ │ │ │ - getPinchData: function(evt) { │ │ │ │ - var distance = this.getDistance(evt.touches); │ │ │ │ - var scale = distance / this.start.distance; │ │ │ │ - return { │ │ │ │ - distance: distance, │ │ │ │ - delta: this.last.distance - distance, │ │ │ │ - scale: scale │ │ │ │ - }; │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Protocol.WFS.v1.prototype.initialize.apply(this, arguments); │ │ │ │ + if (this.outputFormat && !this.readFormat) { │ │ │ │ + if (this.outputFormat.toLowerCase() == "gml2") { │ │ │ │ + this.readFormat = new OpenLayers.Format.GML.v2({ │ │ │ │ + featureType: this.featureType, │ │ │ │ + featureNS: this.featureNS, │ │ │ │ + geometryName: this.geometryName │ │ │ │ + }); │ │ │ │ + } else if (this.outputFormat.toLowerCase() == "json") { │ │ │ │ + this.readFormat = new OpenLayers.Format.GeoJSON(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Pinch" │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.WFS.v1_1_0" │ │ │ │ }); │ │ │ │ - │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/PinchZoom.js │ │ │ │ + OpenLayers/Format/SOSGetFeatureOfInterest.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Handler/Pinch.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Format/GML/v3.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.PinchZoom │ │ │ │ + * Class: OpenLayers.Format.SOSGetFeatureOfInterest │ │ │ │ + * Read and write SOS GetFeatureOfInterest. This is used to get to │ │ │ │ + * the location of the features (stations). The stations can have 1 or more │ │ │ │ + * sensors. │ │ │ │ * │ │ │ │ - * Inherits: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ +OpenLayers.Format.SOSGetFeatureOfInterest = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: type │ │ │ │ - * {OpenLayers.Control.TYPES} │ │ │ │ - */ │ │ │ │ - type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ + /** │ │ │ │ + * Constant: VERSION │ │ │ │ + * {String} 1.0.0 │ │ │ │ + */ │ │ │ │ + VERSION: "1.0.0", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: pinchOrigin │ │ │ │ - * {Object} Cached object representing the pinch start (in pixels). │ │ │ │ - */ │ │ │ │ - pinchOrigin: null, │ │ │ │ + /** │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + */ │ │ │ │ + namespaces: { │ │ │ │ + sos: "http://www.opengis.net/sos/1.0", │ │ │ │ + gml: "http://www.opengis.net/gml", │ │ │ │ + sa: "http://www.opengis.net/sampling/1.0", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: currentCenter │ │ │ │ - * {Object} Cached object representing the latest pinch center (in pixels). │ │ │ │ - */ │ │ │ │ - currentCenter: null, │ │ │ │ + /** │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} Schema location │ │ │ │ + */ │ │ │ │ + schemaLocation: "http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosAll.xsd", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * true. │ │ │ │ - */ │ │ │ │ - autoActivate: true, │ │ │ │ + /** │ │ │ │ + * Property: defaultPrefix │ │ │ │ + */ │ │ │ │ + defaultPrefix: "sos", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: regExes │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ + */ │ │ │ │ + regExes: { │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.SOSGetFeatureOfInterest │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Parse a GetFeatureOfInterest response and return an array of features │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} An array of features. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ + } │ │ │ │ + │ │ │ │ + var info = { │ │ │ │ + features: [] │ │ │ │ + }; │ │ │ │ + this.readNode(data, info); │ │ │ │ + │ │ │ │ + var features = []; │ │ │ │ + for (var i = 0, len = info.features.length; i < len; i++) { │ │ │ │ + var container = info.features[i]; │ │ │ │ + // reproject features if needed │ │ │ │ + if (this.internalProjection && this.externalProjection && │ │ │ │ + container.components[0]) { │ │ │ │ + container.components[0].transform( │ │ │ │ + this.externalProjection, this.internalProjection │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + var feature = new OpenLayers.Feature.Vector( │ │ │ │ + container.components[0], container.attributes); │ │ │ │ + features.push(feature); │ │ │ │ + } │ │ │ │ + return features; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "sa": { │ │ │ │ + "SamplingPoint": function(node, obj) { │ │ │ │ + // sampling point can also be without a featureMember if │ │ │ │ + // there is only 1 │ │ │ │ + if (!obj.attributes) { │ │ │ │ + var feature = { │ │ │ │ + attributes: {} │ │ │ │ + }; │ │ │ │ + obj.features.push(feature); │ │ │ │ + obj = feature; │ │ │ │ + } │ │ │ │ + obj.attributes.id = this.getAttributeNS(node, │ │ │ │ + this.namespaces.gml, "id"); │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "position": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ + "FeatureCollection": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "featureMember": function(node, obj) { │ │ │ │ + var feature = { │ │ │ │ + attributes: {} │ │ │ │ + }; │ │ │ │ + obj.features.push(feature); │ │ │ │ + this.readChildNodes(node, feature); │ │ │ │ + }, │ │ │ │ + "name": function(node, obj) { │ │ │ │ + obj.attributes.name = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "pos": function(node, obj) { │ │ │ │ + // we need to parse the srsName to get to the │ │ │ │ + // externalProjection, that's why we cannot use │ │ │ │ + // GML v3 for this │ │ │ │ + if (!this.externalProjection) { │ │ │ │ + this.externalProjection = new OpenLayers.Projection( │ │ │ │ + node.getAttribute("srsName")); │ │ │ │ + } │ │ │ │ + OpenLayers.Format.GML.v3.prototype.readers.gml.pos.apply( │ │ │ │ + this, [node, obj]); │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.GML.v3.prototype.readers.gml) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: writers │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ + * node names they produce. │ │ │ │ + */ │ │ │ │ + writers: { │ │ │ │ + "sos": { │ │ │ │ + "GetFeatureOfInterest": function(options) { │ │ │ │ + var node = this.createElementNSPlus("GetFeatureOfInterest", { │ │ │ │ + attributes: { │ │ │ │ + version: this.VERSION, │ │ │ │ + service: 'SOS', │ │ │ │ + "xsi:schemaLocation": this.schemaLocation │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + for (var i = 0, len = options.fois.length; i < len; i++) { │ │ │ │ + this.writeNode("FeatureOfInterestId", { │ │ │ │ + foi: options.fois[i] │ │ │ │ + }, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "FeatureOfInterestId": function(options) { │ │ │ │ + var node = this.createElementNSPlus("FeatureOfInterestId", { │ │ │ │ + value: options.foi │ │ │ │ + }); │ │ │ │ + return node; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.SOSGetFeatureOfInterest" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Protocol/SOS/v1_0_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Protocol/SOS.js │ │ │ │ + * @requires OpenLayers/Format/SOSGetFeatureOfInterest.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Protocol.SOS.v1_0_0 │ │ │ │ + * An SOS v1.0.0 Protocol for vector layers. Create a new instance with the │ │ │ │ + * <OpenLayers.Protocol.SOS.v1_0_0> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Protocol> │ │ │ │ + */ │ │ │ │ +OpenLayers.Protocol.SOS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: preserveCenter │ │ │ │ - * {Boolean} Set this to true if you don't want the map center to change │ │ │ │ - * while pinching. For example you may want to set preserveCenter to │ │ │ │ - * true when the user location is being watched and you want to preserve │ │ │ │ - * the user location at the center of the map even if he zooms in or │ │ │ │ - * out using pinch. This property's value can be changed any time on an │ │ │ │ - * existing instance. Default is false. │ │ │ │ + * APIProperty: fois │ │ │ │ + * {Array(String)} Array of features of interest (foi) │ │ │ │ */ │ │ │ │ - preserveCenter: false, │ │ │ │ + fois: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: handlerOptions │ │ │ │ - * {Object} Used to set non-default properties on the pinch handler │ │ │ │ + * Property: formatOptions │ │ │ │ + * {Object} Optional options for the format. If a format is not provided, │ │ │ │ + * this property can be used to extend the default format options. │ │ │ │ */ │ │ │ │ + formatOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.PinchZoom │ │ │ │ - * Create a control for zooming with pinch gestures. This works on devices │ │ │ │ - * with multi-touch support. │ │ │ │ + * Constructor: OpenLayers.Protocol.SOS │ │ │ │ + * A class for giving layers an SOS protocol. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * the control │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ + * │ │ │ │ + * Valid options properties: │ │ │ │ + * url - {String} URL to send requests to (required). │ │ │ │ + * fois - {Array} The features of interest (required). │ │ │ │ */ │ │ │ │ initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ - this.handler = new OpenLayers.Handler.Pinch(this, { │ │ │ │ - start: this.pinchStart, │ │ │ │ - move: this.pinchMove, │ │ │ │ - done: this.pinchDone │ │ │ │ - }, this.handlerOptions); │ │ │ │ + OpenLayers.Protocol.prototype.initialize.apply(this, [options]); │ │ │ │ + if (!options.format) { │ │ │ │ + this.format = new OpenLayers.Format.SOSGetFeatureOfInterest( │ │ │ │ + this.formatOptions); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: pinchStart │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * pinchData - {Object} pinch data object related to the current touchmove │ │ │ │ - * of the pinch gesture. This give us the current scale of the pinch. │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up the protocol. │ │ │ │ */ │ │ │ │ - pinchStart: function(evt, pinchData) { │ │ │ │ - var xy = (this.preserveCenter) ? │ │ │ │ - this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy; │ │ │ │ - this.pinchOrigin = xy; │ │ │ │ - this.currentCenter = xy; │ │ │ │ + destroy: function() { │ │ │ │ + if (this.options && !this.options.format) { │ │ │ │ + this.format.destroy(); │ │ │ │ + } │ │ │ │ + this.format = null; │ │ │ │ + OpenLayers.Protocol.prototype.destroy.apply(this); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: pinchMove │ │ │ │ + * APIMethod: read │ │ │ │ + * Construct a request for reading new sensor positions. This is done by │ │ │ │ + * issuing one GetFeatureOfInterest request. │ │ │ │ + */ │ │ │ │ + read: function(options) { │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.options || {}); │ │ │ │ + var response = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "read" │ │ │ │ + }); │ │ │ │ + var format = this.format; │ │ │ │ + var data = OpenLayers.Format.XML.prototype.write.apply(format, │ │ │ │ + [format.writeNode("sos:GetFeatureOfInterest", { │ │ │ │ + fois: this.fois │ │ │ │ + })] │ │ │ │ + ); │ │ │ │ + response.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, response, options), │ │ │ │ + data: data │ │ │ │ + }); │ │ │ │ + return response; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: handleRead │ │ │ │ + * Deal with response from the read request. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * pinchData - {Object} pinch data object related to the current touchmove │ │ │ │ - * of the pinch gesture. This give us the current scale of the pinch. │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} The response object to pass │ │ │ │ + * to the user callback. │ │ │ │ + * options - {Object} The user options passed to the read call. │ │ │ │ */ │ │ │ │ - pinchMove: function(evt, pinchData) { │ │ │ │ - var scale = pinchData.scale; │ │ │ │ - var containerOrigin = this.map.layerContainerOriginPx; │ │ │ │ - var pinchOrigin = this.pinchOrigin; │ │ │ │ - var current = (this.preserveCenter) ? │ │ │ │ - this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy; │ │ │ │ - │ │ │ │ - var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x)); │ │ │ │ - var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y)); │ │ │ │ - │ │ │ │ - this.map.applyTransform(dx, dy, scale); │ │ │ │ - this.currentCenter = current; │ │ │ │ + handleRead: function(response, options) { │ │ │ │ + if (options.callback) { │ │ │ │ + var request = response.priv; │ │ │ │ + if (request.status >= 200 && request.status < 300) { │ │ │ │ + // success │ │ │ │ + response.features = this.parseFeatures(request); │ │ │ │ + response.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ + } else { │ │ │ │ + // failure │ │ │ │ + response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + } │ │ │ │ + options.callback.call(options.scope, response); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: pinchDone │ │ │ │ + * Method: parseFeatures │ │ │ │ + * Read HTTP response body and return features │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * start - {Object} pinch data object related to the touchstart event that │ │ │ │ - * started the pinch gesture. │ │ │ │ - * last - {Object} pinch data object related to the last touchmove event │ │ │ │ - * of the pinch gesture. This give us the final scale of the pinch. │ │ │ │ + * request - {XMLHttpRequest} The request object │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array({<OpenLayers.Feature.Vector>})} Array of features │ │ │ │ */ │ │ │ │ - pinchDone: function(evt, start, last) { │ │ │ │ - this.map.applyTransform(); │ │ │ │ - var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true); │ │ │ │ - if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) { │ │ │ │ - var resolution = this.map.getResolutionForZoom(zoom); │ │ │ │ - │ │ │ │ - var location = this.map.getLonLatFromPixel(this.pinchOrigin); │ │ │ │ - var zoomPixel = this.currentCenter; │ │ │ │ - var size = this.map.getSize(); │ │ │ │ - │ │ │ │ - location.lon += resolution * ((size.w / 2) - zoomPixel.x); │ │ │ │ - location.lat -= resolution * ((size.h / 2) - zoomPixel.y); │ │ │ │ - │ │ │ │ - // Force a reflow before calling setCenter. This is to work │ │ │ │ - // around an issue occuring in iOS. │ │ │ │ - // │ │ │ │ - // See https://github.com/openlayers/openlayers/pull/351. │ │ │ │ - // │ │ │ │ - // Without a reflow setting the layer container div's top left │ │ │ │ - // style properties to "0px" - as done in Map.moveTo when zoom │ │ │ │ - // is changed - won't actually correctly reposition the layer │ │ │ │ - // container div. │ │ │ │ - // │ │ │ │ - // Also, we need to use a statement that the Google Closure │ │ │ │ - // compiler won't optimize away. │ │ │ │ - this.map.div.clientWidth = this.map.div.clientWidth; │ │ │ │ - │ │ │ │ - this.map.setCenter(location, zoom); │ │ │ │ + parseFeatures: function(request) { │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText; │ │ │ │ + } │ │ │ │ + if (!doc || doc.length <= 0) { │ │ │ │ + return null; │ │ │ │ } │ │ │ │ + return this.format.read(doc); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.PinchZoom" │ │ │ │ - │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.SOS.v1_0_0" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/ZoomIn.js │ │ │ │ + OpenLayers/Format/CSWGetRecords.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control/Button.js │ │ │ │ + * @requires OpenLayers/Format.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.ZoomIn │ │ │ │ - * The ZoomIn control is a button to increase the zoom level of a map. │ │ │ │ + * Class: OpenLayers.Format.CSWGetRecords │ │ │ │ + * Default version is 2.0.2. │ │ │ │ * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Format>} A CSWGetRecords format of the given version. │ │ │ │ */ │ │ │ │ -OpenLayers.Control.ZoomIn = OpenLayers.Class(OpenLayers.Control.Button, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: trigger │ │ │ │ - */ │ │ │ │ - trigger: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.zoomIn(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ +OpenLayers.Format.CSWGetRecords = function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults( │ │ │ │ + options, OpenLayers.Format.CSWGetRecords.DEFAULTS │ │ │ │ + ); │ │ │ │ + var cls = OpenLayers.Format.CSWGetRecords["v" + options.version.replace(/\./g, "_")]; │ │ │ │ + if (!cls) { │ │ │ │ + throw "Unsupported CSWGetRecords version: " + options.version; │ │ │ │ + } │ │ │ │ + return new cls(options); │ │ │ │ +}; │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ZoomIn" │ │ │ │ -}); │ │ │ │ +/** │ │ │ │ + * Constant: DEFAULTS │ │ │ │ + * {Object} Default properties for the CSWGetRecords format. │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.CSWGetRecords.DEFAULTS = { │ │ │ │ + "version": "2.0.2" │ │ │ │ +}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/ScaleLine.js │ │ │ │ + OpenLayers/Format/CSWGetRecords/v2_0_2.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Format/CSWGetRecords.js │ │ │ │ + * @requires OpenLayers/Format/Filter/v1_0_0.js │ │ │ │ + * @requires OpenLayers/Format/Filter/v1_1_0.js │ │ │ │ + * @requires OpenLayers/Format/OWSCommon/v1_0_0.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.ScaleLine │ │ │ │ - * The ScaleLine displays a small line indicator representing the current │ │ │ │ - * map scale on the map. By default it is drawn in the lower left corner of │ │ │ │ - * the map. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Format.CSWGetRecords.v2_0_2 │ │ │ │ + * A format for creating CSWGetRecords v2.0.2 transactions. │ │ │ │ + * Create a new instance with the │ │ │ │ + * <OpenLayers.Format.CSWGetRecords.v2_0_2> constructor. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - * │ │ │ │ - * Is a very close copy of: │ │ │ │ - * - <OpenLayers.Control.Scale> │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ +OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: maxWidth │ │ │ │ - * {Integer} Maximum width of the scale line in pixels. Default is 100. │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ */ │ │ │ │ - maxWidth: 100, │ │ │ │ + namespaces: { │ │ │ │ + csw: "http://www.opengis.net/cat/csw/2.0.2", │ │ │ │ + dc: "http://purl.org/dc/elements/1.1/", │ │ │ │ + dct: "http://purl.org/dc/terms/", │ │ │ │ + gmd: "http://www.isotc211.org/2005/gmd", │ │ │ │ + geonet: "http://www.fao.org/geonetwork", │ │ │ │ + ogc: "http://www.opengis.net/ogc", │ │ │ │ + ows: "http://www.opengis.net/ows", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: topOutUnits │ │ │ │ - * {String} Units for zoomed out on top bar. Default is km. │ │ │ │ + * Property: defaultPrefix │ │ │ │ + * {String} The default prefix (used by Format.XML). │ │ │ │ */ │ │ │ │ - topOutUnits: "km", │ │ │ │ + defaultPrefix: "csw", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: topInUnits │ │ │ │ - * {String} Units for zoomed in on top bar. Default is m. │ │ │ │ + * Property: version │ │ │ │ + * {String} CSW version number. │ │ │ │ */ │ │ │ │ - topInUnits: "m", │ │ │ │ + version: "2.0.2", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: bottomOutUnits │ │ │ │ - * {String} Units for zoomed out on bottom bar. Default is mi. │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} http://www.opengis.net/cat/csw/2.0.2 │ │ │ │ + * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd │ │ │ │ */ │ │ │ │ - bottomOutUnits: "mi", │ │ │ │ + schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: bottomInUnits │ │ │ │ - * {String} Units for zoomed in on bottom bar. Default is ft. │ │ │ │ + * APIProperty: requestId │ │ │ │ + * {String} Value of the requestId attribute of the GetRecords element. │ │ │ │ */ │ │ │ │ - bottomInUnits: "ft", │ │ │ │ + requestId: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: eTop │ │ │ │ - * {DOMElement} │ │ │ │ + * APIProperty: resultType │ │ │ │ + * {String} Value of the resultType attribute of the GetRecords element, │ │ │ │ + * specifies the result type in the GetRecords response, "hits" is │ │ │ │ + * the default. │ │ │ │ */ │ │ │ │ - eTop: null, │ │ │ │ + resultType: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: eBottom │ │ │ │ - * {DOMElement} │ │ │ │ + * APIProperty: outputFormat │ │ │ │ + * {String} Value of the outputFormat attribute of the GetRecords element, │ │ │ │ + * specifies the format of the GetRecords response, │ │ │ │ + * "application/xml" is the default. │ │ │ │ */ │ │ │ │ - eBottom: null, │ │ │ │ + outputFormat: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: geodesic │ │ │ │ - * {Boolean} Use geodesic measurement. Default is false. The recommended │ │ │ │ - * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to │ │ │ │ - * true, the scale will be calculated based on the horizontal size of the │ │ │ │ - * pixel in the center of the map viewport. │ │ │ │ + * APIProperty: outputSchema │ │ │ │ + * {String} Value of the outputSchema attribute of the GetRecords element, │ │ │ │ + * specifies the schema of the GetRecords response. │ │ │ │ */ │ │ │ │ - geodesic: false, │ │ │ │ + outputSchema: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.ScaleLine │ │ │ │ - * Create a new scale line control. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be used │ │ │ │ - * to extend the control. │ │ │ │ + * APIProperty: startPosition │ │ │ │ + * {String} Value of the startPosition attribute of the GetRecords element, │ │ │ │ + * specifies the start position (offset+1) for the GetRecords response, │ │ │ │ + * 1 is the default. │ │ │ │ */ │ │ │ │ + startPosition: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * APIProperty: maxRecords │ │ │ │ + * {String} Value of the maxRecords attribute of the GetRecords element, │ │ │ │ + * specifies the maximum number of records in the GetRecords response, │ │ │ │ + * 10 is the default. │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (!this.eTop) { │ │ │ │ - // stick in the top bar │ │ │ │ - this.eTop = document.createElement("div"); │ │ │ │ - this.eTop.className = this.displayClass + "Top"; │ │ │ │ - var theLen = this.topInUnits.length; │ │ │ │ - this.div.appendChild(this.eTop); │ │ │ │ - if ((this.topOutUnits == "") || (this.topInUnits == "")) { │ │ │ │ - this.eTop.style.visibility = "hidden"; │ │ │ │ - } else { │ │ │ │ - this.eTop.style.visibility = "visible"; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // and the bottom bar │ │ │ │ - this.eBottom = document.createElement("div"); │ │ │ │ - this.eBottom.className = this.displayClass + "Bottom"; │ │ │ │ - this.div.appendChild(this.eBottom); │ │ │ │ - if ((this.bottomOutUnits == "") || (this.bottomInUnits == "")) { │ │ │ │ - this.eBottom.style.visibility = "hidden"; │ │ │ │ - } else { │ │ │ │ - this.eBottom.style.visibility = "visible"; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.map.events.register('moveend', this, this.update); │ │ │ │ - this.update(); │ │ │ │ - return this.div; │ │ │ │ - }, │ │ │ │ + maxRecords: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getBarLen │ │ │ │ - * Given a number, round it down to the nearest 1,2,5 times a power of 10. │ │ │ │ - * That seems a fairly useful set of number groups to use. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * maxLen - {float} the number we're rounding down from │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Float} the rounded number (less than or equal to maxLen) │ │ │ │ + /** │ │ │ │ + * APIProperty: DistributedSearch │ │ │ │ + * {String} Value of the csw:DistributedSearch element, used when writing │ │ │ │ + * a csw:GetRecords document. │ │ │ │ */ │ │ │ │ - getBarLen: function(maxLen) { │ │ │ │ - // nearest power of 10 lower than maxLen │ │ │ │ - var digits = parseInt(Math.log(maxLen) / Math.log(10)); │ │ │ │ - var pow10 = Math.pow(10, digits); │ │ │ │ + DistributedSearch: null, │ │ │ │ │ │ │ │ - // ok, find first character │ │ │ │ - var firstChar = parseInt(maxLen / pow10); │ │ │ │ + /** │ │ │ │ + * APIProperty: ResponseHandler │ │ │ │ + * {Array({String})} Values of the csw:ResponseHandler elements, used when │ │ │ │ + * writting a csw:GetRecords document. │ │ │ │ + */ │ │ │ │ + ResponseHandler: null, │ │ │ │ │ │ │ │ - // right, put it into the correct bracket │ │ │ │ - var barLen; │ │ │ │ - if (firstChar > 5) { │ │ │ │ - barLen = 5; │ │ │ │ - } else if (firstChar > 2) { │ │ │ │ - barLen = 2; │ │ │ │ - } else { │ │ │ │ - barLen = 1; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: Query │ │ │ │ + * {String} Value of the csw:Query element, used when writing a csw:GetRecords │ │ │ │ + * document. │ │ │ │ + */ │ │ │ │ + Query: null, │ │ │ │ │ │ │ │ - // scale it up the correct power of 10 │ │ │ │ - return barLen * pow10; │ │ │ │ + /** │ │ │ │ + * Property: regExes │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ + */ │ │ │ │ + regExes: { │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: update │ │ │ │ - * Update the size of the bars, and the labels they contain. │ │ │ │ + * Constructor: OpenLayers.Format.CSWGetRecords.v2_0_2 │ │ │ │ + * A class for parsing and generating CSWGetRecords v2.0.2 transactions. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ + * │ │ │ │ + * Valid options properties (documented as class properties): │ │ │ │ + * - requestId │ │ │ │ + * - resultType │ │ │ │ + * - outputFormat │ │ │ │ + * - outputSchema │ │ │ │ + * - startPosition │ │ │ │ + * - maxRecords │ │ │ │ + * - DistributedSearch │ │ │ │ + * - ResponseHandler │ │ │ │ + * - Query │ │ │ │ */ │ │ │ │ - update: function() { │ │ │ │ - var res = this.map.getResolution(); │ │ │ │ - if (!res) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var curMapUnits = this.map.getUnits(); │ │ │ │ - var inches = OpenLayers.INCHES_PER_UNIT; │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // convert maxWidth to map units │ │ │ │ - var maxSizeData = this.maxWidth * res * inches[curMapUnits]; │ │ │ │ - var geodesicRatio = 1; │ │ │ │ - if (this.geodesic === true) { │ │ │ │ - var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w || │ │ │ │ - 0.000001) * this.maxWidth; │ │ │ │ - var maxSizeKilometers = maxSizeData / inches["km"]; │ │ │ │ - geodesicRatio = maxSizeGeodesic / maxSizeKilometers; │ │ │ │ - maxSizeData *= geodesicRatio; │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Parse the response from a GetRecords request. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ } │ │ │ │ - │ │ │ │ - // decide whether to use large or small scale units │ │ │ │ - var topUnits; │ │ │ │ - var bottomUnits; │ │ │ │ - if (maxSizeData > 100000) { │ │ │ │ - topUnits = this.topOutUnits; │ │ │ │ - bottomUnits = this.bottomOutUnits; │ │ │ │ - } else { │ │ │ │ - topUnits = this.topInUnits; │ │ │ │ - bottomUnits = this.bottomInUnits; │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ } │ │ │ │ + var obj = {}; │ │ │ │ + this.readNode(data, obj); │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // and to map units units │ │ │ │ - var topMax = maxSizeData / inches[topUnits]; │ │ │ │ - var bottomMax = maxSizeData / inches[bottomUnits]; │ │ │ │ - │ │ │ │ - // now trim this down to useful block length │ │ │ │ - var topRounded = this.getBarLen(topMax); │ │ │ │ - var bottomRounded = this.getBarLen(bottomMax); │ │ │ │ - │ │ │ │ - // and back to display units │ │ │ │ - topMax = topRounded / inches[curMapUnits] * inches[topUnits]; │ │ │ │ - bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits]; │ │ │ │ - │ │ │ │ - // and to pixel units │ │ │ │ - var topPx = topMax / res / geodesicRatio; │ │ │ │ - var bottomPx = bottomMax / res / geodesicRatio; │ │ │ │ - │ │ │ │ - // now set the pixel widths │ │ │ │ - // and the values inside them │ │ │ │ - │ │ │ │ - if (this.eBottom.style.visibility == "visible") { │ │ │ │ - this.eBottom.style.width = Math.round(bottomPx) + "px"; │ │ │ │ - this.eBottom.innerHTML = bottomRounded + " " + bottomUnits; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "csw": { │ │ │ │ + "GetRecordsResponse": function(node, obj) { │ │ │ │ + obj.records = []; │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + var version = this.getAttributeNS(node, "", 'version'); │ │ │ │ + if (version != "") { │ │ │ │ + obj.version = version; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "RequestId": function(node, obj) { │ │ │ │ + obj.RequestId = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "SearchStatus": function(node, obj) { │ │ │ │ + obj.SearchStatus = {}; │ │ │ │ + var timestamp = this.getAttributeNS(node, "", 'timestamp'); │ │ │ │ + if (timestamp != "") { │ │ │ │ + obj.SearchStatus.timestamp = timestamp; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "SearchResults": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + var attrs = node.attributes; │ │ │ │ + var SearchResults = {}; │ │ │ │ + for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ + if ((attrs[i].name == "numberOfRecordsMatched") || │ │ │ │ + (attrs[i].name == "numberOfRecordsReturned") || │ │ │ │ + (attrs[i].name == "nextRecord")) { │ │ │ │ + SearchResults[attrs[i].name] = parseInt(attrs[i].nodeValue); │ │ │ │ + } else { │ │ │ │ + SearchResults[attrs[i].name] = attrs[i].nodeValue; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + obj.SearchResults = SearchResults; │ │ │ │ + }, │ │ │ │ + "SummaryRecord": function(node, obj) { │ │ │ │ + var record = { │ │ │ │ + type: "SummaryRecord" │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, record); │ │ │ │ + obj.records.push(record); │ │ │ │ + }, │ │ │ │ + "BriefRecord": function(node, obj) { │ │ │ │ + var record = { │ │ │ │ + type: "BriefRecord" │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, record); │ │ │ │ + obj.records.push(record); │ │ │ │ + }, │ │ │ │ + "DCMIRecord": function(node, obj) { │ │ │ │ + var record = { │ │ │ │ + type: "DCMIRecord" │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, record); │ │ │ │ + obj.records.push(record); │ │ │ │ + }, │ │ │ │ + "Record": function(node, obj) { │ │ │ │ + var record = { │ │ │ │ + type: "Record" │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, record); │ │ │ │ + obj.records.push(record); │ │ │ │ + }, │ │ │ │ + "*": function(node, obj) { │ │ │ │ + var name = node.localName || node.nodeName.split(":").pop(); │ │ │ │ + obj[name] = this.getChildValue(node); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "geonet": { │ │ │ │ + "info": function(node, obj) { │ │ │ │ + var gninfo = {}; │ │ │ │ + this.readChildNodes(node, gninfo); │ │ │ │ + obj.gninfo = gninfo; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "dc": { │ │ │ │ + // audience, contributor, coverage, creator, date, description, format, │ │ │ │ + // identifier, language, provenance, publisher, relation, rights, │ │ │ │ + // rightsHolder, source, subject, title, type, URI │ │ │ │ + "*": function(node, obj) { │ │ │ │ + var name = node.localName || node.nodeName.split(":").pop(); │ │ │ │ + if (!(OpenLayers.Util.isArray(obj[name]))) { │ │ │ │ + obj[name] = []; │ │ │ │ + } │ │ │ │ + var dc_element = {}; │ │ │ │ + var attrs = node.attributes; │ │ │ │ + for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ + dc_element[attrs[i].name] = attrs[i].nodeValue; │ │ │ │ + } │ │ │ │ + dc_element.value = this.getChildValue(node); │ │ │ │ + if (dc_element.value != "") { │ │ │ │ + obj[name].push(dc_element); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "dct": { │ │ │ │ + // abstract, modified, spatial │ │ │ │ + "*": function(node, obj) { │ │ │ │ + var name = node.localName || node.nodeName.split(":").pop(); │ │ │ │ + if (!(OpenLayers.Util.isArray(obj[name]))) { │ │ │ │ + obj[name] = []; │ │ │ │ + } │ │ │ │ + obj[name].push(this.getChildValue(node)); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "ows": OpenLayers.Util.applyDefaults({ │ │ │ │ + "BoundingBox": function(node, obj) { │ │ │ │ + if (obj.bounds) { │ │ │ │ + obj.BoundingBox = [{ │ │ │ │ + crs: obj.projection, │ │ │ │ + value: [ │ │ │ │ + obj.bounds.left, │ │ │ │ + obj.bounds.bottom, │ │ │ │ + obj.bounds.right, │ │ │ │ + obj.bounds.top │ │ │ │ + ] │ │ │ │ + }]; │ │ │ │ + delete obj.projection; │ │ │ │ + delete obj.bounds; │ │ │ │ + } │ │ │ │ + OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"]["BoundingBox"].apply( │ │ │ │ + this, arguments); │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"]) │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.eTop.style.visibility == "visible") { │ │ │ │ - this.eTop.style.width = Math.round(topPx) + "px"; │ │ │ │ - this.eTop.innerHTML = topRounded + " " + topUnits; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: write │ │ │ │ + * Given an configuration js object, write a CSWGetRecords request. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} A object mapping the request. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A serialized CSWGetRecords request. │ │ │ │ + */ │ │ │ │ + write: function(options) { │ │ │ │ + var node = this.writeNode("csw:GetRecords", options); │ │ │ │ + node.setAttribute("xmlns:gmd", this.namespaces.gmd); │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ + }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Property: writers │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ + * node names they produce. │ │ │ │ + */ │ │ │ │ + writers: { │ │ │ │ + "csw": { │ │ │ │ + "GetRecords": function(options) { │ │ │ │ + if (!options) { │ │ │ │ + options = {}; │ │ │ │ + } │ │ │ │ + var node = this.createElementNSPlus("csw:GetRecords", { │ │ │ │ + attributes: { │ │ │ │ + service: "CSW", │ │ │ │ + version: this.version, │ │ │ │ + requestId: options.requestId || this.requestId, │ │ │ │ + resultType: options.resultType || this.resultType, │ │ │ │ + outputFormat: options.outputFormat || this.outputFormat, │ │ │ │ + outputSchema: options.outputSchema || this.outputSchema, │ │ │ │ + startPosition: options.startPosition || this.startPosition, │ │ │ │ + maxRecords: options.maxRecords || this.maxRecords │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + if (options.DistributedSearch || this.DistributedSearch) { │ │ │ │ + this.writeNode( │ │ │ │ + "csw:DistributedSearch", │ │ │ │ + options.DistributedSearch || this.DistributedSearch, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + var ResponseHandler = options.ResponseHandler || this.ResponseHandler; │ │ │ │ + if (OpenLayers.Util.isArray(ResponseHandler) && ResponseHandler.length > 0) { │ │ │ │ + // ResponseHandler must be a non-empty array │ │ │ │ + for (var i = 0, len = ResponseHandler.length; i < len; i++) { │ │ │ │ + this.writeNode( │ │ │ │ + "csw:ResponseHandler", │ │ │ │ + ResponseHandler[i], │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.writeNode("Query", options.Query || this.Query, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "DistributedSearch": function(options) { │ │ │ │ + var node = this.createElementNSPlus("csw:DistributedSearch", { │ │ │ │ + attributes: { │ │ │ │ + hopCount: options.hopCount │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "ResponseHandler": function(options) { │ │ │ │ + var node = this.createElementNSPlus("csw:ResponseHandler", { │ │ │ │ + value: options.value │ │ │ │ + }); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Query": function(options) { │ │ │ │ + if (!options) { │ │ │ │ + options = {}; │ │ │ │ + } │ │ │ │ + var node = this.createElementNSPlus("csw:Query", { │ │ │ │ + attributes: { │ │ │ │ + typeNames: options.typeNames || "csw:Record" │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + var ElementName = options.ElementName; │ │ │ │ + if (OpenLayers.Util.isArray(ElementName) && ElementName.length > 0) { │ │ │ │ + // ElementName must be a non-empty array │ │ │ │ + for (var i = 0, len = ElementName.length; i < len; i++) { │ │ │ │ + this.writeNode( │ │ │ │ + "csw:ElementName", │ │ │ │ + ElementName[i], │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.writeNode( │ │ │ │ + "csw:ElementSetName", │ │ │ │ + options.ElementSetName || { │ │ │ │ + value: 'summary' │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (options.Constraint) { │ │ │ │ + this.writeNode( │ │ │ │ + "csw:Constraint", │ │ │ │ + options.Constraint, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (options.SortBy) { │ │ │ │ + this.writeNode( │ │ │ │ + "ogc:SortBy", │ │ │ │ + options.SortBy, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "ElementName": function(options) { │ │ │ │ + var node = this.createElementNSPlus("csw:ElementName", { │ │ │ │ + value: options.value │ │ │ │ + }); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "ElementSetName": function(options) { │ │ │ │ + var node = this.createElementNSPlus("csw:ElementSetName", { │ │ │ │ + attributes: { │ │ │ │ + typeNames: options.typeNames │ │ │ │ + }, │ │ │ │ + value: options.value │ │ │ │ + }); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Constraint": function(options) { │ │ │ │ + var node = this.createElementNSPlus("csw:Constraint", { │ │ │ │ + attributes: { │ │ │ │ + version: options.version │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + if (options.Filter) { │ │ │ │ + var format = new OpenLayers.Format.Filter({ │ │ │ │ + version: options.version │ │ │ │ + }); │ │ │ │ + node.appendChild(format.write(options.Filter)); │ │ │ │ + } else if (options.CqlText) { │ │ │ │ + var child = this.createElementNSPlus("CqlText", { │ │ │ │ + value: options.CqlText.value │ │ │ │ + }); │ │ │ │ + node.appendChild(child); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers["ogc"] │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ScaleLine" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.CSWGetRecords.v2_0_2" │ │ │ │ }); │ │ │ │ - │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Handler/Hover.js │ │ │ │ + OpenLayers/Protocol/CSW/v2_0_2.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Handler.js │ │ │ │ + * @requires OpenLayers/Protocol/CSW.js │ │ │ │ + * @requires OpenLayers/Format/CSWGetRecords/v2_0_2.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Handler.Hover │ │ │ │ - * The hover handler is to be used to emulate mouseovers on objects │ │ │ │ - * on the map that aren't DOM elements. For example one can use │ │ │ │ - * this handler to send WMS/GetFeatureInfo requests as the user │ │ │ │ - * moves the mouve over the map. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Protocol.CSW.v2_0_2 │ │ │ │ + * CS-W (Catalogue services for the Web) version 2.0.2 protocol. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Handler> │ │ │ │ + * - <OpenLayers.Protocol> │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ +OpenLayers.Protocol.CSW.v2_0_2 = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: delay │ │ │ │ - * {Integer} - Number of milliseconds between mousemoves before │ │ │ │ - * the event is considered a hover. Default is 500. │ │ │ │ + * Property: formatOptions │ │ │ │ + * {Object} Optional options for the format. If a format is not provided, │ │ │ │ + * this property can be used to extend the default format options. │ │ │ │ */ │ │ │ │ - delay: 500, │ │ │ │ + formatOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: pixelTolerance │ │ │ │ - * {Integer} - Maximum number of pixels between mousemoves for │ │ │ │ - * an event to be considered a hover. Default is null. │ │ │ │ + * Constructor: OpenLayers.Protocol.CSW.v2_0_2 │ │ │ │ + * A class for CSW version 2.0.2 protocol management. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ - pixelTolerance: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.initialize.apply(this, [options]); │ │ │ │ + if (!options.format) { │ │ │ │ + this.format = new OpenLayers.Format.CSWGetRecords.v2_0_2(OpenLayers.Util.extend({}, this.formatOptions)); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: stopMove │ │ │ │ - * {Boolean} - Stop other listeners from being notified on mousemoves. │ │ │ │ - * Default is false. │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up the protocol. │ │ │ │ */ │ │ │ │ - stopMove: false, │ │ │ │ + destroy: function() { │ │ │ │ + if (this.options && !this.options.format) { │ │ │ │ + this.format.destroy(); │ │ │ │ + } │ │ │ │ + this.format = null; │ │ │ │ + OpenLayers.Protocol.prototype.destroy.apply(this); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: px │ │ │ │ - * {<OpenLayers.Pixel>} - The location of the last mousemove, expressed │ │ │ │ - * in pixels. │ │ │ │ + * Method: read │ │ │ │ + * Construct a request for reading new records from the Catalogue. │ │ │ │ */ │ │ │ │ - px: null, │ │ │ │ + read: function(options) { │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.options || {}); │ │ │ │ + var response = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "read" │ │ │ │ + }); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: timerId │ │ │ │ - * {Number} - The id of the timer. │ │ │ │ - */ │ │ │ │ - timerId: null, │ │ │ │ + var data = this.format.write(options.params || options); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Handler.Hover │ │ │ │ - * Construct a hover handler. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} The control that initialized this │ │ │ │ - * handler. The control is assumed to have a valid map property; that │ │ │ │ - * map is used in the handler's own setMap method. │ │ │ │ - * callbacks - {Object} An object with keys corresponding to callbacks │ │ │ │ - * that will be called by the handler. The callbacks should │ │ │ │ - * expect to receive a single argument, the event. Callbacks for │ │ │ │ - * 'move', the mouse is moving, and 'pause', the mouse is pausing, │ │ │ │ - * are supported. │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * the handler. │ │ │ │ - */ │ │ │ │ + response.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, response, options), │ │ │ │ + params: options.params, │ │ │ │ + headers: options.headers, │ │ │ │ + data: data │ │ │ │ + }); │ │ │ │ + │ │ │ │ + return response; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: mousemove │ │ │ │ - * Called when the mouse moves on the map. │ │ │ │ + * Method: handleRead │ │ │ │ + * Deal with response from the read request. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Continue propagating this event. │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} The response object to pass │ │ │ │ + * to the user callback. │ │ │ │ + * This response is given a code property, and optionally a data property. │ │ │ │ + * The latter represents the CSW records as returned by the call to │ │ │ │ + * the CSW format read method. │ │ │ │ + * options - {Object} The user options passed to the read call. │ │ │ │ */ │ │ │ │ - mousemove: function(evt) { │ │ │ │ - if (this.passesTolerance(evt.xy)) { │ │ │ │ - this.clearTimer(); │ │ │ │ - this.callback('move', [evt]); │ │ │ │ - this.px = evt.xy; │ │ │ │ - // clone the evt so original properties can be accessed even │ │ │ │ - // if the browser deletes them during the delay │ │ │ │ - evt = OpenLayers.Util.extend({}, evt); │ │ │ │ - this.timerId = window.setTimeout( │ │ │ │ - OpenLayers.Function.bind(this.delayedCall, this, evt), │ │ │ │ - this.delay │ │ │ │ - ); │ │ │ │ + handleRead: function(response, options) { │ │ │ │ + if (options.callback) { │ │ │ │ + var request = response.priv; │ │ │ │ + if (request.status >= 200 && request.status < 300) { │ │ │ │ + // success │ │ │ │ + response.data = this.parseData(request); │ │ │ │ + response.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ + } else { │ │ │ │ + // failure │ │ │ │ + response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + } │ │ │ │ + options.callback.call(options.scope, response); │ │ │ │ } │ │ │ │ - return !this.stopMove; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: mouseout │ │ │ │ - * Called when the mouse goes out of the map. │ │ │ │ + * Method: parseData │ │ │ │ + * Read HTTP response body and return records │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * request - {XMLHttpRequest} The request object │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Continue propagating this event. │ │ │ │ + * {Object} The CSW records as returned by the call to the format read method. │ │ │ │ */ │ │ │ │ - mouseout: function(evt) { │ │ │ │ - if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) { │ │ │ │ - this.clearTimer(); │ │ │ │ - this.callback('move', [evt]); │ │ │ │ + parseData: function(request) { │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText; │ │ │ │ } │ │ │ │ - return true; │ │ │ │ + if (!doc || doc.length <= 0) { │ │ │ │ + return null; │ │ │ │ + } │ │ │ │ + return this.format.read(doc); │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.CSW.v2_0_2" │ │ │ │ + │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/Text.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Geometry/Point.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.Text │ │ │ │ + * Read Text format. Create a new instance with the <OpenLayers.Format.Text> │ │ │ │ + * constructor. This reads text which is formatted like CSV text, using │ │ │ │ + * tabs as the seperator by default. It provides parsing of data originally │ │ │ │ + * used in the MapViewerService, described on the wiki. This Format is used │ │ │ │ + * by the <OpenLayers.Layer.Text> class. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, { │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: passesTolerance │ │ │ │ - * Determine whether the mouse move is within the optional pixel tolerance. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The mouse move is within the pixel tolerance. │ │ │ │ + * APIProperty: defaultStyle │ │ │ │ + * defaultStyle allows one to control the default styling of the features. │ │ │ │ + * It should be a symbolizer hash. By default, this is set to match the │ │ │ │ + * Layer.Text behavior, which is to use the default OpenLayers Icon. │ │ │ │ */ │ │ │ │ - passesTolerance: function(px) { │ │ │ │ - var passes = true; │ │ │ │ - if (this.pixelTolerance && this.px) { │ │ │ │ - var dpx = Math.sqrt( │ │ │ │ - Math.pow(this.px.x - px.x, 2) + │ │ │ │ - Math.pow(this.px.y - px.y, 2) │ │ │ │ - ); │ │ │ │ - if (dpx < this.pixelTolerance) { │ │ │ │ - passes = false; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return passes; │ │ │ │ - }, │ │ │ │ + defaultStyle: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clearTimer │ │ │ │ - * Clear the timer and set <timerId> to null. │ │ │ │ + * APIProperty: extractStyles │ │ │ │ + * set to true to extract styles from the TSV files, using information │ │ │ │ + * from the image or icon, iconSize and iconOffset fields. This will result │ │ │ │ + * in features with a symbolizer (style) property set, using the │ │ │ │ + * default symbolizer specified in <defaultStyle>. Set to false if you │ │ │ │ + * wish to use a styleMap or OpenLayers.Style options to style your │ │ │ │ + * layer instead. │ │ │ │ */ │ │ │ │ - clearTimer: function() { │ │ │ │ - if (this.timerId != null) { │ │ │ │ - window.clearTimeout(this.timerId); │ │ │ │ - this.timerId = null; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + extractStyles: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: delayedCall │ │ │ │ - * Triggers pause callback. │ │ │ │ + * Constructor: OpenLayers.Format.Text │ │ │ │ + * Create a new parser for TSV Text. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - delayedCall: function(evt) { │ │ │ │ - this.callback('pause', [evt]); │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + │ │ │ │ + if (options.extractStyles !== false) { │ │ │ │ + options.defaultStyle = { │ │ │ │ + 'externalGraphic': OpenLayers.Util.getImageLocation("marker.png"), │ │ │ │ + 'graphicWidth': 21, │ │ │ │ + 'graphicHeight': 25, │ │ │ │ + 'graphicXOffset': -10.5, │ │ │ │ + 'graphicYOffset': -12.5 │ │ │ │ + }; │ │ │ │ + } │ │ │ │ + │ │ │ │ + OpenLayers.Format.prototype.initialize.apply(this, [options]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the handler. │ │ │ │ + * APIMethod: read │ │ │ │ + * Return a list of features from a Tab Seperated Values text string. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * text - {String} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The handler was successfully deactivated. │ │ │ │ + * Array({<OpenLayers.Feature.Vector>}) │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.clearTimer(); │ │ │ │ - deactivated = true; │ │ │ │ + read: function(text) { │ │ │ │ + var lines = text.split('\n'); │ │ │ │ + var columns; │ │ │ │ + var features = []; │ │ │ │ + // length - 1 to allow for trailing new line │ │ │ │ + for (var lcv = 0; lcv < (lines.length - 1); lcv++) { │ │ │ │ + var currLine = lines[lcv].replace(/^\s*/, '').replace(/\s*$/, ''); │ │ │ │ + │ │ │ │ + if (currLine.charAt(0) != '#') { │ │ │ │ + /* not a comment */ │ │ │ │ + │ │ │ │ + if (!columns) { │ │ │ │ + //First line is columns │ │ │ │ + columns = currLine.split('\t'); │ │ │ │ + } else { │ │ │ │ + var vals = currLine.split('\t'); │ │ │ │ + var geometry = new OpenLayers.Geometry.Point(0, 0); │ │ │ │ + var attributes = {}; │ │ │ │ + var style = this.defaultStyle ? │ │ │ │ + OpenLayers.Util.applyDefaults({}, this.defaultStyle) : │ │ │ │ + null; │ │ │ │ + var icon, iconSize, iconOffset, overflow; │ │ │ │ + var set = false; │ │ │ │ + for (var valIndex = 0; valIndex < vals.length; valIndex++) { │ │ │ │ + if (vals[valIndex]) { │ │ │ │ + if (columns[valIndex] == 'point') { │ │ │ │ + var coords = vals[valIndex].split(','); │ │ │ │ + geometry.y = parseFloat(coords[0]); │ │ │ │ + geometry.x = parseFloat(coords[1]); │ │ │ │ + set = true; │ │ │ │ + } else if (columns[valIndex] == 'lat') { │ │ │ │ + geometry.y = parseFloat(vals[valIndex]); │ │ │ │ + set = true; │ │ │ │ + } else if (columns[valIndex] == 'lon') { │ │ │ │ + geometry.x = parseFloat(vals[valIndex]); │ │ │ │ + set = true; │ │ │ │ + } else if (columns[valIndex] == 'title') │ │ │ │ + attributes['title'] = vals[valIndex]; │ │ │ │ + else if (columns[valIndex] == 'image' || │ │ │ │ + columns[valIndex] == 'icon' && style) { │ │ │ │ + style['externalGraphic'] = vals[valIndex]; │ │ │ │ + } else if (columns[valIndex] == 'iconSize' && style) { │ │ │ │ + var size = vals[valIndex].split(','); │ │ │ │ + style['graphicWidth'] = parseFloat(size[0]); │ │ │ │ + style['graphicHeight'] = parseFloat(size[1]); │ │ │ │ + } else if (columns[valIndex] == 'iconOffset' && style) { │ │ │ │ + var offset = vals[valIndex].split(','); │ │ │ │ + style['graphicXOffset'] = parseFloat(offset[0]); │ │ │ │ + style['graphicYOffset'] = parseFloat(offset[1]); │ │ │ │ + } else if (columns[valIndex] == 'description') { │ │ │ │ + attributes['description'] = vals[valIndex]; │ │ │ │ + } else if (columns[valIndex] == 'overflow') { │ │ │ │ + attributes['overflow'] = vals[valIndex]; │ │ │ │ + } else { │ │ │ │ + // For StyleMap filtering, allow additional │ │ │ │ + // columns to be stored as attributes. │ │ │ │ + attributes[columns[valIndex]] = vals[valIndex]; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (set) { │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry.transform(this.externalProjection, │ │ │ │ + this.internalProjection); │ │ │ │ + } │ │ │ │ + var feature = new OpenLayers.Feature.Vector(geometry, attributes, style); │ │ │ │ + features.push(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - return deactivated; │ │ │ │ + return features; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Hover" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.Text" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/WMSGetFeatureInfo.js │ │ │ │ + OpenLayers/Format/EncodedPolyline.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Format.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.WMSGetFeatureInfo │ │ │ │ - * Class to read GetFeatureInfo responses from Web Mapping Services │ │ │ │ + * Class: OpenLayers.Format.EncodedPolyline │ │ │ │ + * Class for reading and writing encoded polylines. Create a new instance │ │ │ │ + * with the <OpenLayers.Format.EncodedPolyline> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Format> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: layerIdentifier │ │ │ │ - * {String} All xml nodes containing this search criteria will populate an │ │ │ │ - * internal array of layer nodes. │ │ │ │ - */ │ │ │ │ - layerIdentifier: '_layer', │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: featureIdentifier │ │ │ │ - * {String} All xml nodes containing this search criteria will populate an │ │ │ │ - * internal array of feature nodes for each layer node found. │ │ │ │ - */ │ │ │ │ - featureIdentifier: '_feature', │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: regExes │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ - */ │ │ │ │ - regExes: { │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ - }, │ │ │ │ +OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: gmlFormat │ │ │ │ - * {<OpenLayers.Format.GML>} internal GML format for parsing geometries │ │ │ │ - * in msGMLOutput │ │ │ │ + * APIProperty: geometryType │ │ │ │ + * {String} Geometry type to output. One of: linestring (default), │ │ │ │ + * linearring, point, multipoint or polygon. If the geometryType is │ │ │ │ + * point, only the first point of the string is returned. │ │ │ │ */ │ │ │ │ - gmlFormat: null, │ │ │ │ + geometryType: "linestring", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.WMSGetFeatureInfo │ │ │ │ - * Create a new parser for WMS GetFeatureInfo responses │ │ │ │ + * Constructor: OpenLayers.Format.EncodedPolyline │ │ │ │ + * Create a new parser for encoded polylines │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read WMS GetFeatureInfo data from a string, and return an array of features │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * this instance │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} An array of features. │ │ │ │ + * {<OpenLayers.Format.EncodedPolyline>} A new encoded polylines parser. │ │ │ │ */ │ │ │ │ - read: function(data) { │ │ │ │ - var result; │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - } │ │ │ │ - var root = data.documentElement; │ │ │ │ - if (root) { │ │ │ │ - var scope = this; │ │ │ │ - var read = this["read_" + root.nodeName]; │ │ │ │ - if (read) { │ │ │ │ - result = read.call(this, root); │ │ │ │ - } else { │ │ │ │ - // fall-back to GML since this is a common output format for WMS │ │ │ │ - // GetFeatureInfo responses │ │ │ │ - result = new OpenLayers.Format.GML((this.options ? this.options : {})).read(data); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - result = data; │ │ │ │ - } │ │ │ │ - return result; │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.prototype.initialize.apply(this, [options]); │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: read_msGMLOutput │ │ │ │ - * Parse msGMLOutput nodes. │ │ │ │ + * APIMethod: read │ │ │ │ + * Deserialize an encoded polyline string and return a vector feature. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * data - {DOMElement} │ │ │ │ + * encoded - {String} An encoded polyline string │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array} │ │ │ │ + * {<OpenLayers.Feature.Vector>} A vector feature with a linestring. │ │ │ │ */ │ │ │ │ - read_msGMLOutput: function(data) { │ │ │ │ - var response = []; │ │ │ │ - var layerNodes = this.getSiblingNodesByTagCriteria(data, │ │ │ │ - this.layerIdentifier); │ │ │ │ - if (layerNodes) { │ │ │ │ - for (var i = 0, len = layerNodes.length; i < len; ++i) { │ │ │ │ - var node = layerNodes[i]; │ │ │ │ - var layerName = node.nodeName; │ │ │ │ - if (node.prefix) { │ │ │ │ - layerName = layerName.split(':')[1]; │ │ │ │ - } │ │ │ │ - var layerName = layerName.replace(this.layerIdentifier, ''); │ │ │ │ - var featureNodes = this.getSiblingNodesByTagCriteria(node, │ │ │ │ - this.featureIdentifier); │ │ │ │ - if (featureNodes) { │ │ │ │ - for (var j = 0; j < featureNodes.length; j++) { │ │ │ │ - var featureNode = featureNodes[j]; │ │ │ │ - var geomInfo = this.parseGeometry(featureNode); │ │ │ │ - var attributes = this.parseAttributes(featureNode); │ │ │ │ - var feature = new OpenLayers.Feature.Vector(geomInfo.geometry, │ │ │ │ - attributes, null); │ │ │ │ - feature.bounds = geomInfo.bounds; │ │ │ │ - feature.type = layerName; │ │ │ │ - response.push(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + read: function(encoded) { │ │ │ │ + var geomType; │ │ │ │ + if (this.geometryType == "linestring") │ │ │ │ + geomType = OpenLayers.Geometry.LineString; │ │ │ │ + else if (this.geometryType == "linearring") │ │ │ │ + geomType = OpenLayers.Geometry.LinearRing; │ │ │ │ + else if (this.geometryType == "multipoint") │ │ │ │ + geomType = OpenLayers.Geometry.MultiPoint; │ │ │ │ + else if (this.geometryType != "point" && this.geometryType != "polygon") │ │ │ │ + return null; │ │ │ │ + │ │ │ │ + var flatPoints = this.decodeDeltas(encoded, 2); │ │ │ │ + var flatPointsLength = flatPoints.length; │ │ │ │ + │ │ │ │ + var pointGeometries = []; │ │ │ │ + for (var i = 0; i + 1 < flatPointsLength;) { │ │ │ │ + var y = flatPoints[i++], │ │ │ │ + x = flatPoints[i++]; │ │ │ │ + pointGeometries.push(new OpenLayers.Geometry.Point(x, y)); │ │ │ │ } │ │ │ │ - return response; │ │ │ │ + │ │ │ │ + │ │ │ │ + if (this.geometryType == "point") │ │ │ │ + return new OpenLayers.Feature.Vector( │ │ │ │ + pointGeometries[0] │ │ │ │ + ); │ │ │ │ + │ │ │ │ + if (this.geometryType == "polygon") │ │ │ │ + return new OpenLayers.Feature.Vector( │ │ │ │ + new OpenLayers.Geometry.Polygon([ │ │ │ │ + new OpenLayers.Geometry.LinearRing(pointGeometries) │ │ │ │ + ]) │ │ │ │ + ); │ │ │ │ + │ │ │ │ + return new OpenLayers.Feature.Vector( │ │ │ │ + new geomType(pointGeometries) │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_FeatureInfoResponse │ │ │ │ - * Parse FeatureInfoResponse nodes. │ │ │ │ + * APIMethod: decode │ │ │ │ + * Deserialize an encoded string and return an array of n-dimensional │ │ │ │ + * points. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * data - {DOMElement} │ │ │ │ + * encoded - {String} An encoded string │ │ │ │ + * dims - {int} The dimension of the points that are returned │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array} │ │ │ │ + * {Array(Array(int))} An array containing n-dimensional arrays of │ │ │ │ + * coordinates. │ │ │ │ */ │ │ │ │ - read_FeatureInfoResponse: function(data) { │ │ │ │ - var response = []; │ │ │ │ - var featureNodes = this.getElementsByTagNameNS(data, '*', │ │ │ │ - 'FIELDS'); │ │ │ │ + decode: function(encoded, dims, opt_factor) { │ │ │ │ + var factor = opt_factor || 1e5; │ │ │ │ + var flatPoints = this.decodeDeltas(encoded, dims, factor); │ │ │ │ + var flatPointsLength = flatPoints.length; │ │ │ │ │ │ │ │ - for (var i = 0, len = featureNodes.length; i < len; i++) { │ │ │ │ - var featureNode = featureNodes[i]; │ │ │ │ - var geom = null; │ │ │ │ + var points = []; │ │ │ │ + for (var i = 0; i + (dims - 1) < flatPointsLength;) { │ │ │ │ + var point = []; │ │ │ │ │ │ │ │ - // attributes can be actual attributes on the FIELDS tag, │ │ │ │ - // or FIELD children │ │ │ │ - var attributes = {}; │ │ │ │ - var j; │ │ │ │ - var jlen = featureNode.attributes.length; │ │ │ │ - if (jlen > 0) { │ │ │ │ - for (j = 0; j < jlen; j++) { │ │ │ │ - var attribute = featureNode.attributes[j]; │ │ │ │ - attributes[attribute.nodeName] = attribute.nodeValue; │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - var nodes = featureNode.childNodes; │ │ │ │ - for (j = 0, jlen = nodes.length; j < jlen; ++j) { │ │ │ │ - var node = nodes[j]; │ │ │ │ - if (node.nodeType != 3) { │ │ │ │ - attributes[node.getAttribute("name")] = │ │ │ │ - node.getAttribute("value"); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + for (var dim = 0; dim < dims; ++dim) { │ │ │ │ + point.push(flatPoints[i++]) │ │ │ │ } │ │ │ │ │ │ │ │ - response.push( │ │ │ │ - new OpenLayers.Feature.Vector(geom, attributes, null) │ │ │ │ - ); │ │ │ │ + points.push(point); │ │ │ │ } │ │ │ │ - return response; │ │ │ │ + │ │ │ │ + return points; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getSiblingNodesByTagCriteria │ │ │ │ - * Recursively searches passed xml node and all it's descendant levels for │ │ │ │ - * nodes whose tagName contains the passed search string. This returns an │ │ │ │ - * array of all sibling nodes which match the criteria from the highest │ │ │ │ - * hierarchial level from which a match is found. │ │ │ │ - * │ │ │ │ + * APIMethod: write │ │ │ │ + * Serialize a feature or array of features into a WKT string. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} An xml node │ │ │ │ - * criteria - {String} Search string which will match some part of a tagName │ │ │ │ - * │ │ │ │ + * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of │ │ │ │ + * features │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * Array({DOMElement}) An array of sibling xml nodes │ │ │ │ + * {String} The WKT string representation of the input geometries │ │ │ │ */ │ │ │ │ - getSiblingNodesByTagCriteria: function(node, criteria) { │ │ │ │ - var nodes = []; │ │ │ │ - var children, tagName, n, matchNodes, child; │ │ │ │ - if (node && node.hasChildNodes()) { │ │ │ │ - children = node.childNodes; │ │ │ │ - n = children.length; │ │ │ │ + write: function(features) { │ │ │ │ + var feature; │ │ │ │ + if (features.constructor == Array) │ │ │ │ + feature = features[0]; │ │ │ │ + else │ │ │ │ + feature = features; │ │ │ │ │ │ │ │ - for (var k = 0; k < n; k++) { │ │ │ │ - child = children[k]; │ │ │ │ - while (child && child.nodeType != 1) { │ │ │ │ - child = child.nextSibling; │ │ │ │ - k++; │ │ │ │ - } │ │ │ │ - tagName = (child ? child.nodeName : ''); │ │ │ │ - if (tagName.length > 0 && tagName.indexOf(criteria) > -1) { │ │ │ │ - nodes.push(child); │ │ │ │ - } else { │ │ │ │ - matchNodes = this.getSiblingNodesByTagCriteria( │ │ │ │ - child, criteria); │ │ │ │ + var geometry = feature.geometry; │ │ │ │ + var type = geometry.CLASS_NAME.split('.')[2].toLowerCase(); │ │ │ │ │ │ │ │ - if (matchNodes.length > 0) { │ │ │ │ - (nodes.length == 0) ? │ │ │ │ - nodes = matchNodes: nodes.push(matchNodes); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + var pointGeometries; │ │ │ │ + if (type == "point") │ │ │ │ + pointGeometries = new Array(geometry); │ │ │ │ + else if (type == "linestring" || │ │ │ │ + type == "linearring" || │ │ │ │ + type == "multipoint") │ │ │ │ + pointGeometries = geometry.components; │ │ │ │ + else if (type == "polygon") │ │ │ │ + pointGeometries = geometry.components[0].components; │ │ │ │ + else │ │ │ │ + return null; │ │ │ │ + │ │ │ │ + var flatPoints = []; │ │ │ │ │ │ │ │ + var pointGeometriesLength = pointGeometries.length; │ │ │ │ + for (var i = 0; i < pointGeometriesLength; ++i) { │ │ │ │ + var pointGeometry = pointGeometries[i]; │ │ │ │ + flatPoints.push(pointGeometry.y); │ │ │ │ + flatPoints.push(pointGeometry.x); │ │ │ │ } │ │ │ │ - return nodes; │ │ │ │ + │ │ │ │ + return this.encodeDeltas(flatPoints, 2); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseAttributes │ │ │ │ + * APIMethod: encode │ │ │ │ + * Serialize an array of n-dimensional points and return an encoded string │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {<DOMElement>} │ │ │ │ + * points - {Array(Array(int))} An array containing n-dimensional │ │ │ │ + * arrays of coordinates │ │ │ │ + * dims - {int} The dimension of the points that should be read │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} An attributes object. │ │ │ │ - * │ │ │ │ - * Notes: │ │ │ │ - * Assumes that attributes are direct child xml nodes of the passed node │ │ │ │ - * and contain only a single text node. │ │ │ │ + * {String} An encoded string │ │ │ │ */ │ │ │ │ - parseAttributes: function(node) { │ │ │ │ - var attributes = {}; │ │ │ │ - if (node.nodeType == 1) { │ │ │ │ - var children = node.childNodes; │ │ │ │ - var n = children.length; │ │ │ │ - for (var i = 0; i < n; ++i) { │ │ │ │ - var child = children[i]; │ │ │ │ - if (child.nodeType == 1) { │ │ │ │ - var grandchildren = child.childNodes; │ │ │ │ - var name = (child.prefix) ? │ │ │ │ - child.nodeName.split(":")[1] : child.nodeName; │ │ │ │ - if (grandchildren.length == 0) { │ │ │ │ - attributes[name] = null; │ │ │ │ - } else if (grandchildren.length == 1) { │ │ │ │ - var grandchild = grandchildren[0]; │ │ │ │ - if (grandchild.nodeType == 3 || │ │ │ │ - grandchild.nodeType == 4) { │ │ │ │ - var value = grandchild.nodeValue.replace( │ │ │ │ - this.regExes.trimSpace, ""); │ │ │ │ - attributes[name] = value; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + encode: function(points, dims, opt_factor) { │ │ │ │ + var factor = opt_factor || 1e5; │ │ │ │ + var flatPoints = []; │ │ │ │ + │ │ │ │ + var pointsLength = points.length; │ │ │ │ + for (var i = 0; i < pointsLength; ++i) { │ │ │ │ + var point = points[i]; │ │ │ │ + │ │ │ │ + for (var dim = 0; dim < dims; ++dim) { │ │ │ │ + flatPoints.push(point[dim]); │ │ │ │ } │ │ │ │ } │ │ │ │ - return attributes; │ │ │ │ + │ │ │ │ + return this.encodeDeltas(flatPoints, dims, factor); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseGeometry │ │ │ │ - * Parse the geometry and the feature bounds out of the node using │ │ │ │ - * Format.GML │ │ │ │ + * APIMethod: encodeDeltas │ │ │ │ + * Encode a list of n-dimensional points and return an encoded string │ │ │ │ + * │ │ │ │ + * Attention: This function will modify the passed array! │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {<DOMElement>} │ │ │ │ + * numbers - {Array.<number>} A list of n-dimensional points. │ │ │ │ + * dimension - {number} The dimension of the points in the list. │ │ │ │ + * opt_factor - {number=} The factor by which the numbers will be │ │ │ │ + * multiplied. The remaining decimal places will get rounded away. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} An object containing the geometry and the feature bounds │ │ │ │ + * {string} The encoded string. │ │ │ │ */ │ │ │ │ - parseGeometry: function(node) { │ │ │ │ - // we need to use the old Format.GML parser since we do not know the │ │ │ │ - // geometry name │ │ │ │ - if (!this.gmlFormat) { │ │ │ │ - this.gmlFormat = new OpenLayers.Format.GML(); │ │ │ │ - } │ │ │ │ - var feature = this.gmlFormat.parseFeature(node); │ │ │ │ - var geometry, bounds = null; │ │ │ │ - if (feature) { │ │ │ │ - geometry = feature.geometry && feature.geometry.clone(); │ │ │ │ - bounds = feature.bounds && feature.bounds.clone(); │ │ │ │ - feature.destroy(); │ │ │ │ - } │ │ │ │ - return { │ │ │ │ - geometry: geometry, │ │ │ │ - bounds: bounds │ │ │ │ - }; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMSGetFeatureInfo" │ │ │ │ + encodeDeltas: function(numbers, dimension, opt_factor) { │ │ │ │ + var factor = opt_factor || 1e5; │ │ │ │ + var d; │ │ │ │ │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/WMSGetFeatureInfo.js │ │ │ │ - ====================================================================== */ │ │ │ │ + var lastNumbers = new Array(dimension); │ │ │ │ + for (d = 0; d < dimension; ++d) { │ │ │ │ + lastNumbers[d] = 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 numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength;) { │ │ │ │ + for (d = 0; d < dimension; ++d, ++i) { │ │ │ │ + var num = numbers[i]; │ │ │ │ + var delta = num - lastNumbers[d]; │ │ │ │ + lastNumbers[d] = num; │ │ │ │ │ │ │ │ + numbers[i] = delta; │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Handler/Click.js │ │ │ │ - * @requires OpenLayers/Handler/Hover.js │ │ │ │ - * @requires OpenLayers/Request.js │ │ │ │ - * @requires OpenLayers/Format/WMSGetFeatureInfo.js │ │ │ │ - */ │ │ │ │ + return this.encodeFloats(numbers, factor); │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.WMSGetFeatureInfo │ │ │ │ - * The WMSGetFeatureInfo control uses a WMS query to get information about a point on the map. The │ │ │ │ - * information may be in a display-friendly format such as HTML, or a machine-friendly format such │ │ │ │ - * as GML, depending on the server's capabilities and the client's configuration. This control │ │ │ │ - * handles click or hover events, attempts to parse the results using an OpenLayers.Format, and │ │ │ │ - * fires a 'getfeatureinfo' event with the click position, the raw body of the response, and an │ │ │ │ - * array of features if it successfully read the response. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: hover │ │ │ │ - * {Boolean} Send GetFeatureInfo requests when mouse stops moving. │ │ │ │ - * Default is false. │ │ │ │ + * APIMethod: decodeDeltas │ │ │ │ + * Decode a list of n-dimensional points from an encoded string │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * encoded - {string} An encoded string. │ │ │ │ + * dimension - {number} The dimension of the points in the encoded string. │ │ │ │ + * opt_factor - {number=} The factor by which the resulting numbers will │ │ │ │ + * be divided. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array.<number>} A list of n-dimensional points. │ │ │ │ */ │ │ │ │ - hover: false, │ │ │ │ + decodeDeltas: function(encoded, dimension, opt_factor) { │ │ │ │ + var factor = opt_factor || 1e5; │ │ │ │ + var d; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: drillDown │ │ │ │ - * {Boolean} Drill down over all WMS layers in the map. When │ │ │ │ - * using drillDown mode, hover is not possible, and an infoFormat that │ │ │ │ - * returns parseable features is required. Default is false. │ │ │ │ - */ │ │ │ │ - drillDown: false, │ │ │ │ + var lastNumbers = new Array(dimension); │ │ │ │ + for (d = 0; d < dimension; ++d) { │ │ │ │ + lastNumbers[d] = 0; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: maxFeatures │ │ │ │ - * {Integer} Maximum number of features to return from a WMS query. This │ │ │ │ - * sets the feature_count parameter on WMS GetFeatureInfo │ │ │ │ - * requests. │ │ │ │ - */ │ │ │ │ - maxFeatures: 10, │ │ │ │ + var numbers = this.decodeFloats(encoded, factor); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: clickCallback │ │ │ │ - * {String} The click callback to register in the │ │ │ │ - * {<OpenLayers.Handler.Click>} object created when the hover │ │ │ │ - * option is set to false. Default is "click". │ │ │ │ - */ │ │ │ │ - clickCallback: "click", │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength;) { │ │ │ │ + for (d = 0; d < dimension; ++d, ++i) { │ │ │ │ + lastNumbers[d] += numbers[i]; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: output │ │ │ │ - * {String} Either "features" or "object". When triggering a getfeatureinfo │ │ │ │ - * request should we pass on an array of features or an object with with │ │ │ │ - * a "features" property and other properties (such as the url of the │ │ │ │ - * WMS). Default is "features". │ │ │ │ - */ │ │ │ │ - output: "features", │ │ │ │ + numbers[i] = lastNumbers[d]; │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: layers │ │ │ │ - * {Array(<OpenLayers.Layer.WMS>)} The layers to query for feature info. │ │ │ │ - * If omitted, all map WMS layers with a url that matches this <url> or │ │ │ │ - * <layerUrls> will be considered. │ │ │ │ - */ │ │ │ │ - layers: null, │ │ │ │ + return numbers; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: queryVisible │ │ │ │ - * {Boolean} If true, filter out hidden layers when searching the map for │ │ │ │ - * layers to query. Default is false. │ │ │ │ - */ │ │ │ │ - queryVisible: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: url │ │ │ │ - * {String} The URL of the WMS service to use. If not provided, the url │ │ │ │ - * of the first eligible layer will be used. │ │ │ │ + * APIMethod: encodeFloats │ │ │ │ + * Encode a list of floating point numbers and return an encoded string │ │ │ │ + * │ │ │ │ + * Attention: This function will modify the passed array! │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * numbers - {Array.<number>} A list of floating point numbers. │ │ │ │ + * opt_factor - {number=} The factor by which the numbers will be │ │ │ │ + * multiplied. The remaining decimal places will get rounded away. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {string} The encoded string. │ │ │ │ */ │ │ │ │ - url: null, │ │ │ │ + encodeFloats: function(numbers, opt_factor) { │ │ │ │ + var factor = opt_factor || 1e5; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: layerUrls │ │ │ │ - * {Array(String)} Optional list of urls for layers that should be queried. │ │ │ │ - * This can be used when the layer url differs from the url used for │ │ │ │ - * making GetFeatureInfo requests (in the case of a layer using cached │ │ │ │ - * tiles). │ │ │ │ - */ │ │ │ │ - layerUrls: null, │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength; ++i) { │ │ │ │ + numbers[i] = Math.round(numbers[i] * factor); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: infoFormat │ │ │ │ - * {String} The mimetype to request from the server. If you are using │ │ │ │ - * drillDown mode and have multiple servers that do not share a common │ │ │ │ - * infoFormat, you can override the control's infoFormat by providing an │ │ │ │ - * INFO_FORMAT parameter in your <OpenLayers.Layer.WMS> instance(s). │ │ │ │ - */ │ │ │ │ - infoFormat: 'text/html', │ │ │ │ + return this.encodeSignedIntegers(numbers); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: vendorParams │ │ │ │ - * {Object} Additional parameters that will be added to the request, for │ │ │ │ - * WMS implementations that support them. This could e.g. look like │ │ │ │ - * (start code) │ │ │ │ - * { │ │ │ │ - * radius: 5 │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ - vendorParams: {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: format │ │ │ │ - * {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses. │ │ │ │ - * Default is <OpenLayers.Format.WMSGetFeatureInfo>. │ │ │ │ + * APIMethod: decodeFloats │ │ │ │ + * Decode a list of floating point numbers from an encoded string │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * encoded - {string} An encoded string. │ │ │ │ + * opt_factor - {number=} The factor by which the result will be divided. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array.<number>} A list of floating point numbers. │ │ │ │ */ │ │ │ │ - format: null, │ │ │ │ + decodeFloats: function(encoded, opt_factor) { │ │ │ │ + var factor = opt_factor || 1e5; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: formatOptions │ │ │ │ - * {Object} Optional properties to set on the format (if one is not provided │ │ │ │ - * in the <format> property. │ │ │ │ - */ │ │ │ │ - formatOptions: null, │ │ │ │ + var numbers = this.decodeSignedIntegers(encoded); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: handlerOptions │ │ │ │ - * {Object} Additional options for the handlers used by this control, e.g. │ │ │ │ - * (start code) │ │ │ │ - * { │ │ │ │ - * "click": {delay: 100}, │ │ │ │ - * "hover": {delay: 300} │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength; ++i) { │ │ │ │ + numbers[i] /= factor; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: handler │ │ │ │ - * {Object} Reference to the <OpenLayers.Handler> for this control │ │ │ │ - */ │ │ │ │ - handler: null, │ │ │ │ + return numbers; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: hoverRequest │ │ │ │ - * {<OpenLayers.Request>} contains the currently running hover request │ │ │ │ - * (if any). │ │ │ │ - */ │ │ │ │ - hoverRequest: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ + * APIMethod: encodeSignedIntegers │ │ │ │ + * Encode a list of signed integers and return an encoded string │ │ │ │ * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * beforegetfeatureinfo - Triggered before the request is sent. │ │ │ │ - * The event object has an *xy* property with the position of the │ │ │ │ - * mouse click or hover event that triggers the request. │ │ │ │ - * nogetfeatureinfo - no queryable layers were found. │ │ │ │ - * getfeatureinfo - Triggered when a GetFeatureInfo response is received. │ │ │ │ - * The event object has a *text* property with the body of the │ │ │ │ - * response (String), a *features* property with an array of the │ │ │ │ - * parsed features, an *xy* property with the position of the mouse │ │ │ │ - * click or hover event that triggered the request, and a *request* │ │ │ │ - * property with the request itself. If drillDown is set to true and │ │ │ │ - * multiple requests were issued to collect feature info from all │ │ │ │ - * layers, *text* and *request* will only contain the response body │ │ │ │ - * and request object of the last request. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: <OpenLayers.Control.WMSGetFeatureInfo> │ │ │ │ + * Attention: This function will modify the passed array! │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} │ │ │ │ + * numbers - {Array.<number>} A list of signed integers. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {string} The encoded string. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - options.handlerOptions = options.handlerOptions || {}; │ │ │ │ - │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + encodeSignedIntegers: function(numbers) { │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength; ++i) { │ │ │ │ + var num = numbers[i]; │ │ │ │ │ │ │ │ - if (!this.format) { │ │ │ │ - this.format = new OpenLayers.Format.WMSGetFeatureInfo( │ │ │ │ - options.formatOptions │ │ │ │ - ); │ │ │ │ - } │ │ │ │ + var signedNum = num << 1; │ │ │ │ + if (num < 0) { │ │ │ │ + signedNum = ~(signedNum); │ │ │ │ + } │ │ │ │ │ │ │ │ - if (this.drillDown === true) { │ │ │ │ - this.hover = false; │ │ │ │ + numbers[i] = signedNum; │ │ │ │ } │ │ │ │ │ │ │ │ - if (this.hover) { │ │ │ │ - this.handler = new OpenLayers.Handler.Hover( │ │ │ │ - this, { │ │ │ │ - 'move': this.cancelHover, │ │ │ │ - 'pause': this.getInfoForHover │ │ │ │ - }, │ │ │ │ - OpenLayers.Util.extend(this.handlerOptions.hover || {}, { │ │ │ │ - 'delay': 250 │ │ │ │ - })); │ │ │ │ - } else { │ │ │ │ - var callbacks = {}; │ │ │ │ - callbacks[this.clickCallback] = this.getInfoForClick; │ │ │ │ - this.handler = new OpenLayers.Handler.Click( │ │ │ │ - this, callbacks, this.handlerOptions.click || {}); │ │ │ │ - } │ │ │ │ + return this.encodeUnsignedIntegers(numbers); │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: getInfoForClick │ │ │ │ - * Called on click │ │ │ │ + * APIMethod: decodeSignedIntegers │ │ │ │ + * Decode a list of signed integers from an encoded string │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * encoded - {string} An encoded string. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array.<number>} A list of signed integers. │ │ │ │ */ │ │ │ │ - getInfoForClick: function(evt) { │ │ │ │ - this.events.triggerEvent("beforegetfeatureinfo", { │ │ │ │ - xy: evt.xy │ │ │ │ - }); │ │ │ │ - // Set the cursor to "wait" to tell the user we're working on their │ │ │ │ - // click. │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ - this.request(evt.xy, {}); │ │ │ │ + decodeSignedIntegers: function(encoded) { │ │ │ │ + var numbers = this.decodeUnsignedIntegers(encoded); │ │ │ │ + │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength; ++i) { │ │ │ │ + var num = numbers[i]; │ │ │ │ + numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1); │ │ │ │ + } │ │ │ │ + │ │ │ │ + return numbers; │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: getInfoForHover │ │ │ │ - * Pause callback for the hover handler │ │ │ │ + * APIMethod: encodeUnsignedIntegers │ │ │ │ + * Encode a list of unsigned integers and return an encoded string │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} │ │ │ │ + * numbers - {Array.<number>} A list of unsigned integers. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {string} The encoded string. │ │ │ │ */ │ │ │ │ - getInfoForHover: function(evt) { │ │ │ │ - this.events.triggerEvent("beforegetfeatureinfo", { │ │ │ │ - xy: evt.xy │ │ │ │ - }); │ │ │ │ - this.request(evt.xy, { │ │ │ │ - hover: true │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ + encodeUnsignedIntegers: function(numbers) { │ │ │ │ + var encoded = ''; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: cancelHover │ │ │ │ - * Cancel callback for the hover handler │ │ │ │ - */ │ │ │ │ - cancelHover: function() { │ │ │ │ - if (this.hoverRequest) { │ │ │ │ - this.hoverRequest.abort(); │ │ │ │ - this.hoverRequest = null; │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength; ++i) { │ │ │ │ + encoded += this.encodeUnsignedInteger(numbers[i]); │ │ │ │ } │ │ │ │ + │ │ │ │ + return encoded; │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: findLayers │ │ │ │ - * Internal method to get the layers, independent of whether we are │ │ │ │ - * inspecting the map or using a client-provided array │ │ │ │ + * APIMethod: decodeUnsignedIntegers │ │ │ │ + * Decode a list of unsigned integers from an encoded string │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * encoded - {string} An encoded string. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array.<number>} A list of unsigned integers. │ │ │ │ */ │ │ │ │ - findLayers: function() { │ │ │ │ + decodeUnsignedIntegers: function(encoded) { │ │ │ │ + var numbers = []; │ │ │ │ │ │ │ │ - var candidates = this.layers || this.map.layers; │ │ │ │ - var layers = []; │ │ │ │ - var layer, url; │ │ │ │ - for (var i = candidates.length - 1; i >= 0; --i) { │ │ │ │ - layer = candidates[i]; │ │ │ │ - if (layer instanceof OpenLayers.Layer.WMS && │ │ │ │ - (!this.queryVisible || layer.getVisibility())) { │ │ │ │ - url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url; │ │ │ │ - // if the control was not configured with a url, set it │ │ │ │ - // to the first layer url │ │ │ │ - if (this.drillDown === false && !this.url) { │ │ │ │ - this.url = url; │ │ │ │ - } │ │ │ │ - if (this.drillDown === true || this.urlMatches(url)) { │ │ │ │ - layers.push(layer); │ │ │ │ - } │ │ │ │ + var current = 0; │ │ │ │ + var shift = 0; │ │ │ │ + │ │ │ │ + var encodedLength = encoded.length; │ │ │ │ + for (var i = 0; i < encodedLength; ++i) { │ │ │ │ + var b = encoded.charCodeAt(i) - 63; │ │ │ │ + │ │ │ │ + current |= (b & 0x1f) << shift; │ │ │ │ + │ │ │ │ + if (b < 0x20) { │ │ │ │ + numbers.push(current); │ │ │ │ + current = 0; │ │ │ │ + shift = 0; │ │ │ │ + } else { │ │ │ │ + shift += 5; │ │ │ │ } │ │ │ │ } │ │ │ │ - return layers; │ │ │ │ + │ │ │ │ + return numbers; │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: urlMatches │ │ │ │ - * Test to see if the provided url matches either the control <url> or one │ │ │ │ - * of the <layerUrls>. │ │ │ │ + * Method: encodeFloat │ │ │ │ + * Encode one single floating point number and return an encoded string │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * url - {String} The url to test. │ │ │ │ + * num - {number} Floating point number that should be encoded. │ │ │ │ + * opt_factor - {number=} The factor by which num will be multiplied. │ │ │ │ + * The remaining decimal places will get rounded away. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The provided url matches the control <url> or one of the │ │ │ │ - * <layerUrls>. │ │ │ │ + * {string} The encoded string. │ │ │ │ */ │ │ │ │ - urlMatches: function(url) { │ │ │ │ - var matches = OpenLayers.Util.isEquivalentUrl(this.url, url); │ │ │ │ - if (!matches && this.layerUrls) { │ │ │ │ - for (var i = 0, len = this.layerUrls.length; i < len; ++i) { │ │ │ │ - if (OpenLayers.Util.isEquivalentUrl(this.layerUrls[i], url)) { │ │ │ │ - matches = true; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return matches; │ │ │ │ + encodeFloat: function(num, opt_factor) { │ │ │ │ + num = Math.round(num * (opt_factor || 1e5)); │ │ │ │ + return this.encodeSignedInteger(num); │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: buildWMSOptions │ │ │ │ - * Build an object with the relevant WMS options for the GetFeatureInfo request │ │ │ │ + * Method: decodeFloat │ │ │ │ + * Decode one single floating point number from an encoded string │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * url - {String} The url to be used for sending the request │ │ │ │ - * layers - {Array(<OpenLayers.Layer.WMS)} An array of layers │ │ │ │ - * clickPosition - {<OpenLayers.Pixel>} The position on the map where the mouse │ │ │ │ - * event occurred. │ │ │ │ - * format - {String} The format from the corresponding GetMap request │ │ │ │ + * encoded - {string} An encoded string. │ │ │ │ + * opt_factor - {number=} The factor by which the result will be divided. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {number} The decoded floating point number. │ │ │ │ */ │ │ │ │ - buildWMSOptions: function(url, layers, clickPosition, format) { │ │ │ │ - var layerNames = [], │ │ │ │ - styleNames = []; │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - if (layers[i].params.LAYERS != null) { │ │ │ │ - layerNames = layerNames.concat(layers[i].params.LAYERS); │ │ │ │ - styleNames = styleNames.concat(this.getStyleNames(layers[i])); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var firstLayer = layers[0]; │ │ │ │ - // use the firstLayer's projection if it matches the map projection - │ │ │ │ - // this assumes that all layers will be available in this projection │ │ │ │ - var projection = this.map.getProjection(); │ │ │ │ - var layerProj = firstLayer.projection; │ │ │ │ - if (layerProj && layerProj.equals(this.map.getProjectionObject())) { │ │ │ │ - projection = layerProj.getCode(); │ │ │ │ - } │ │ │ │ - var params = OpenLayers.Util.extend({ │ │ │ │ - service: "WMS", │ │ │ │ - version: firstLayer.params.VERSION, │ │ │ │ - request: "GetFeatureInfo", │ │ │ │ - exceptions: firstLayer.params.EXCEPTIONS, │ │ │ │ - bbox: this.map.getExtent().toBBOX(null, │ │ │ │ - firstLayer.reverseAxisOrder()), │ │ │ │ - feature_count: this.maxFeatures, │ │ │ │ - height: this.map.getSize().h, │ │ │ │ - width: this.map.getSize().w, │ │ │ │ - format: format, │ │ │ │ - info_format: firstLayer.params.INFO_FORMAT || this.infoFormat │ │ │ │ - }, (parseFloat(firstLayer.params.VERSION) >= 1.3) ? { │ │ │ │ - crs: projection, │ │ │ │ - i: parseInt(clickPosition.x), │ │ │ │ - j: parseInt(clickPosition.y) │ │ │ │ - } : { │ │ │ │ - srs: projection, │ │ │ │ - x: parseInt(clickPosition.x), │ │ │ │ - y: parseInt(clickPosition.y) │ │ │ │ - }); │ │ │ │ - if (layerNames.length != 0) { │ │ │ │ - params = OpenLayers.Util.extend({ │ │ │ │ - layers: layerNames, │ │ │ │ - query_layers: layerNames, │ │ │ │ - styles: styleNames │ │ │ │ - }, params); │ │ │ │ - } │ │ │ │ - OpenLayers.Util.applyDefaults(params, this.vendorParams); │ │ │ │ - return { │ │ │ │ - url: url, │ │ │ │ - params: OpenLayers.Util.upperCaseObject(params), │ │ │ │ - callback: function(request) { │ │ │ │ - this.handleResponse(clickPosition, request, url); │ │ │ │ - }, │ │ │ │ - scope: this │ │ │ │ - }; │ │ │ │ + decodeFloat: function(encoded, opt_factor) { │ │ │ │ + var result = this.decodeSignedInteger(encoded); │ │ │ │ + return result / (opt_factor || 1e5); │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: getStyleNames │ │ │ │ - * Gets the STYLES parameter for the layer. Make sure the STYLES parameter │ │ │ │ - * matches the LAYERS parameter │ │ │ │ + * Method: encodeSignedInteger │ │ │ │ + * Encode one single signed integer and return an encoded string │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.WMS>} │ │ │ │ + * num - {number} Signed integer that should be encoded. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array(String)} The STYLES parameter │ │ │ │ + * {string} The encoded string. │ │ │ │ */ │ │ │ │ - getStyleNames: function(layer) { │ │ │ │ - // in the event of a WMS layer bundling multiple layers but not │ │ │ │ - // specifying styles,we need the same number of commas to specify │ │ │ │ - // the default style for each of the layers. We can't just leave it │ │ │ │ - // blank as we may be including other layers that do specify styles. │ │ │ │ - var styleNames; │ │ │ │ - if (layer.params.STYLES) { │ │ │ │ - styleNames = layer.params.STYLES; │ │ │ │ - } else { │ │ │ │ - if (OpenLayers.Util.isArray(layer.params.LAYERS)) { │ │ │ │ - styleNames = new Array(layer.params.LAYERS.length); │ │ │ │ - } else { // Assume it's a String │ │ │ │ - styleNames = layer.params.LAYERS.replace(/[^,]/g, ""); │ │ │ │ - } │ │ │ │ + encodeSignedInteger: function(num) { │ │ │ │ + var signedNum = num << 1; │ │ │ │ + if (num < 0) { │ │ │ │ + signedNum = ~(signedNum); │ │ │ │ } │ │ │ │ - return styleNames; │ │ │ │ + │ │ │ │ + return this.encodeUnsignedInteger(signedNum); │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: request │ │ │ │ - * Sends a GetFeatureInfo request to the WMS │ │ │ │ + * Method: decodeSignedInteger │ │ │ │ + * Decode one single signed integer from an encoded string │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * clickPosition - {<OpenLayers.Pixel>} The position on the map where the │ │ │ │ - * mouse event occurred. │ │ │ │ - * options - {Object} additional options for this method. │ │ │ │ + * encoded - {string} An encoded string. │ │ │ │ * │ │ │ │ - * Valid options: │ │ │ │ - * - *hover* {Boolean} true if we do the request for the hover handler │ │ │ │ + * Returns: │ │ │ │ + * {number} The decoded signed integer. │ │ │ │ */ │ │ │ │ - request: function(clickPosition, options) { │ │ │ │ - var layers = this.findLayers(); │ │ │ │ - if (layers.length == 0) { │ │ │ │ - this.events.triggerEvent("nogetfeatureinfo"); │ │ │ │ - // Reset the cursor. │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - │ │ │ │ - options = options || {}; │ │ │ │ - if (this.drillDown === false) { │ │ │ │ - var wmsOptions = this.buildWMSOptions(this.url, layers, │ │ │ │ - clickPosition, layers[0].params.FORMAT); │ │ │ │ - var request = OpenLayers.Request.GET(wmsOptions); │ │ │ │ - │ │ │ │ - if (options.hover === true) { │ │ │ │ - this.hoverRequest = request; │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this._requestCount = 0; │ │ │ │ - this._numRequests = 0; │ │ │ │ - this.features = []; │ │ │ │ - // group according to service url to combine requests │ │ │ │ - var services = {}, │ │ │ │ - url; │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - var layer = layers[i]; │ │ │ │ - var service, found = false; │ │ │ │ - url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url; │ │ │ │ - if (url in services) { │ │ │ │ - services[url].push(layer); │ │ │ │ - } else { │ │ │ │ - this._numRequests++; │ │ │ │ - services[url] = [layer]; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var layers; │ │ │ │ - for (var url in services) { │ │ │ │ - layers = services[url]; │ │ │ │ - var wmsOptions = this.buildWMSOptions(url, layers, │ │ │ │ - clickPosition, layers[0].params.FORMAT); │ │ │ │ - OpenLayers.Request.GET(wmsOptions); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + decodeSignedInteger: function(encoded) { │ │ │ │ + var result = this.decodeUnsignedInteger(encoded); │ │ │ │ + return ((result & 1) ? ~(result >> 1) : (result >> 1)); │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: triggerGetFeatureInfo │ │ │ │ - * Trigger the getfeatureinfo event when all is done │ │ │ │ + * Method: encodeUnsignedInteger │ │ │ │ + * Encode one single unsigned integer and return an encoded string │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * request - {XMLHttpRequest} The request object │ │ │ │ - * xy - {<OpenLayers.Pixel>} The position on the map where the │ │ │ │ - * mouse event occurred. │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} or │ │ │ │ - * {Array({Object}) when output is "object". The object has a url and a │ │ │ │ - * features property which contains an array of features. │ │ │ │ + * num - {number} Unsigned integer that should be encoded. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {string} The encoded string. │ │ │ │ */ │ │ │ │ - triggerGetFeatureInfo: function(request, xy, features) { │ │ │ │ - this.events.triggerEvent("getfeatureinfo", { │ │ │ │ - text: request.responseText, │ │ │ │ - features: features, │ │ │ │ - request: request, │ │ │ │ - xy: xy │ │ │ │ - }); │ │ │ │ - │ │ │ │ - // Reset the cursor. │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ + encodeUnsignedInteger: function(num) { │ │ │ │ + var value, encoded = ''; │ │ │ │ + while (num >= 0x20) { │ │ │ │ + value = (0x20 | (num & 0x1f)) + 63; │ │ │ │ + encoded += (String.fromCharCode(value)); │ │ │ │ + num >>= 5; │ │ │ │ + } │ │ │ │ + value = num + 63; │ │ │ │ + encoded += (String.fromCharCode(value)); │ │ │ │ + return encoded; │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: handleResponse │ │ │ │ - * Handler for the GetFeatureInfo response. │ │ │ │ + * Method: decodeUnsignedInteger │ │ │ │ + * Decode one single unsigned integer from an encoded string │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * xy - {<OpenLayers.Pixel>} The position on the map where the │ │ │ │ - * mouse event occurred. │ │ │ │ - * request - {XMLHttpRequest} The request object. │ │ │ │ - * url - {String} The url which was used for this request. │ │ │ │ + * encoded - {string} An encoded string. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {number} The decoded unsigned integer. │ │ │ │ */ │ │ │ │ - handleResponse: function(xy, request, url) { │ │ │ │ + decodeUnsignedInteger: function(encoded) { │ │ │ │ + var result = 0; │ │ │ │ + var shift = 0; │ │ │ │ │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText; │ │ │ │ - } │ │ │ │ - var features = this.format.read(doc); │ │ │ │ - if (this.drillDown === false) { │ │ │ │ - this.triggerGetFeatureInfo(request, xy, features); │ │ │ │ - } else { │ │ │ │ - this._requestCount++; │ │ │ │ - if (this.output === "object") { │ │ │ │ - this._features = (this._features || []).concat({ │ │ │ │ - url: url, │ │ │ │ - features: features │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - this._features = (this._features || []).concat(features); │ │ │ │ - } │ │ │ │ - if (this._requestCount === this._numRequests) { │ │ │ │ - this.triggerGetFeatureInfo(request, xy, this._features.concat()); │ │ │ │ - delete this._features; │ │ │ │ - delete this._requestCount; │ │ │ │ - delete this._numRequests; │ │ │ │ - } │ │ │ │ + var encodedLength = encoded.length; │ │ │ │ + for (var i = 0; i < encodedLength; ++i) { │ │ │ │ + var b = encoded.charCodeAt(i) - 63; │ │ │ │ + │ │ │ │ + result |= (b & 0x1f) << shift; │ │ │ │ + │ │ │ │ + if (b < 0x20) │ │ │ │ + break; │ │ │ │ + │ │ │ │ + shift += 5; │ │ │ │ } │ │ │ │ + │ │ │ │ + return result; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.WMSGetFeatureInfo" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.EncodedPolyline" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/OverviewMap.js │ │ │ │ + OpenLayers/Format/ArcXML.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/BaseTypes.js │ │ │ │ - * @requires OpenLayers/Events/buttonclick.js │ │ │ │ - * @requires OpenLayers/Map.js │ │ │ │ - * @requires OpenLayers/Handler/Click.js │ │ │ │ - * @requires OpenLayers/Handler/Drag.js │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ + * @requires OpenLayers/Geometry/Point.js │ │ │ │ + * @requires OpenLayers/Geometry/MultiPolygon.js │ │ │ │ + * @requires OpenLayers/Geometry/LinearRing.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.OverviewMap │ │ │ │ - * The OverMap control creates a small overview map, useful to display the │ │ │ │ - * extent of a zoomed map and your main map and provide additional │ │ │ │ - * navigation options to the User. By default the overview map is drawn in │ │ │ │ - * the lower right corner of the main map. Create a new overview map with the │ │ │ │ - * <OpenLayers.Control.OverviewMap> constructor. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Format.ArcXML │ │ │ │ + * Read/Write ArcXML. Create a new instance with the <OpenLayers.Format.ArcXML> │ │ │ │ + * constructor. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ +OpenLayers.Format.ArcXML = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: element │ │ │ │ - * {DOMElement} The DOM element that contains the overview map │ │ │ │ + * Property: fontStyleKeys │ │ │ │ + * {Array} List of keys used in font styling. │ │ │ │ */ │ │ │ │ - element: null, │ │ │ │ + fontStyleKeys: [ │ │ │ │ + 'antialiasing', 'blockout', 'font', 'fontcolor', 'fontsize', 'fontstyle', │ │ │ │ + 'glowing', 'interval', 'outline', 'printmode', 'shadow', 'transparency' │ │ │ │ + ], │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: ovmap │ │ │ │ - * {<OpenLayers.Map>} A reference to the overview map itself. │ │ │ │ + * Property: request │ │ │ │ + * A get_image request destined for an ArcIMS server. │ │ │ │ */ │ │ │ │ - ovmap: null, │ │ │ │ + request: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: size │ │ │ │ - * {<OpenLayers.Size>} The overvew map size in pixels. Note that this is │ │ │ │ - * the size of the map itself - the element that contains the map (default │ │ │ │ - * class name olControlOverviewMapElement) may have padding or other style │ │ │ │ - * attributes added via CSS. │ │ │ │ + * Property: response │ │ │ │ + * A parsed response from an ArcIMS server. │ │ │ │ */ │ │ │ │ - size: { │ │ │ │ - w: 180, │ │ │ │ - h: 90 │ │ │ │ - }, │ │ │ │ + response: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: layers │ │ │ │ - * {Array(<OpenLayers.Layer>)} Ordered list of layers in the overview map. │ │ │ │ - * If none are sent at construction, the base layer for the main map is used. │ │ │ │ + * Constructor: OpenLayers.Format.ArcXML │ │ │ │ + * Create a new parser/writer for ArcXML. Create an instance of this class │ │ │ │ + * to begin authoring a request to an ArcIMS service. This is used │ │ │ │ + * primarily by the ArcIMS layer, but could be used to do other wild │ │ │ │ + * stuff, like geocoding. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - layers: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + this.request = new OpenLayers.Format.ArcXML.Request(); │ │ │ │ + this.response = new OpenLayers.Format.ArcXML.Response(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: minRectSize │ │ │ │ - * {Integer} The minimum width or height (in pixels) of the extent │ │ │ │ - * rectangle on the overview map. When the extent rectangle reaches │ │ │ │ - * this size, it will be replaced depending on the value of the │ │ │ │ - * <minRectDisplayClass> property. Default is 15 pixels. │ │ │ │ - */ │ │ │ │ - minRectSize: 15, │ │ │ │ + if (options) { │ │ │ │ + if (options.requesttype == "feature") { │ │ │ │ + this.request.get_image = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: minRectDisplayClass │ │ │ │ - * {String} Replacement style class name for the extent rectangle when │ │ │ │ - * <minRectSize> is reached. This string will be suffixed on to the │ │ │ │ - * displayClass. Default is "RectReplacement". │ │ │ │ - * │ │ │ │ - * Example CSS declaration: │ │ │ │ - * (code) │ │ │ │ - * .olControlOverviewMapRectReplacement { │ │ │ │ - * overflow: hidden; │ │ │ │ - * cursor: move; │ │ │ │ - * background-image: url("img/overview_replacement.gif"); │ │ │ │ - * background-repeat: no-repeat; │ │ │ │ - * background-position: center; │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ - minRectDisplayClass: "RectReplacement", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: minRatio │ │ │ │ - * {Float} The ratio of the overview map resolution to the main map │ │ │ │ - * resolution at which to zoom farther out on the overview map. │ │ │ │ - */ │ │ │ │ - minRatio: 8, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: maxRatio │ │ │ │ - * {Float} The ratio of the overview map resolution to the main map │ │ │ │ - * resolution at which to zoom farther in on the overview map. │ │ │ │ - */ │ │ │ │ - maxRatio: 32, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: mapOptions │ │ │ │ - * {Object} An object containing any non-default properties to be sent to │ │ │ │ - * the overview map's map constructor. These should include any │ │ │ │ - * non-default options that the main map was constructed with. │ │ │ │ - */ │ │ │ │ - mapOptions: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: autoPan │ │ │ │ - * {Boolean} Always pan the overview map, so the extent marker remains in │ │ │ │ - * the center. Default is false. If true, when you drag the extent │ │ │ │ - * marker, the overview map will update itself so the marker returns │ │ │ │ - * to the center. │ │ │ │ - */ │ │ │ │ - autoPan: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: handlers │ │ │ │ - * {Object} │ │ │ │ - */ │ │ │ │ - handlers: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: resolutionFactor │ │ │ │ - * {Object} │ │ │ │ - */ │ │ │ │ - resolutionFactor: 1, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: maximized │ │ │ │ - * {Boolean} Start as maximized (visible). Defaults to false. │ │ │ │ - */ │ │ │ │ - maximized: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: maximizeTitle │ │ │ │ - * {String} This property is used for showing a tooltip over the │ │ │ │ - * maximize div. Defaults to "" (no title). │ │ │ │ - */ │ │ │ │ - maximizeTitle: "", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: minimizeTitle │ │ │ │ - * {String} This property is used for showing a tooltip over the │ │ │ │ - * minimize div. Defaults to "" (no title). │ │ │ │ - */ │ │ │ │ - minimizeTitle: "", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.OverviewMap │ │ │ │ - * Create a new overview map │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Properties of this object will be set on the overview │ │ │ │ - * map object. Note, to set options on the map object contained in this │ │ │ │ - * control, set <mapOptions> as one of the options properties. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - this.layers = []; │ │ │ │ - this.handlers = {}; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Deconstruct the control │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - if (!this.mapDiv) { // we've already been destroyed │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (this.handlers.click) { │ │ │ │ - this.handlers.click.destroy(); │ │ │ │ - } │ │ │ │ - if (this.handlers.drag) { │ │ │ │ - this.handlers.drag.destroy(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.ovmap && this.ovmap.viewPortDiv.removeChild(this.extentRectangle); │ │ │ │ - this.extentRectangle = null; │ │ │ │ - │ │ │ │ - if (this.rectEvents) { │ │ │ │ - this.rectEvents.destroy(); │ │ │ │ - this.rectEvents = null; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.ovmap) { │ │ │ │ - this.ovmap.destroy(); │ │ │ │ - this.ovmap = null; │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.element.removeChild(this.mapDiv); │ │ │ │ - this.mapDiv = null; │ │ │ │ - │ │ │ │ - this.div.removeChild(this.element); │ │ │ │ - this.element = null; │ │ │ │ - │ │ │ │ - if (this.maximizeDiv) { │ │ │ │ - this.div.removeChild(this.maximizeDiv); │ │ │ │ - this.maximizeDiv = null; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.minimizeDiv) { │ │ │ │ - this.div.removeChild(this.minimizeDiv); │ │ │ │ - this.minimizeDiv = null; │ │ │ │ - } │ │ │ │ + var qry = this.request.get_feature.query; │ │ │ │ + this.addCoordSys(qry.featurecoordsys, options.featureCoordSys); │ │ │ │ + this.addCoordSys(qry.filtercoordsys, options.filterCoordSys); │ │ │ │ │ │ │ │ - this.map.events.un({ │ │ │ │ - buttonclick: this.onButtonClick, │ │ │ │ - moveend: this.update, │ │ │ │ - changebaselayer: this.baseLayerDraw, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + if (options.polygon) { │ │ │ │ + qry.isspatial = true; │ │ │ │ + qry.spatialfilter.polygon = options.polygon; │ │ │ │ + } else if (options.envelope) { │ │ │ │ + qry.isspatial = true; │ │ │ │ + qry.spatialfilter.envelope = { │ │ │ │ + minx: 0, │ │ │ │ + miny: 0, │ │ │ │ + maxx: 0, │ │ │ │ + maxy: 0 │ │ │ │ + }; │ │ │ │ + this.parseEnvelope(qry.spatialfilter.envelope, options.envelope); │ │ │ │ + } │ │ │ │ + } else if (options.requesttype == "image") { │ │ │ │ + this.request.get_feature = null; │ │ │ │ │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + var props = this.request.get_image.properties; │ │ │ │ + this.parseEnvelope(props.envelope, options.envelope); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ - * Render the control in the browser. │ │ │ │ - */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (this.layers.length === 0) { │ │ │ │ - if (this.map.baseLayer) { │ │ │ │ - var layer = this.map.baseLayer.clone(); │ │ │ │ - this.layers = [layer]; │ │ │ │ + this.addLayers(props.layerlist, options.layers); │ │ │ │ + this.addImageSize(props.imagesize, options.tileSize); │ │ │ │ + this.addCoordSys(props.featurecoordsys, options.featureCoordSys); │ │ │ │ + this.addCoordSys(props.filtercoordsys, options.filterCoordSys); │ │ │ │ } else { │ │ │ │ - this.map.events.register("changebaselayer", this, this.baseLayerDraw); │ │ │ │ - return this.div; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - // create overview map DOM elements │ │ │ │ - this.element = document.createElement('div'); │ │ │ │ - this.element.className = this.displayClass + 'Element'; │ │ │ │ - this.element.style.display = 'none'; │ │ │ │ - │ │ │ │ - this.mapDiv = document.createElement('div'); │ │ │ │ - this.mapDiv.style.width = this.size.w + 'px'; │ │ │ │ - this.mapDiv.style.height = this.size.h + 'px'; │ │ │ │ - this.mapDiv.style.position = 'relative'; │ │ │ │ - this.mapDiv.style.overflow = 'hidden'; │ │ │ │ - this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap'); │ │ │ │ - │ │ │ │ - this.extentRectangle = document.createElement('div'); │ │ │ │ - this.extentRectangle.style.position = 'absolute'; │ │ │ │ - this.extentRectangle.style.zIndex = 1000; //HACK │ │ │ │ - this.extentRectangle.className = this.displayClass + 'ExtentRectangle'; │ │ │ │ - │ │ │ │ - this.element.appendChild(this.mapDiv); │ │ │ │ - │ │ │ │ - this.div.appendChild(this.element); │ │ │ │ - │ │ │ │ - // Optionally add min/max buttons if the control will go in the │ │ │ │ - // map viewport. │ │ │ │ - if (!this.outsideViewport) { │ │ │ │ - this.div.className += " " + this.displayClass + 'Container'; │ │ │ │ - // maximize button div │ │ │ │ - var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png'); │ │ │ │ - this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ - this.displayClass + 'MaximizeButton', │ │ │ │ - null, │ │ │ │ - null, │ │ │ │ - img, │ │ │ │ - 'absolute'); │ │ │ │ - this.maximizeDiv.style.display = 'none'; │ │ │ │ - this.maximizeDiv.className = this.displayClass + 'MaximizeButton olButton'; │ │ │ │ - if (this.maximizeTitle) { │ │ │ │ - this.maximizeDiv.title = this.maximizeTitle; │ │ │ │ - } │ │ │ │ - this.div.appendChild(this.maximizeDiv); │ │ │ │ - │ │ │ │ - // minimize button div │ │ │ │ - var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png'); │ │ │ │ - this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ - 'OpenLayers_Control_minimizeDiv', │ │ │ │ - null, │ │ │ │ - null, │ │ │ │ - img, │ │ │ │ - 'absolute'); │ │ │ │ - this.minimizeDiv.style.display = 'none'; │ │ │ │ - this.minimizeDiv.className = this.displayClass + 'MinimizeButton olButton'; │ │ │ │ - if (this.minimizeTitle) { │ │ │ │ - this.minimizeDiv.title = this.minimizeTitle; │ │ │ │ + // if an arcxml object is being created with no request type, it is │ │ │ │ + // probably going to consume a response, so do not throw an error if │ │ │ │ + // the requesttype is not defined │ │ │ │ + this.request = null; │ │ │ │ } │ │ │ │ - this.div.appendChild(this.minimizeDiv); │ │ │ │ - this.minimizeControl(); │ │ │ │ - } else { │ │ │ │ - // show the overview map │ │ │ │ - this.element.style.display = ''; │ │ │ │ - } │ │ │ │ - if (this.map.getExtent()) { │ │ │ │ - this.update(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.map.events.on({ │ │ │ │ - buttonclick: this.onButtonClick, │ │ │ │ - moveend: this.update, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - │ │ │ │ - if (this.maximized) { │ │ │ │ - this.maximizeControl(); │ │ │ │ } │ │ │ │ - return this.div; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: baseLayerDraw │ │ │ │ - * Draw the base layer - called if unable to complete in the initial draw │ │ │ │ - */ │ │ │ │ - baseLayerDraw: function() { │ │ │ │ - this.draw(); │ │ │ │ - this.map.events.unregister("changebaselayer", this, this.baseLayerDraw); │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: rectDrag │ │ │ │ - * Handle extent rectangle drag │ │ │ │ + * Method: parseEnvelope │ │ │ │ + * Parse an array of coordinates into an ArcXML envelope structure. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} The pixel location of the drag. │ │ │ │ + * env - {Object} An envelope object that will contain the parsed coordinates. │ │ │ │ + * arr - {Array(double)} An array of coordinates in the order: [ minx, miny, maxx, maxy ] │ │ │ │ */ │ │ │ │ - rectDrag: function(px) { │ │ │ │ - var deltaX = this.handlers.drag.last.x - px.x; │ │ │ │ - var deltaY = this.handlers.drag.last.y - px.y; │ │ │ │ - if (deltaX != 0 || deltaY != 0) { │ │ │ │ - var rectTop = this.rectPxBounds.top; │ │ │ │ - var rectLeft = this.rectPxBounds.left; │ │ │ │ - var rectHeight = Math.abs(this.rectPxBounds.getHeight()); │ │ │ │ - var rectWidth = this.rectPxBounds.getWidth(); │ │ │ │ - // don't allow dragging off of parent element │ │ │ │ - var newTop = Math.max(0, (rectTop - deltaY)); │ │ │ │ - newTop = Math.min(newTop, │ │ │ │ - this.ovmap.size.h - this.hComp - rectHeight); │ │ │ │ - var newLeft = Math.max(0, (rectLeft - deltaX)); │ │ │ │ - newLeft = Math.min(newLeft, │ │ │ │ - this.ovmap.size.w - this.wComp - rectWidth); │ │ │ │ - this.setRectPxBounds(new OpenLayers.Bounds(newLeft, │ │ │ │ - newTop + rectHeight, │ │ │ │ - newLeft + rectWidth, │ │ │ │ - newTop)); │ │ │ │ + parseEnvelope: function(env, arr) { │ │ │ │ + if (arr && arr.length == 4) { │ │ │ │ + env.minx = arr[0]; │ │ │ │ + env.miny = arr[1]; │ │ │ │ + env.maxx = arr[2]; │ │ │ │ + env.maxy = arr[3]; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: mapDivClick │ │ │ │ - * Handle browser events │ │ │ │ + /** │ │ │ │ + * Method: addLayers │ │ │ │ + * Add a collection of layers to another collection of layers. Each layer in the list is tuple of │ │ │ │ + * { id, visible }. These layer collections represent the │ │ │ │ + * /ARCXML/REQUEST/get_image/PROPERTIES/LAYERLIST/LAYERDEF items in ArcXML │ │ │ │ + * │ │ │ │ + * TODO: Add support for dynamic layer rendering. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} evt │ │ │ │ + * ll - {Array({id,visible})} A list of layer definitions. │ │ │ │ + * lyrs - {Array({id,visible})} A list of layer definitions. │ │ │ │ */ │ │ │ │ - mapDivClick: function(evt) { │ │ │ │ - var pxCenter = this.rectPxBounds.getCenterPixel(); │ │ │ │ - var deltaX = evt.xy.x - pxCenter.x; │ │ │ │ - var deltaY = evt.xy.y - pxCenter.y; │ │ │ │ - var top = this.rectPxBounds.top; │ │ │ │ - var left = this.rectPxBounds.left; │ │ │ │ - var height = Math.abs(this.rectPxBounds.getHeight()); │ │ │ │ - var width = this.rectPxBounds.getWidth(); │ │ │ │ - var newTop = Math.max(0, (top + deltaY)); │ │ │ │ - newTop = Math.min(newTop, this.ovmap.size.h - height); │ │ │ │ - var newLeft = Math.max(0, (left + deltaX)); │ │ │ │ - newLeft = Math.min(newLeft, this.ovmap.size.w - width); │ │ │ │ - this.setRectPxBounds(new OpenLayers.Bounds(newLeft, │ │ │ │ - newTop + height, │ │ │ │ - newLeft + width, │ │ │ │ - newTop)); │ │ │ │ - this.updateMapToRect(); │ │ │ │ + addLayers: function(ll, lyrs) { │ │ │ │ + for (var lind = 0, len = lyrs.length; lind < len; lind++) { │ │ │ │ + ll.push(lyrs[lind]); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onButtonClick │ │ │ │ + * Method: addImageSize │ │ │ │ + * Set the size of the requested image. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * imsize - {Object} An ArcXML imagesize object. │ │ │ │ + * olsize - {<OpenLayers.Size>} The image size to set. │ │ │ │ */ │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - if (evt.buttonElement === this.minimizeDiv) { │ │ │ │ - this.minimizeControl(); │ │ │ │ - } else if (evt.buttonElement === this.maximizeDiv) { │ │ │ │ - this.maximizeControl(); │ │ │ │ + addImageSize: function(imsize, olsize) { │ │ │ │ + if (olsize !== null) { │ │ │ │ + imsize.width = olsize.w; │ │ │ │ + imsize.height = olsize.h; │ │ │ │ + imsize.printwidth = olsize.w; │ │ │ │ + imsize.printheight = olsize.h; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: maximizeControl │ │ │ │ - * Unhide the control. Called when the control is in the map viewport. │ │ │ │ + * Method: addCoordSys │ │ │ │ + * Add the coordinate system information to an object. The object may be │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * e - {<OpenLayers.Event>} │ │ │ │ + * featOrFilt - {Object} A featurecoordsys or filtercoordsys ArcXML structure. │ │ │ │ + * fsys - {String} or {<OpenLayers.Projection>} or {filtercoordsys} or │ │ │ │ + * {featurecoordsys} A projection representation. If it's a {String}, │ │ │ │ + * the value is assumed to be the SRID. If it's a {OpenLayers.Projection} │ │ │ │ + * AND Proj4js is available, the projection number and name are extracted │ │ │ │ + * from there. If it's a filter or feature ArcXML structure, it is copied. │ │ │ │ */ │ │ │ │ - maximizeControl: function(e) { │ │ │ │ - this.element.style.display = ''; │ │ │ │ - this.showToggle(false); │ │ │ │ - if (e != null) { │ │ │ │ - OpenLayers.Event.stop(e); │ │ │ │ + addCoordSys: function(featOrFilt, fsys) { │ │ │ │ + if (typeof fsys == "string") { │ │ │ │ + featOrFilt.id = parseInt(fsys); │ │ │ │ + featOrFilt.string = fsys; │ │ │ │ + } │ │ │ │ + // is this a proj4js instance? │ │ │ │ + else if (typeof fsys == "object" && fsys.proj !== null) { │ │ │ │ + featOrFilt.id = fsys.proj.srsProjNumber; │ │ │ │ + featOrFilt.string = fsys.proj.srsCode; │ │ │ │ + } else { │ │ │ │ + featOrFilt = fsys; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: minimizeControl │ │ │ │ - * Hide all the contents of the control, shrink the size, │ │ │ │ - * add the maximize icon │ │ │ │ - * │ │ │ │ + * APIMethod: iserror │ │ │ │ + * Check to see if the response from the server was an error. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * e - {<OpenLayers.Event>} │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. If nothing is supplied, │ │ │ │ + * the current response is examined. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true if the response was an error. │ │ │ │ */ │ │ │ │ - minimizeControl: function(e) { │ │ │ │ - this.element.style.display = 'none'; │ │ │ │ - this.showToggle(true); │ │ │ │ - if (e != null) { │ │ │ │ - OpenLayers.Event.stop(e); │ │ │ │ + iserror: function(data) { │ │ │ │ + var ret = null; │ │ │ │ + │ │ │ │ + if (!data) { │ │ │ │ + ret = (this.response.error !== ''); │ │ │ │ + } else { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + var errorNodes = data.documentElement.getElementsByTagName("ERROR"); │ │ │ │ + ret = (errorNodes !== null && errorNodes.length > 0); │ │ │ │ } │ │ │ │ + │ │ │ │ + return ret; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: showToggle │ │ │ │ - * Hide/Show the toggle depending on whether the control is minimized │ │ │ │ - * │ │ │ │ + * APIMethod: read │ │ │ │ + * Read data from a string, and return an response. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * minimize - {Boolean} │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Format.ArcXML.Response>} An ArcXML response. Note that this response │ │ │ │ + * data may change in the future. │ │ │ │ */ │ │ │ │ - showToggle: function(minimize) { │ │ │ │ - if (this.maximizeDiv) { │ │ │ │ - this.maximizeDiv.style.display = minimize ? '' : 'none'; │ │ │ │ - } │ │ │ │ - if (this.minimizeDiv) { │ │ │ │ - this.minimizeDiv.style.display = minimize ? 'none' : ''; │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: update │ │ │ │ - * Update the overview map after layers move. │ │ │ │ - */ │ │ │ │ - update: function() { │ │ │ │ - if (this.ovmap == null) { │ │ │ │ - this.createMap(); │ │ │ │ + var arcNode = null; │ │ │ │ + if (data && data.documentElement) { │ │ │ │ + if (data.documentElement.nodeName == "ARCXML") { │ │ │ │ + arcNode = data.documentElement; │ │ │ │ + } else { │ │ │ │ + arcNode = data.documentElement.getElementsByTagName("ARCXML")[0]; │ │ │ │ + } │ │ │ │ } │ │ │ │ │ │ │ │ - if (this.autoPan || !this.isSuitableOverview()) { │ │ │ │ - this.updateOverview(); │ │ │ │ + // in Safari, arcNode will be there but will have a child named │ │ │ │ + // parsererror │ │ │ │ + if (!arcNode || arcNode.firstChild.nodeName === 'parsererror') { │ │ │ │ + var error, source; │ │ │ │ + try { │ │ │ │ + error = data.firstChild.nodeValue; │ │ │ │ + source = data.firstChild.childNodes[1].firstChild.nodeValue; │ │ │ │ + } catch (err) { │ │ │ │ + // pass │ │ │ │ + } │ │ │ │ + throw { │ │ │ │ + message: "Error parsing the ArcXML request", │ │ │ │ + error: error, │ │ │ │ + source: source │ │ │ │ + }; │ │ │ │ } │ │ │ │ │ │ │ │ - // update extent rectangle │ │ │ │ - this.updateRectToMap(); │ │ │ │ + var response = this.parseResponse(arcNode); │ │ │ │ + return response; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: isSuitableOverview │ │ │ │ - * Determines if the overview map is suitable given the extent and │ │ │ │ - * resolution of the main map. │ │ │ │ + * APIMethod: write │ │ │ │ + * Generate an ArcXml document string for sending to an ArcIMS server. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A string representing the ArcXML document request. │ │ │ │ */ │ │ │ │ - isSuitableOverview: function() { │ │ │ │ - var mapExtent = this.map.getExtent(); │ │ │ │ - var maxExtent = this.map.getMaxExtent(); │ │ │ │ - var testExtent = new OpenLayers.Bounds( │ │ │ │ - Math.max(mapExtent.left, maxExtent.left), │ │ │ │ - Math.max(mapExtent.bottom, maxExtent.bottom), │ │ │ │ - Math.min(mapExtent.right, maxExtent.right), │ │ │ │ - Math.min(mapExtent.top, maxExtent.top)); │ │ │ │ - │ │ │ │ - if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ - testExtent = testExtent.transform( │ │ │ │ - this.map.getProjectionObject(), │ │ │ │ - this.ovmap.getProjectionObject()); │ │ │ │ + write: function(request) { │ │ │ │ + if (!request) { │ │ │ │ + request = this.request; │ │ │ │ } │ │ │ │ + var root = this.createElementNS("", "ARCXML"); │ │ │ │ + root.setAttribute("version", "1.1"); │ │ │ │ │ │ │ │ - var resRatio = this.ovmap.getResolution() / this.map.getResolution(); │ │ │ │ - return ((resRatio > this.minRatio) && │ │ │ │ - (resRatio <= this.maxRatio) && │ │ │ │ - (this.ovmap.getExtent().containsBounds(testExtent))); │ │ │ │ - }, │ │ │ │ + var reqElem = this.createElementNS("", "REQUEST"); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method updateOverview │ │ │ │ - * Called by <update> if <isSuitableOverview> returns true │ │ │ │ - */ │ │ │ │ - updateOverview: function() { │ │ │ │ - var mapRes = this.map.getResolution(); │ │ │ │ - var targetRes = this.ovmap.getResolution(); │ │ │ │ - var resRatio = targetRes / mapRes; │ │ │ │ - if (resRatio > this.maxRatio) { │ │ │ │ - // zoom in overview map │ │ │ │ - targetRes = this.minRatio * mapRes; │ │ │ │ - } else if (resRatio <= this.minRatio) { │ │ │ │ - // zoom out overview map │ │ │ │ - targetRes = this.maxRatio * mapRes; │ │ │ │ - } │ │ │ │ - var center; │ │ │ │ - if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ - center = this.map.center.clone(); │ │ │ │ - center.transform(this.map.getProjectionObject(), │ │ │ │ - this.ovmap.getProjectionObject()); │ │ │ │ - } else { │ │ │ │ - center = this.map.center; │ │ │ │ - } │ │ │ │ - this.ovmap.setCenter(center, this.ovmap.getZoomForResolution( │ │ │ │ - targetRes * this.resolutionFactor)); │ │ │ │ - this.updateRectToMap(); │ │ │ │ - }, │ │ │ │ + if (request.get_image != null) { │ │ │ │ + var getElem = this.createElementNS("", "GET_IMAGE"); │ │ │ │ + reqElem.appendChild(getElem); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: createMap │ │ │ │ - * Construct the map that this control contains │ │ │ │ - */ │ │ │ │ - createMap: function() { │ │ │ │ - // create the overview map │ │ │ │ - var options = OpenLayers.Util.extend({ │ │ │ │ - controls: [], │ │ │ │ - maxResolution: 'auto', │ │ │ │ - fallThrough: false │ │ │ │ - }, this.mapOptions); │ │ │ │ - this.ovmap = new OpenLayers.Map(this.mapDiv, options); │ │ │ │ - this.ovmap.viewPortDiv.appendChild(this.extentRectangle); │ │ │ │ + var propElem = this.createElementNS("", "PROPERTIES"); │ │ │ │ + getElem.appendChild(propElem); │ │ │ │ │ │ │ │ - // prevent ovmap from being destroyed when the page unloads, because │ │ │ │ - // the OverviewMap control has to do this (and does it). │ │ │ │ - OpenLayers.Event.stopObserving(window, 'unload', this.ovmap.unloadDestroy); │ │ │ │ + var props = request.get_image.properties; │ │ │ │ + if (props.featurecoordsys != null) { │ │ │ │ + var feat = this.createElementNS("", "FEATURECOORDSYS"); │ │ │ │ + propElem.appendChild(feat); │ │ │ │ │ │ │ │ - this.ovmap.addLayers(this.layers); │ │ │ │ - this.ovmap.zoomToMaxExtent(); │ │ │ │ - // check extent rectangle border width │ │ │ │ - this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle, │ │ │ │ - 'border-left-width')) + │ │ │ │ - parseInt(OpenLayers.Element.getStyle(this.extentRectangle, │ │ │ │ - 'border-right-width')); │ │ │ │ - this.wComp = (this.wComp) ? this.wComp : 2; │ │ │ │ - this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle, │ │ │ │ - 'border-top-width')) + │ │ │ │ - parseInt(OpenLayers.Element.getStyle(this.extentRectangle, │ │ │ │ - 'border-bottom-width')); │ │ │ │ - this.hComp = (this.hComp) ? this.hComp : 2; │ │ │ │ + if (props.featurecoordsys.id === 0) { │ │ │ │ + feat.setAttribute("string", props.featurecoordsys['string']); │ │ │ │ + } else { │ │ │ │ + feat.setAttribute("id", props.featurecoordsys.id); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - this.handlers.drag = new OpenLayers.Handler.Drag( │ │ │ │ - this, { │ │ │ │ - move: this.rectDrag, │ │ │ │ - done: this.updateMapToRect │ │ │ │ - }, { │ │ │ │ - map: this.ovmap │ │ │ │ + if (props.filtercoordsys != null) { │ │ │ │ + var filt = this.createElementNS("", "FILTERCOORDSYS"); │ │ │ │ + propElem.appendChild(filt); │ │ │ │ + │ │ │ │ + if (props.filtercoordsys.id === 0) { │ │ │ │ + filt.setAttribute("string", props.filtercoordsys.string); │ │ │ │ + } else { │ │ │ │ + filt.setAttribute("id", props.filtercoordsys.id); │ │ │ │ + } │ │ │ │ } │ │ │ │ - ); │ │ │ │ - this.handlers.click = new OpenLayers.Handler.Click( │ │ │ │ - this, { │ │ │ │ - "click": this.mapDivClick │ │ │ │ - }, { │ │ │ │ - "single": true, │ │ │ │ - "double": false, │ │ │ │ - "stopSingle": true, │ │ │ │ - "stopDouble": true, │ │ │ │ - "pixelTolerance": 1, │ │ │ │ - map: this.ovmap │ │ │ │ + │ │ │ │ + if (props.envelope != null) { │ │ │ │ + var env = this.createElementNS("", "ENVELOPE"); │ │ │ │ + propElem.appendChild(env); │ │ │ │ + │ │ │ │ + env.setAttribute("minx", props.envelope.minx); │ │ │ │ + env.setAttribute("miny", props.envelope.miny); │ │ │ │ + env.setAttribute("maxx", props.envelope.maxx); │ │ │ │ + env.setAttribute("maxy", props.envelope.maxy); │ │ │ │ } │ │ │ │ - ); │ │ │ │ - this.handlers.click.activate(); │ │ │ │ │ │ │ │ - this.rectEvents = new OpenLayers.Events(this, this.extentRectangle, │ │ │ │ - null, true); │ │ │ │ - this.rectEvents.register("mouseover", this, function(e) { │ │ │ │ - if (!this.handlers.drag.active && !this.map.dragging) { │ │ │ │ - this.handlers.drag.activate(); │ │ │ │ + var imagesz = this.createElementNS("", "IMAGESIZE"); │ │ │ │ + propElem.appendChild(imagesz); │ │ │ │ + │ │ │ │ + imagesz.setAttribute("height", props.imagesize.height); │ │ │ │ + imagesz.setAttribute("width", props.imagesize.width); │ │ │ │ + │ │ │ │ + if (props.imagesize.height != props.imagesize.printheight || │ │ │ │ + props.imagesize.width != props.imagesize.printwidth) { │ │ │ │ + imagesz.setAttribute("printheight", props.imagesize.printheight); │ │ │ │ + imagesz.setArrtibute("printwidth", props.imagesize.printwidth); │ │ │ │ } │ │ │ │ - }); │ │ │ │ - this.rectEvents.register("mouseout", this, function(e) { │ │ │ │ - if (!this.handlers.drag.dragging) { │ │ │ │ - this.handlers.drag.deactivate(); │ │ │ │ + │ │ │ │ + if (props.background != null) { │ │ │ │ + var backgrnd = this.createElementNS("", "BACKGROUND"); │ │ │ │ + propElem.appendChild(backgrnd); │ │ │ │ + │ │ │ │ + backgrnd.setAttribute("color", │ │ │ │ + props.background.color.r + "," + │ │ │ │ + props.background.color.g + "," + │ │ │ │ + props.background.color.b); │ │ │ │ + │ │ │ │ + if (props.background.transcolor !== null) { │ │ │ │ + backgrnd.setAttribute("transcolor", │ │ │ │ + props.background.transcolor.r + "," + │ │ │ │ + props.background.transcolor.g + "," + │ │ │ │ + props.background.transcolor.b); │ │ │ │ + } │ │ │ │ } │ │ │ │ - }); │ │ │ │ │ │ │ │ - if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ - var sourceUnits = this.map.getProjectionObject().getUnits() || │ │ │ │ - this.map.units || this.map.baseLayer.units; │ │ │ │ - var targetUnits = this.ovmap.getProjectionObject().getUnits() || │ │ │ │ - this.ovmap.units || this.ovmap.baseLayer.units; │ │ │ │ - this.resolutionFactor = sourceUnits && targetUnits ? │ │ │ │ - OpenLayers.INCHES_PER_UNIT[sourceUnits] / │ │ │ │ - OpenLayers.INCHES_PER_UNIT[targetUnits] : 1; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + if (props.layerlist != null && props.layerlist.length > 0) { │ │ │ │ + var layerlst = this.createElementNS("", "LAYERLIST"); │ │ │ │ + propElem.appendChild(layerlst); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: updateRectToMap │ │ │ │ - * Updates the extent rectangle position and size to match the map extent │ │ │ │ - */ │ │ │ │ - updateRectToMap: function() { │ │ │ │ - // If the projections differ we need to reproject │ │ │ │ - var bounds; │ │ │ │ - if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ - bounds = this.map.getExtent().transform( │ │ │ │ - this.map.getProjectionObject(), │ │ │ │ - this.ovmap.getProjectionObject()); │ │ │ │ - } else { │ │ │ │ - bounds = this.map.getExtent(); │ │ │ │ - } │ │ │ │ - var pxBounds = this.getRectBoundsFromMapBounds(bounds); │ │ │ │ - if (pxBounds) { │ │ │ │ - this.setRectPxBounds(pxBounds); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + for (var ld = 0; ld < props.layerlist.length; ld++) { │ │ │ │ + var ldef = this.createElementNS("", "LAYERDEF"); │ │ │ │ + layerlst.appendChild(ldef); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: updateMapToRect │ │ │ │ - * Updates the map extent to match the extent rectangle position and size │ │ │ │ - */ │ │ │ │ - updateMapToRect: function() { │ │ │ │ - var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds); │ │ │ │ - if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ - lonLatBounds = lonLatBounds.transform( │ │ │ │ - this.ovmap.getProjectionObject(), │ │ │ │ - this.map.getProjectionObject()); │ │ │ │ - } │ │ │ │ - this.map.panTo(lonLatBounds.getCenterLonLat()); │ │ │ │ - }, │ │ │ │ + ldef.setAttribute("id", props.layerlist[ld].id); │ │ │ │ + ldef.setAttribute("visible", props.layerlist[ld].visible); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setRectPxBounds │ │ │ │ - * Set extent rectangle pixel bounds. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * pxBounds - {<OpenLayers.Bounds>} │ │ │ │ - */ │ │ │ │ - setRectPxBounds: function(pxBounds) { │ │ │ │ - var top = Math.max(pxBounds.top, 0); │ │ │ │ - var left = Math.max(pxBounds.left, 0); │ │ │ │ - var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()), │ │ │ │ - this.ovmap.size.h - this.hComp); │ │ │ │ - var right = Math.min(pxBounds.left + pxBounds.getWidth(), │ │ │ │ - this.ovmap.size.w - this.wComp); │ │ │ │ - var width = Math.max(right - left, 0); │ │ │ │ - var height = Math.max(bottom - top, 0); │ │ │ │ - if (width < this.minRectSize || height < this.minRectSize) { │ │ │ │ - this.extentRectangle.className = this.displayClass + │ │ │ │ - this.minRectDisplayClass; │ │ │ │ - var rLeft = left + (width / 2) - (this.minRectSize / 2); │ │ │ │ - var rTop = top + (height / 2) - (this.minRectSize / 2); │ │ │ │ - this.extentRectangle.style.top = Math.round(rTop) + 'px'; │ │ │ │ - this.extentRectangle.style.left = Math.round(rLeft) + 'px'; │ │ │ │ - this.extentRectangle.style.height = this.minRectSize + 'px'; │ │ │ │ - this.extentRectangle.style.width = this.minRectSize + 'px'; │ │ │ │ - } else { │ │ │ │ - this.extentRectangle.className = this.displayClass + │ │ │ │ - 'ExtentRectangle'; │ │ │ │ - this.extentRectangle.style.top = Math.round(top) + 'px'; │ │ │ │ - this.extentRectangle.style.left = Math.round(left) + 'px'; │ │ │ │ - this.extentRectangle.style.height = Math.round(height) + 'px'; │ │ │ │ - this.extentRectangle.style.width = Math.round(width) + 'px'; │ │ │ │ - } │ │ │ │ - this.rectPxBounds = new OpenLayers.Bounds( │ │ │ │ - Math.round(left), Math.round(bottom), │ │ │ │ - Math.round(right), Math.round(top) │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ + if (typeof props.layerlist[ld].query == "object") { │ │ │ │ + var query = props.layerlist[ld].query; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getRectBoundsFromMapBounds │ │ │ │ - * Get the rect bounds from the map bounds. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * lonLatBounds - {<OpenLayers.Bounds>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>}A bounds which is the passed-in map lon/lat extent │ │ │ │ - * translated into pixel bounds for the overview map │ │ │ │ - */ │ │ │ │ - getRectBoundsFromMapBounds: function(lonLatBounds) { │ │ │ │ - var leftBottomPx = this.getOverviewPxFromLonLat({ │ │ │ │ - lon: lonLatBounds.left, │ │ │ │ - lat: lonLatBounds.bottom │ │ │ │ - }); │ │ │ │ - var rightTopPx = this.getOverviewPxFromLonLat({ │ │ │ │ - lon: lonLatBounds.right, │ │ │ │ - lat: lonLatBounds.top │ │ │ │ - }); │ │ │ │ - var bounds = null; │ │ │ │ - if (leftBottomPx && rightTopPx) { │ │ │ │ - bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y, │ │ │ │ - rightTopPx.x, rightTopPx.y); │ │ │ │ - } │ │ │ │ - return bounds; │ │ │ │ - }, │ │ │ │ + if (query.where.length < 0) { │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getMapBoundsFromRectBounds │ │ │ │ - * Get the map bounds from the rect bounds. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * pxBounds - {<OpenLayers.Bounds>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} Bounds which is the passed-in overview rect bounds │ │ │ │ - * translated into lon/lat bounds for the overview map │ │ │ │ - */ │ │ │ │ - getMapBoundsFromRectBounds: function(pxBounds) { │ │ │ │ - var leftBottomLonLat = this.getLonLatFromOverviewPx({ │ │ │ │ - x: pxBounds.left, │ │ │ │ - y: pxBounds.bottom │ │ │ │ - }); │ │ │ │ - var rightTopLonLat = this.getLonLatFromOverviewPx({ │ │ │ │ - x: pxBounds.right, │ │ │ │ - y: pxBounds.top │ │ │ │ - }); │ │ │ │ - return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat, │ │ │ │ - rightTopLonLat.lon, rightTopLonLat.lat); │ │ │ │ - }, │ │ │ │ + var queryElem = null; │ │ │ │ + if (typeof query.spatialfilter == "boolean" && query.spatialfilter) { │ │ │ │ + // handle spatial filter madness │ │ │ │ + queryElem = this.createElementNS("", "SPATIALQUERY"); │ │ │ │ + } else { │ │ │ │ + queryElem = this.createElementNS("", "QUERY"); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getLonLatFromOverviewPx │ │ │ │ - * Get a map location from a pixel location │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * overviewMapPx - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or │ │ │ │ - * an object with a │ │ │ │ - * 'x' and 'y' properties. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Location which is the passed-in overview map │ │ │ │ - * OpenLayers.Pixel, translated into lon/lat by the overview │ │ │ │ - * map. An object with a 'lon' and 'lat' properties. │ │ │ │ - */ │ │ │ │ - getLonLatFromOverviewPx: function(overviewMapPx) { │ │ │ │ - var size = this.ovmap.size; │ │ │ │ - var res = this.ovmap.getResolution(); │ │ │ │ - var center = this.ovmap.getExtent().getCenterLonLat(); │ │ │ │ + queryElem.setAttribute("where", query.where); │ │ │ │ │ │ │ │ - var deltaX = overviewMapPx.x - (size.w / 2); │ │ │ │ - var deltaY = overviewMapPx.y - (size.h / 2); │ │ │ │ + if (typeof query.accuracy == "number" && query.accuracy > 0) { │ │ │ │ + queryElem.setAttribute("accuracy", query.accuracy); │ │ │ │ + } │ │ │ │ + if (typeof query.featurelimit == "number" && query.featurelimit < 2000) { │ │ │ │ + queryElem.setAttribute("featurelimit", query.featurelimit); │ │ │ │ + } │ │ │ │ + if (typeof query.subfields == "string" && query.subfields != "#ALL#") { │ │ │ │ + queryElem.setAttribute("subfields", query.subfields); │ │ │ │ + } │ │ │ │ + if (typeof query.joinexpression == "string" && query.joinexpression.length > 0) { │ │ │ │ + queryElem.setAttribute("joinexpression", query.joinexpression); │ │ │ │ + } │ │ │ │ + if (typeof query.jointables == "string" && query.jointables.length > 0) { │ │ │ │ + queryElem.setAttribute("jointables", query.jointables); │ │ │ │ + } │ │ │ │ │ │ │ │ - return { │ │ │ │ - lon: center.lon + deltaX * res, │ │ │ │ - lat: center.lat - deltaY * res │ │ │ │ - }; │ │ │ │ - }, │ │ │ │ + ldef.appendChild(queryElem); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getOverviewPxFromLonLat │ │ │ │ - * Get a pixel location from a map location │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an │ │ │ │ - * object with a 'lon' and 'lat' properties. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Location which is the passed-in OpenLayers.LonLat, │ │ │ │ - * translated into overview map pixels │ │ │ │ - */ │ │ │ │ - getOverviewPxFromLonLat: function(lonlat) { │ │ │ │ - var res = this.ovmap.getResolution(); │ │ │ │ - var extent = this.ovmap.getExtent(); │ │ │ │ - if (extent) { │ │ │ │ - return { │ │ │ │ - x: Math.round(1 / res * (lonlat.lon - extent.left)), │ │ │ │ - y: Math.round(1 / res * (extent.top - lonlat.lat)) │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + if (typeof props.layerlist[ld].renderer == "object") { │ │ │ │ + this.addRenderer(ldef, props.layerlist[ld].renderer); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else if (request.get_feature != null) { │ │ │ │ + var getElem = this.createElementNS("", "GET_FEATURES"); │ │ │ │ + getElem.setAttribute("outputmode", "newxml"); │ │ │ │ + getElem.setAttribute("checkesc", "true"); │ │ │ │ │ │ │ │ - CLASS_NAME: 'OpenLayers.Control.OverviewMap' │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/Scale.js │ │ │ │ - ====================================================================== */ │ │ │ │ + if (request.get_feature.geometry) { │ │ │ │ + getElem.setAttribute("geometry", request.get_feature.geometry); │ │ │ │ + } else { │ │ │ │ + getElem.setAttribute("geometry", "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. */ │ │ │ │ + if (request.get_feature.compact) { │ │ │ │ + getElem.setAttribute("compact", request.get_feature.compact); │ │ │ │ + } │ │ │ │ │ │ │ │ + if (request.get_feature.featurelimit == "number") { │ │ │ │ + getElem.setAttribute("featurelimit", request.get_feature.featurelimit); │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Lang.js │ │ │ │ - */ │ │ │ │ + getElem.setAttribute("globalenvelope", "true"); │ │ │ │ + reqElem.appendChild(getElem); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.Scale │ │ │ │ - * The Scale control displays the current map scale as a ratio (e.g. Scale = │ │ │ │ - * 1:1M). By default it is displayed in the lower right corner of the map. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + if (request.get_feature.layer != null && request.get_feature.layer.length > 0) { │ │ │ │ + var lyrElem = this.createElementNS("", "LAYER"); │ │ │ │ + lyrElem.setAttribute("id", request.get_feature.layer); │ │ │ │ + getElem.appendChild(lyrElem); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: element │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - element: null, │ │ │ │ + var fquery = request.get_feature.query; │ │ │ │ + if (fquery != null) { │ │ │ │ + var qElem = null; │ │ │ │ + if (fquery.isspatial) { │ │ │ │ + qElem = this.createElementNS("", "SPATIALQUERY"); │ │ │ │ + } else { │ │ │ │ + qElem = this.createElementNS("", "QUERY"); │ │ │ │ + } │ │ │ │ + getElem.appendChild(qElem); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: geodesic │ │ │ │ - * {Boolean} Use geodesic measurement. Default is false. The recommended │ │ │ │ - * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to │ │ │ │ - * true, the scale will be calculated based on the horizontal size of the │ │ │ │ - * pixel in the center of the map viewport. │ │ │ │ - */ │ │ │ │ - geodesic: false, │ │ │ │ + if (typeof fquery.accuracy == "number") { │ │ │ │ + qElem.setAttribute("accuracy", fquery.accuracy); │ │ │ │ + } │ │ │ │ + //qElem.setAttribute("featurelimit", "5"); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.Scale │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * element - {DOMElement} │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ - initialize: function(element, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.element = OpenLayers.Util.getElement(element); │ │ │ │ + if (fquery.featurecoordsys != null) { │ │ │ │ + var fcsElem1 = this.createElementNS("", "FEATURECOORDSYS"); │ │ │ │ + │ │ │ │ + if (fquery.featurecoordsys.id == 0) { │ │ │ │ + fcsElem1.setAttribute("string", fquery.featurecoordsys.string); │ │ │ │ + } else { │ │ │ │ + fcsElem1.setAttribute("id", fquery.featurecoordsys.id); │ │ │ │ + } │ │ │ │ + qElem.appendChild(fcsElem1); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (fquery.filtercoordsys != null) { │ │ │ │ + var fcsElem2 = this.createElementNS("", "FILTERCOORDSYS"); │ │ │ │ + │ │ │ │ + if (fquery.filtercoordsys.id === 0) { │ │ │ │ + fcsElem2.setAttribute("string", fquery.filtercoordsys.string); │ │ │ │ + } else { │ │ │ │ + fcsElem2.setAttribute("id", fquery.filtercoordsys.id); │ │ │ │ + } │ │ │ │ + qElem.appendChild(fcsElem2); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (fquery.buffer > 0) { │ │ │ │ + var bufElem = this.createElementNS("", "BUFFER"); │ │ │ │ + bufElem.setAttribute("distance", fquery.buffer); │ │ │ │ + qElem.appendChild(bufElem); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (fquery.isspatial) { │ │ │ │ + var spfElem = this.createElementNS("", "SPATIALFILTER"); │ │ │ │ + spfElem.setAttribute("relation", fquery.spatialfilter.relation); │ │ │ │ + qElem.appendChild(spfElem); │ │ │ │ + │ │ │ │ + if (fquery.spatialfilter.envelope) { │ │ │ │ + var envElem = this.createElementNS("", "ENVELOPE"); │ │ │ │ + envElem.setAttribute("minx", fquery.spatialfilter.envelope.minx); │ │ │ │ + envElem.setAttribute("miny", fquery.spatialfilter.envelope.miny); │ │ │ │ + envElem.setAttribute("maxx", fquery.spatialfilter.envelope.maxx); │ │ │ │ + envElem.setAttribute("maxy", fquery.spatialfilter.envelope.maxy); │ │ │ │ + spfElem.appendChild(envElem); │ │ │ │ + } else if (typeof fquery.spatialfilter.polygon == "object") { │ │ │ │ + spfElem.appendChild(this.writePolygonGeometry(fquery.spatialfilter.polygon)); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (fquery.where != null && fquery.where.length > 0) { │ │ │ │ + qElem.setAttribute("where", fquery.where); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + root.appendChild(reqElem); │ │ │ │ + │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [root]); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (!this.element) { │ │ │ │ - this.element = document.createElement("div"); │ │ │ │ - this.div.appendChild(this.element); │ │ │ │ + │ │ │ │ + addGroupRenderer: function(ldef, toprenderer) { │ │ │ │ + var topRelem = this.createElementNS("", "GROUPRENDERER"); │ │ │ │ + ldef.appendChild(topRelem); │ │ │ │ + │ │ │ │ + for (var rind = 0; rind < toprenderer.length; rind++) { │ │ │ │ + var renderer = toprenderer[rind]; │ │ │ │ + this.addRenderer(topRelem, renderer); │ │ │ │ } │ │ │ │ - this.map.events.register('moveend', this, this.updateScale); │ │ │ │ - this.updateScale(); │ │ │ │ - return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: updateScale │ │ │ │ - */ │ │ │ │ - updateScale: function() { │ │ │ │ - var scale; │ │ │ │ - if (this.geodesic === true) { │ │ │ │ - var units = this.map.getUnits(); │ │ │ │ - if (!units) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - var inches = OpenLayers.INCHES_PER_UNIT; │ │ │ │ - scale = (this.map.getGeodesicPixelSize().w || 0.000001) * │ │ │ │ - inches["km"] * OpenLayers.DOTS_PER_INCH; │ │ │ │ + │ │ │ │ + addRenderer: function(topRelem, renderer) { │ │ │ │ + if (OpenLayers.Util.isArray(renderer)) { │ │ │ │ + this.addGroupRenderer(topRelem, renderer); │ │ │ │ } else { │ │ │ │ - scale = this.map.getScale(); │ │ │ │ - } │ │ │ │ + var renderElem = this.createElementNS("", renderer.type.toUpperCase() + "RENDERER"); │ │ │ │ + topRelem.appendChild(renderElem); │ │ │ │ │ │ │ │ - if (!scale) { │ │ │ │ - return; │ │ │ │ + if (renderElem.tagName == "VALUEMAPRENDERER") { │ │ │ │ + this.addValueMapRenderer(renderElem, renderer); │ │ │ │ + } else if (renderElem.tagName == "VALUEMAPLABELRENDERER") { │ │ │ │ + this.addValueMapLabelRenderer(renderElem, renderer); │ │ │ │ + } else if (renderElem.tagName == "SIMPLELABELRENDERER") { │ │ │ │ + this.addSimpleLabelRenderer(renderElem, renderer); │ │ │ │ + } else if (renderElem.tagName == "SCALEDEPENDENTRENDERER") { │ │ │ │ + this.addScaleDependentRenderer(renderElem, renderer); │ │ │ │ + } │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (scale >= 9500 && scale <= 950000) { │ │ │ │ - scale = Math.round(scale / 1000) + "K"; │ │ │ │ - } else if (scale >= 950000) { │ │ │ │ - scale = Math.round(scale / 1000000) + "M"; │ │ │ │ - } else { │ │ │ │ - scale = Math.round(scale); │ │ │ │ + │ │ │ │ + addScaleDependentRenderer: function(renderElem, renderer) { │ │ │ │ + if (typeof renderer.lower == "string" || typeof renderer.lower == "number") { │ │ │ │ + renderElem.setAttribute("lower", renderer.lower); │ │ │ │ + } │ │ │ │ + if (typeof renderer.upper == "string" || typeof renderer.upper == "number") { │ │ │ │ + renderElem.setAttribute("upper", renderer.upper); │ │ │ │ } │ │ │ │ │ │ │ │ - this.element.innerHTML = OpenLayers.i18n("Scale = 1 : ${scaleDenom}", { │ │ │ │ - 'scaleDenom': scale │ │ │ │ - }); │ │ │ │ + this.addRenderer(renderElem, renderer.renderer); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Scale" │ │ │ │ -}); │ │ │ │ │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/Graticule.js │ │ │ │ - ====================================================================== */ │ │ │ │ + addValueMapLabelRenderer: function(renderElem, renderer) { │ │ │ │ + renderElem.setAttribute("lookupfield", renderer.lookupfield); │ │ │ │ + renderElem.setAttribute("labelfield", renderer.labelfield); │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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 (typeof renderer.exacts == "object") { │ │ │ │ + for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) { │ │ │ │ + var exact = renderer.exacts[ext]; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Lang.js │ │ │ │ - * @requires OpenLayers/Rule.js │ │ │ │ - * @requires OpenLayers/StyleMap.js │ │ │ │ - * @requires OpenLayers/Layer/Vector.js │ │ │ │ - */ │ │ │ │ + var eelem = this.createElementNS("", "EXACT"); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.Graticule │ │ │ │ - * The Graticule displays a grid of latitude/longitude lines reprojected on │ │ │ │ - * the map. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - * │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + if (typeof exact.value == "string") { │ │ │ │ + eelem.setAttribute("value", exact.value); │ │ │ │ + } │ │ │ │ + if (typeof exact.label == "string") { │ │ │ │ + eelem.setAttribute("label", exact.label); │ │ │ │ + } │ │ │ │ + if (typeof exact.method == "string") { │ │ │ │ + eelem.setAttribute("method", exact.method); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * true. │ │ │ │ - */ │ │ │ │ - autoActivate: true, │ │ │ │ + renderElem.appendChild(eelem); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: intervals │ │ │ │ - * {Array(Float)} A list of possible graticule widths in degrees. │ │ │ │ - */ │ │ │ │ - intervals: [45, 30, 20, 10, 5, 2, 1, │ │ │ │ - 0.5, 0.2, 0.1, 0.05, 0.01, │ │ │ │ - 0.005, 0.002, 0.001 │ │ │ │ - ], │ │ │ │ + if (typeof exact.symbol == "object") { │ │ │ │ + var selem = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: displayInLayerSwitcher │ │ │ │ - * {Boolean} Allows the Graticule control to be switched on and off by │ │ │ │ - * LayerSwitcher control. Defaults is true. │ │ │ │ - */ │ │ │ │ - displayInLayerSwitcher: true, │ │ │ │ + if (exact.symbol.type == "text") { │ │ │ │ + selem = this.createElementNS("", "TEXTSYMBOL"); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: visible │ │ │ │ - * {Boolean} should the graticule be initially visible (default=true) │ │ │ │ - */ │ │ │ │ - visible: true, │ │ │ │ + if (selem != null) { │ │ │ │ + var keys = this.fontStyleKeys; │ │ │ │ + for (var i = 0, len = keys.length; i < len; i++) { │ │ │ │ + var key = keys[i]; │ │ │ │ + if (exact.symbol[key]) { │ │ │ │ + selem.setAttribute(key, exact.symbol[key]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + eelem.appendChild(selem); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } // for each exact │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: numPoints │ │ │ │ - * {Integer} The number of points to use in each graticule line. Higher │ │ │ │ - * numbers result in a smoother curve for projected maps │ │ │ │ - */ │ │ │ │ - numPoints: 50, │ │ │ │ + addValueMapRenderer: function(renderElem, renderer) { │ │ │ │ + renderElem.setAttribute("lookupfield", renderer.lookupfield); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: targetSize │ │ │ │ - * {Integer} The maximum size of the grid in pixels on the map │ │ │ │ - */ │ │ │ │ - targetSize: 200, │ │ │ │ + if (typeof renderer.ranges == "object") { │ │ │ │ + for (var rng = 0, rnglen = renderer.ranges.length; rng < rnglen; rng++) { │ │ │ │ + var range = renderer.ranges[rng]; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: layerName │ │ │ │ - * {String} The name to be displayed in the layer switcher, default is set │ │ │ │ - * by {<OpenLayers.Lang>}. │ │ │ │ - */ │ │ │ │ - layerName: null, │ │ │ │ + var relem = this.createElementNS("", "RANGE"); │ │ │ │ + relem.setAttribute("lower", range.lower); │ │ │ │ + relem.setAttribute("upper", range.upper); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: labelled │ │ │ │ - * {Boolean} Should the graticule lines be labelled?. default=true │ │ │ │ - */ │ │ │ │ - labelled: true, │ │ │ │ + renderElem.appendChild(relem); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: labelFormat │ │ │ │ - * {String} the format of the labels, default = 'dm'. See │ │ │ │ - * <OpenLayers.Util.getFormattedLonLat> for other options. │ │ │ │ - */ │ │ │ │ - labelFormat: 'dm', │ │ │ │ + if (typeof range.symbol == "object") { │ │ │ │ + var selem = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: lineSymbolizer │ │ │ │ - * {symbolizer} the symbolizer used to render lines │ │ │ │ - */ │ │ │ │ - lineSymbolizer: { │ │ │ │ - strokeColor: "#333", │ │ │ │ - strokeWidth: 1, │ │ │ │ - strokeOpacity: 0.5 │ │ │ │ - }, │ │ │ │ + if (range.symbol.type == "simplepolygon") { │ │ │ │ + selem = this.createElementNS("", "SIMPLEPOLYGONSYMBOL"); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: labelSymbolizer │ │ │ │ - * {symbolizer} the symbolizer used to render labels │ │ │ │ - */ │ │ │ │ - labelSymbolizer: {}, │ │ │ │ + if (selem != null) { │ │ │ │ + if (typeof range.symbol.boundarycolor == "string") { │ │ │ │ + selem.setAttribute("boundarycolor", range.symbol.boundarycolor); │ │ │ │ + } │ │ │ │ + if (typeof range.symbol.fillcolor == "string") { │ │ │ │ + selem.setAttribute("fillcolor", range.symbol.fillcolor); │ │ │ │ + } │ │ │ │ + if (typeof range.symbol.filltransparency == "number") { │ │ │ │ + selem.setAttribute("filltransparency", range.symbol.filltransparency); │ │ │ │ + } │ │ │ │ + relem.appendChild(selem); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } // for each range │ │ │ │ + } else if (typeof renderer.exacts == "object") { │ │ │ │ + for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) { │ │ │ │ + var exact = renderer.exacts[ext]; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: gratLayer │ │ │ │ - * {<OpenLayers.Layer.Vector>} vector layer used to draw the graticule on │ │ │ │ - */ │ │ │ │ - gratLayer: null, │ │ │ │ + var eelem = this.createElementNS("", "EXACT"); │ │ │ │ + if (typeof exact.value == "string") { │ │ │ │ + eelem.setAttribute("value", exact.value); │ │ │ │ + } │ │ │ │ + if (typeof exact.label == "string") { │ │ │ │ + eelem.setAttribute("label", exact.label); │ │ │ │ + } │ │ │ │ + if (typeof exact.method == "string") { │ │ │ │ + eelem.setAttribute("method", exact.method); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.Graticule │ │ │ │ - * Create a new graticule control to display a grid of latitude longitude │ │ │ │ - * lines. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be used │ │ │ │ - * to extend the control. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - options.layerName = options.layerName || OpenLayers.i18n("Graticule"); │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + renderElem.appendChild(eelem); │ │ │ │ │ │ │ │ - this.labelSymbolizer.stroke = false; │ │ │ │ - this.labelSymbolizer.fill = false; │ │ │ │ - this.labelSymbolizer.label = "${label}"; │ │ │ │ - this.labelSymbolizer.labelAlign = "${labelAlign}"; │ │ │ │ - this.labelSymbolizer.labelXOffset = "${xOffset}"; │ │ │ │ - this.labelSymbolizer.labelYOffset = "${yOffset}"; │ │ │ │ - }, │ │ │ │ + if (typeof exact.symbol == "object") { │ │ │ │ + var selem = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - if (this.gratLayer) { │ │ │ │ - this.gratLayer.destroy(); │ │ │ │ - this.gratLayer = null; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + if (exact.symbol.type == "simplemarker") { │ │ │ │ + selem = this.createElementNS("", "SIMPLEMARKERSYMBOL"); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ - * │ │ │ │ - * initializes the graticule layer and does the initial update │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (!this.gratLayer) { │ │ │ │ - var gratStyle = new OpenLayers.Style({}, { │ │ │ │ - rules: [new OpenLayers.Rule({ │ │ │ │ - 'symbolizer': { │ │ │ │ - "Point": this.labelSymbolizer, │ │ │ │ - "Line": this.lineSymbolizer │ │ │ │ + if (selem != null) { │ │ │ │ + if (typeof exact.symbol.antialiasing == "string") { │ │ │ │ + selem.setAttribute("antialiasing", exact.symbol.antialiasing); │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.color == "string") { │ │ │ │ + selem.setAttribute("color", exact.symbol.color); │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.outline == "string") { │ │ │ │ + selem.setAttribute("outline", exact.symbol.outline); │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.overlap == "string") { │ │ │ │ + selem.setAttribute("overlap", exact.symbol.overlap); │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.shadow == "string") { │ │ │ │ + selem.setAttribute("shadow", exact.symbol.shadow); │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.transparency == "number") { │ │ │ │ + selem.setAttribute("transparency", exact.symbol.transparency); │ │ │ │ + } │ │ │ │ + //if (typeof exact.symbol.type == "string") │ │ │ │ + // selem.setAttribute("type", exact.symbol.type); │ │ │ │ + if (typeof exact.symbol.usecentroid == "string") { │ │ │ │ + selem.setAttribute("usecentroid", exact.symbol.usecentroid); │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.width == "number") { │ │ │ │ + selem.setAttribute("width", exact.symbol.width); │ │ │ │ + } │ │ │ │ + │ │ │ │ + eelem.appendChild(selem); │ │ │ │ } │ │ │ │ - })] │ │ │ │ - }); │ │ │ │ - this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, { │ │ │ │ - styleMap: new OpenLayers.StyleMap({ │ │ │ │ - 'default': gratStyle │ │ │ │ - }), │ │ │ │ - visibility: this.visible, │ │ │ │ - displayInLayerSwitcher: this.displayInLayerSwitcher │ │ │ │ - }); │ │ │ │ + } │ │ │ │ + } // for each exact │ │ │ │ } │ │ │ │ - return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.map.addLayer(this.gratLayer); │ │ │ │ - this.map.events.register('moveend', this, this.update); │ │ │ │ - this.update(); │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ + │ │ │ │ + addSimpleLabelRenderer: function(renderElem, renderer) { │ │ │ │ + renderElem.setAttribute("field", renderer.field); │ │ │ │ + var keys = ['featureweight', 'howmanylabels', 'labelbufferratio', │ │ │ │ + 'labelpriorities', 'labelweight', 'linelabelposition', │ │ │ │ + 'rotationalangles' │ │ │ │ + ]; │ │ │ │ + for (var i = 0, len = keys.length; i < len; i++) { │ │ │ │ + var key = keys[i]; │ │ │ │ + if (renderer[key]) { │ │ │ │ + renderElem.setAttribute(key, renderer[key]); │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.map.events.unregister('moveend', this, this.update); │ │ │ │ - this.map.removeLayer(this.gratLayer); │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ + if (renderer.symbol.type == "text") { │ │ │ │ + var symbol = renderer.symbol; │ │ │ │ + var selem = this.createElementNS("", "TEXTSYMBOL"); │ │ │ │ + renderElem.appendChild(selem); │ │ │ │ + │ │ │ │ + var keys = this.fontStyleKeys; │ │ │ │ + for (var i = 0, len = keys.length; i < len; i++) { │ │ │ │ + var key = keys[i]; │ │ │ │ + if (symbol[key]) { │ │ │ │ + selem.setAttribute(key, renderer[key]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ - /** │ │ │ │ - * Method: update │ │ │ │ - * │ │ │ │ - * calculates the grid to be displayed and actually draws it │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - update: function() { │ │ │ │ - //wait for the map to be initialized before proceeding │ │ │ │ - var mapBounds = this.map.getExtent(); │ │ │ │ - if (!mapBounds) { │ │ │ │ - return; │ │ │ │ + │ │ │ │ + writePolygonGeometry: function(polygon) { │ │ │ │ + if (!(polygon instanceof OpenLayers.Geometry.Polygon)) { │ │ │ │ + throw { │ │ │ │ + message: 'Cannot write polygon geometry to ArcXML with an ' + │ │ │ │ + polygon.CLASS_NAME + ' object.', │ │ │ │ + geometry: polygon │ │ │ │ + }; │ │ │ │ } │ │ │ │ │ │ │ │ - //clear out the old grid │ │ │ │ - this.gratLayer.destroyFeatures(); │ │ │ │ + var polyElem = this.createElementNS("", "POLYGON"); │ │ │ │ │ │ │ │ - //get the projection objects required │ │ │ │ - var llProj = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ - var mapProj = this.map.getProjectionObject(); │ │ │ │ - var mapRes = this.map.getResolution(); │ │ │ │ + for (var ln = 0, lnlen = polygon.components.length; ln < lnlen; ln++) { │ │ │ │ + var ring = polygon.components[ln]; │ │ │ │ + var ringElem = this.createElementNS("", "RING"); │ │ │ │ │ │ │ │ - //if the map is in lon/lat, then the lines are straight and only one │ │ │ │ - //point is required │ │ │ │ - if (mapProj.proj && mapProj.proj.projName == "longlat") { │ │ │ │ - this.numPoints = 1; │ │ │ │ - } │ │ │ │ + for (var rn = 0, rnlen = ring.components.length; rn < rnlen; rn++) { │ │ │ │ + var point = ring.components[rn]; │ │ │ │ + var pointElem = this.createElementNS("", "POINT"); │ │ │ │ │ │ │ │ - //get the map center in EPSG:4326 │ │ │ │ - var mapCenter = this.map.getCenter(); //lon and lat here are really map x and y │ │ │ │ - var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat); │ │ │ │ - OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj); │ │ │ │ + pointElem.setAttribute("x", point.x); │ │ │ │ + pointElem.setAttribute("y", point.y); │ │ │ │ │ │ │ │ - /* This block of code determines the lon/lat interval to use for the │ │ │ │ - * grid by calculating the diagonal size of one grid cell at the map │ │ │ │ - * center. Iterates through the intervals array until the diagonal │ │ │ │ - * length is less than the targetSize option. │ │ │ │ - */ │ │ │ │ - //find lat/lon interval that results in a grid of less than the target size │ │ │ │ - var testSq = this.targetSize * mapRes; │ │ │ │ - testSq *= testSq; //compare squares rather than doing a square root to save time │ │ │ │ - var llInterval; │ │ │ │ - for (var i = 0; i < this.intervals.length; ++i) { │ │ │ │ - llInterval = this.intervals[i]; //could do this for both x and y?? │ │ │ │ - var delta = llInterval / 2; │ │ │ │ - var p1 = mapCenterLL.offset({ │ │ │ │ - x: -delta, │ │ │ │ - y: -delta │ │ │ │ - }); //test coords in EPSG:4326 space │ │ │ │ - var p2 = mapCenterLL.offset({ │ │ │ │ - x: delta, │ │ │ │ - y: delta │ │ │ │ - }); │ │ │ │ - OpenLayers.Projection.transform(p1, llProj, mapProj); // convert them back to map projection │ │ │ │ - OpenLayers.Projection.transform(p2, llProj, mapProj); │ │ │ │ - var distSq = (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y); │ │ │ │ - if (distSq <= testSq) { │ │ │ │ - break; │ │ │ │ + ringElem.appendChild(pointElem); │ │ │ │ } │ │ │ │ + │ │ │ │ + polyElem.appendChild(ringElem); │ │ │ │ } │ │ │ │ - //alert(llInterval); │ │ │ │ │ │ │ │ - //round the LL center to an even number based on the interval │ │ │ │ - mapCenterLL.x = Math.floor(mapCenterLL.x / llInterval) * llInterval; │ │ │ │ - mapCenterLL.y = Math.floor(mapCenterLL.y / llInterval) * llInterval; │ │ │ │ - //TODO adjust for minutses/seconds? │ │ │ │ + return polyElem; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /* The following 2 blocks calculate the nodes of the grid along a │ │ │ │ - * line of constant longitude (then latitiude) running through the │ │ │ │ - * center of the map until it reaches the map edge. The calculation │ │ │ │ - * goes from the center in both directions to the edge. │ │ │ │ - */ │ │ │ │ - //get the central longitude line, increment the latitude │ │ │ │ - var iter = 0; │ │ │ │ - var centerLonPoints = [mapCenterLL.clone()]; │ │ │ │ - var newPoint = mapCenterLL.clone(); │ │ │ │ - var mapXY; │ │ │ │ - do { │ │ │ │ - newPoint = newPoint.offset({ │ │ │ │ - x: 0, │ │ │ │ - y: llInterval │ │ │ │ - }); │ │ │ │ - mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); │ │ │ │ - centerLonPoints.unshift(newPoint); │ │ │ │ - } while (mapBounds.containsPixel(mapXY) && ++iter < 1000); │ │ │ │ - newPoint = mapCenterLL.clone(); │ │ │ │ - do { │ │ │ │ - newPoint = newPoint.offset({ │ │ │ │ - x: 0, │ │ │ │ - y: -llInterval │ │ │ │ - }); │ │ │ │ - mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); │ │ │ │ - centerLonPoints.push(newPoint); │ │ │ │ - } while (mapBounds.containsPixel(mapXY) && ++iter < 1000); │ │ │ │ + /** │ │ │ │ + * Method: parseResponse │ │ │ │ + * Take an ArcXML response, and parse in into this object's internal properties. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} The ArcXML response, as either a string or the │ │ │ │ + * top level DOMElement of the response. │ │ │ │ + */ │ │ │ │ + parseResponse: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + var newData = new OpenLayers.Format.XML(); │ │ │ │ + data = newData.read(data); │ │ │ │ + } │ │ │ │ + var response = new OpenLayers.Format.ArcXML.Response(); │ │ │ │ │ │ │ │ - //get the central latitude line, increment the longitude │ │ │ │ - iter = 0; │ │ │ │ - var centerLatPoints = [mapCenterLL.clone()]; │ │ │ │ - newPoint = mapCenterLL.clone(); │ │ │ │ - do { │ │ │ │ - newPoint = newPoint.offset({ │ │ │ │ - x: -llInterval, │ │ │ │ - y: 0 │ │ │ │ - }); │ │ │ │ - mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); │ │ │ │ - centerLatPoints.unshift(newPoint); │ │ │ │ - } while (mapBounds.containsPixel(mapXY) && ++iter < 1000); │ │ │ │ - newPoint = mapCenterLL.clone(); │ │ │ │ - do { │ │ │ │ - newPoint = newPoint.offset({ │ │ │ │ - x: llInterval, │ │ │ │ - y: 0 │ │ │ │ - }); │ │ │ │ - mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); │ │ │ │ - centerLatPoints.push(newPoint); │ │ │ │ - } while (mapBounds.containsPixel(mapXY) && ++iter < 1000); │ │ │ │ + var errorNode = data.getElementsByTagName("ERROR"); │ │ │ │ │ │ │ │ - //now generate a line for each node in the central lat and lon lines │ │ │ │ - //first loop over constant longitude │ │ │ │ - var lines = []; │ │ │ │ - for (var i = 0; i < centerLatPoints.length; ++i) { │ │ │ │ - var lon = centerLatPoints[i].x; │ │ │ │ - var pointList = []; │ │ │ │ - var labelPoint = null; │ │ │ │ - var latEnd = Math.min(centerLonPoints[0].y, 90); │ │ │ │ - var latStart = Math.max(centerLonPoints[centerLonPoints.length - 1].y, -90); │ │ │ │ - var latDelta = (latEnd - latStart) / this.numPoints; │ │ │ │ - var lat = latStart; │ │ │ │ - for (var j = 0; j <= this.numPoints; ++j) { │ │ │ │ - var gridPoint = new OpenLayers.Geometry.Point(lon, lat); │ │ │ │ - gridPoint.transform(llProj, mapProj); │ │ │ │ - pointList.push(gridPoint); │ │ │ │ - lat += latDelta; │ │ │ │ - if (gridPoint.y >= mapBounds.bottom && !labelPoint) { │ │ │ │ - labelPoint = gridPoint; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.labelled) { │ │ │ │ - //keep track of when this grid line crosses the map bounds to set │ │ │ │ - //the label position │ │ │ │ - //labels along the bottom, add 10 pixel offset up into the map │ │ │ │ - //TODO add option for labels on top │ │ │ │ - var labelPos = new OpenLayers.Geometry.Point(labelPoint.x, mapBounds.bottom); │ │ │ │ - var labelAttrs = { │ │ │ │ - value: lon, │ │ │ │ - label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lon, "lon", this.labelFormat) : "", │ │ │ │ - labelAlign: "cb", │ │ │ │ - xOffset: 0, │ │ │ │ - yOffset: 2 │ │ │ │ - }; │ │ │ │ - this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs)); │ │ │ │ + if (errorNode != null && errorNode.length > 0) { │ │ │ │ + response.error = this.getChildValue(errorNode, "Unknown error."); │ │ │ │ + } else { │ │ │ │ + var responseNode = data.getElementsByTagName("RESPONSE"); │ │ │ │ + │ │ │ │ + if (responseNode == null || responseNode.length == 0) { │ │ │ │ + response.error = "No RESPONSE tag found in ArcXML response."; │ │ │ │ + return response; │ │ │ │ } │ │ │ │ - var geom = new OpenLayers.Geometry.LineString(pointList); │ │ │ │ - lines.push(new OpenLayers.Feature.Vector(geom)); │ │ │ │ - } │ │ │ │ │ │ │ │ - //now draw the lines of constant latitude │ │ │ │ - for (var j = 0; j < centerLonPoints.length; ++j) { │ │ │ │ - lat = centerLonPoints[j].y; │ │ │ │ - if (lat < -90 || lat > 90) { //latitudes only valid between -90 and 90 │ │ │ │ - continue; │ │ │ │ + var rtype = responseNode[0].firstChild.nodeName; │ │ │ │ + if (rtype == "#text") { │ │ │ │ + rtype = responseNode[0].firstChild.nextSibling.nodeName; │ │ │ │ } │ │ │ │ - var pointList = []; │ │ │ │ - var lonStart = centerLatPoints[0].x; │ │ │ │ - var lonEnd = centerLatPoints[centerLatPoints.length - 1].x; │ │ │ │ - var lonDelta = (lonEnd - lonStart) / this.numPoints; │ │ │ │ - var lon = lonStart; │ │ │ │ - var labelPoint = null; │ │ │ │ - for (var i = 0; i <= this.numPoints; ++i) { │ │ │ │ - var gridPoint = new OpenLayers.Geometry.Point(lon, lat); │ │ │ │ - gridPoint.transform(llProj, mapProj); │ │ │ │ - pointList.push(gridPoint); │ │ │ │ - lon += lonDelta; │ │ │ │ - if (gridPoint.x < mapBounds.right) { │ │ │ │ - labelPoint = gridPoint; │ │ │ │ + │ │ │ │ + if (rtype == "IMAGE") { │ │ │ │ + var envelopeNode = data.getElementsByTagName("ENVELOPE"); │ │ │ │ + var outputNode = data.getElementsByTagName("OUTPUT"); │ │ │ │ + │ │ │ │ + if (envelopeNode == null || envelopeNode.length == 0) { │ │ │ │ + response.error = "No ENVELOPE tag found in ArcXML response."; │ │ │ │ + } else if (outputNode == null || outputNode.length == 0) { │ │ │ │ + response.error = "No OUTPUT tag found in ArcXML response."; │ │ │ │ + } else { │ │ │ │ + var envAttr = this.parseAttributes(envelopeNode[0]); │ │ │ │ + var outputAttr = this.parseAttributes(outputNode[0]); │ │ │ │ + │ │ │ │ + if (typeof outputAttr.type == "string") { │ │ │ │ + response.image = { │ │ │ │ + envelope: envAttr, │ │ │ │ + output: { │ │ │ │ + type: outputAttr.type, │ │ │ │ + data: this.getChildValue(outputNode[0]) │ │ │ │ + } │ │ │ │ + }; │ │ │ │ + } else { │ │ │ │ + response.image = { │ │ │ │ + envelope: envAttr, │ │ │ │ + output: outputAttr │ │ │ │ + }; │ │ │ │ + } │ │ │ │ } │ │ │ │ - } │ │ │ │ - if (this.labelled) { │ │ │ │ - //keep track of when this grid line crosses the map bounds to set │ │ │ │ - //the label position │ │ │ │ - //labels along the right, 30 pixel offset left into the map │ │ │ │ - //TODO add option for labels on left │ │ │ │ - var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y); │ │ │ │ - var labelAttrs = { │ │ │ │ - value: lat, │ │ │ │ - label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lat, "lat", this.labelFormat) : "", │ │ │ │ - labelAlign: "rb", │ │ │ │ - xOffset: -2, │ │ │ │ - yOffset: 2 │ │ │ │ - }; │ │ │ │ - this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs)); │ │ │ │ - } │ │ │ │ - var geom = new OpenLayers.Geometry.LineString(pointList); │ │ │ │ - lines.push(new OpenLayers.Feature.Vector(geom)); │ │ │ │ - } │ │ │ │ - this.gratLayer.addFeatures(lines); │ │ │ │ - }, │ │ │ │ + } else if (rtype == "FEATURES") { │ │ │ │ + var features = responseNode[0].getElementsByTagName("FEATURES"); │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Graticule" │ │ │ │ -}); │ │ │ │ + // get the feature count │ │ │ │ + var featureCount = features[0].getElementsByTagName("FEATURECOUNT"); │ │ │ │ + response.features.featurecount = featureCount[0].getAttribute("count"); │ │ │ │ │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/ArgParser.js │ │ │ │ - ====================================================================== */ │ │ │ │ + if (response.features.featurecount > 0) { │ │ │ │ + // get the feature envelope │ │ │ │ + var envelope = features[0].getElementsByTagName("ENVELOPE"); │ │ │ │ + response.features.envelope = this.parseAttributes(envelope[0], typeof(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. */ │ │ │ │ + // get the field values per feature │ │ │ │ + var featureList = features[0].getElementsByTagName("FEATURE"); │ │ │ │ + for (var fn = 0; fn < featureList.length; fn++) { │ │ │ │ + var feature = new OpenLayers.Feature.Vector(); │ │ │ │ + var fields = featureList[fn].getElementsByTagName("FIELD"); │ │ │ │ + │ │ │ │ + for (var fdn = 0; fdn < fields.length; fdn++) { │ │ │ │ + var fieldName = fields[fdn].getAttribute("name"); │ │ │ │ + var fieldValue = fields[fdn].getAttribute("value"); │ │ │ │ + feature.attributes[fieldName] = fieldValue; │ │ │ │ + } │ │ │ │ │ │ │ │ + var geom = featureList[fn].getElementsByTagName("POLYGON"); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - */ │ │ │ │ + if (geom.length > 0) { │ │ │ │ + // if there is a polygon, create an openlayers polygon, and assign │ │ │ │ + // it to the .geometry property of the feature │ │ │ │ + var ring = geom[0].getElementsByTagName("RING"); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.ArgParser │ │ │ │ - * The ArgParser control adds location bar query string parsing functionality │ │ │ │ - * to an OpenLayers Map. │ │ │ │ - * When added to a Map control, on a page load/refresh, the Map will │ │ │ │ - * automatically take the href string and parse it for lon, lat, zoom, and │ │ │ │ - * layers information. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + var polys = []; │ │ │ │ + for (var rn = 0; rn < ring.length; rn++) { │ │ │ │ + var linearRings = []; │ │ │ │ + linearRings.push(this.parsePointGeometry(ring[rn])); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: center │ │ │ │ - * {<OpenLayers.LonLat>} │ │ │ │ - */ │ │ │ │ - center: null, │ │ │ │ + var holes = ring[rn].getElementsByTagName("HOLE"); │ │ │ │ + for (var hn = 0; hn < holes.length; hn++) { │ │ │ │ + linearRings.push(this.parsePointGeometry(holes[hn])); │ │ │ │ + } │ │ │ │ + holes = null; │ │ │ │ + polys.push(new OpenLayers.Geometry.Polygon(linearRings)); │ │ │ │ + linearRings = null; │ │ │ │ + } │ │ │ │ + ring = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: zoom │ │ │ │ - * {int} │ │ │ │ - */ │ │ │ │ - zoom: null, │ │ │ │ + if (polys.length == 1) { │ │ │ │ + feature.geometry = polys[0]; │ │ │ │ + } else { │ │ │ │ + feature.geometry = new OpenLayers.Geometry.MultiPolygon(polys); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: layers │ │ │ │ - * {String} Each character represents the state of the corresponding layer │ │ │ │ - * on the map. │ │ │ │ - */ │ │ │ │ - layers: null, │ │ │ │ + response.features.feature.push(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + response.error = "Unidentified response type."; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return response; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: displayProjection │ │ │ │ - * {<OpenLayers.Projection>} Requires proj4js support. │ │ │ │ - * Projection used when reading the coordinates from the URL. This will │ │ │ │ - * reproject the map coordinates from the URL into the map's │ │ │ │ - * projection. │ │ │ │ - * │ │ │ │ - * If you are using this functionality, be aware that any permalink │ │ │ │ - * which is added to the map will determine the coordinate type which │ │ │ │ - * is read from the URL, which means you should not add permalinks with │ │ │ │ - * different displayProjections to the same map. │ │ │ │ - */ │ │ │ │ - displayProjection: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.ArgParser │ │ │ │ + * Method: parseAttributes │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getParameters │ │ │ │ + * node - {<DOMElement>} An element to parse attributes from. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An attributes object, with properties set to attribute values. │ │ │ │ */ │ │ │ │ - getParameters: function(url) { │ │ │ │ - url = url || window.location.href; │ │ │ │ - var parameters = OpenLayers.Util.getParameters(url); │ │ │ │ - │ │ │ │ - // If we have an anchor in the url use it to split the url │ │ │ │ - var index = url.indexOf('#'); │ │ │ │ - if (index > 0) { │ │ │ │ - // create an url to parse on the getParameters │ │ │ │ - url = '?' + url.substring(index + 1, url.length); │ │ │ │ - │ │ │ │ - OpenLayers.Util.extend(parameters, │ │ │ │ - OpenLayers.Util.getParameters(url)); │ │ │ │ + parseAttributes: function(node, type) { │ │ │ │ + var attributes = {}; │ │ │ │ + for (var attr = 0; attr < node.attributes.length; attr++) { │ │ │ │ + if (type == "number") { │ │ │ │ + attributes[node.attributes[attr].nodeName] = parseFloat(node.attributes[attr].nodeValue); │ │ │ │ + } else { │ │ │ │ + attributes[node.attributes[attr].nodeName] = node.attributes[attr].nodeValue; │ │ │ │ + } │ │ │ │ } │ │ │ │ - return parameters; │ │ │ │ + return attributes; │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the control. │ │ │ │ - * │ │ │ │ + * Method: parsePointGeometry │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * node - {<DOMElement>} An element to parse <COORDS> or <POINT> arcxml data from. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry.LinearRing>} A linear ring represented by the node's points. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - │ │ │ │ - //make sure we dont already have an arg parser attached │ │ │ │ - for (var i = 0, len = this.map.controls.length; i < len; i++) { │ │ │ │ - var control = this.map.controls[i]; │ │ │ │ - if ((control != this) && │ │ │ │ - (control.CLASS_NAME == "OpenLayers.Control.ArgParser")) { │ │ │ │ + parsePointGeometry: function(node) { │ │ │ │ + var ringPoints = []; │ │ │ │ + var coords = node.getElementsByTagName("COORDS"); │ │ │ │ │ │ │ │ - // If a second argparser is added to the map, then we │ │ │ │ - // override the displayProjection to be the one added to the │ │ │ │ - // map. │ │ │ │ - if (control.displayProjection != this.displayProjection) { │ │ │ │ - this.displayProjection = control.displayProjection; │ │ │ │ + if (coords.length > 0) { │ │ │ │ + // if coords is present, it's the only coords item │ │ │ │ + var coordArr = this.getChildValue(coords[0]); │ │ │ │ + coordArr = coordArr.split(/;/); │ │ │ │ + for (var cn = 0; cn < coordArr.length; cn++) { │ │ │ │ + var coordItems = coordArr[cn].split(/ /); │ │ │ │ + ringPoints.push(new OpenLayers.Geometry.Point(coordItems[0], coordItems[1])); │ │ │ │ + } │ │ │ │ + coords = null; │ │ │ │ + } else { │ │ │ │ + var point = node.getElementsByTagName("POINT"); │ │ │ │ + if (point.length > 0) { │ │ │ │ + for (var pn = 0; pn < point.length; pn++) { │ │ │ │ + ringPoints.push( │ │ │ │ + new OpenLayers.Geometry.Point( │ │ │ │ + parseFloat(point[pn].getAttribute("x")), │ │ │ │ + parseFloat(point[pn].getAttribute("y")) │ │ │ │ + ) │ │ │ │ + ); │ │ │ │ } │ │ │ │ - │ │ │ │ - break; │ │ │ │ } │ │ │ │ + point = null; │ │ │ │ } │ │ │ │ - if (i == this.map.controls.length) { │ │ │ │ │ │ │ │ - var args = this.getParameters(); │ │ │ │ - // Be careful to set layer first, to not trigger unnecessary layer loads │ │ │ │ - if (args.layers) { │ │ │ │ - this.layers = args.layers; │ │ │ │ + return new OpenLayers.Geometry.LinearRing(ringPoints); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // when we add a new layer, set its visibility │ │ │ │ - this.map.events.register('addlayer', this, │ │ │ │ - this.configureLayers); │ │ │ │ - this.configureLayers(); │ │ │ │ - } │ │ │ │ - if (args.lat && args.lon) { │ │ │ │ - this.center = new OpenLayers.LonLat(parseFloat(args.lon), │ │ │ │ - parseFloat(args.lat)); │ │ │ │ - if (args.zoom) { │ │ │ │ - this.zoom = parseFloat(args.zoom); │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Format.ArcXML" │ │ │ │ +}); │ │ │ │ │ │ │ │ - // when we add a new baselayer to see when we can set the center │ │ │ │ - this.map.events.register('changebaselayer', this, │ │ │ │ - this.setCenter); │ │ │ │ - this.setCenter(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ +OpenLayers.Format.ArcXML.Request = OpenLayers.Class({ │ │ │ │ + initialize: function(params) { │ │ │ │ + var defaults = { │ │ │ │ + get_image: { │ │ │ │ + properties: { │ │ │ │ + background: null, │ │ │ │ + /*{ │ │ │ │ + color: { r:255, g:255, b:255 }, │ │ │ │ + transcolor: null │ │ │ │ + },*/ │ │ │ │ + draw: true, │ │ │ │ + envelope: { │ │ │ │ + minx: 0, │ │ │ │ + miny: 0, │ │ │ │ + maxx: 0, │ │ │ │ + maxy: 0 │ │ │ │ + }, │ │ │ │ + featurecoordsys: { │ │ │ │ + id: 0, │ │ │ │ + string: "", │ │ │ │ + datumtransformid: 0, │ │ │ │ + datumtransformstring: "" │ │ │ │ + }, │ │ │ │ + filtercoordsys: { │ │ │ │ + id: 0, │ │ │ │ + string: "", │ │ │ │ + datumtransformid: 0, │ │ │ │ + datumtransformstring: "" │ │ │ │ + }, │ │ │ │ + imagesize: { │ │ │ │ + height: 0, │ │ │ │ + width: 0, │ │ │ │ + dpi: 96, │ │ │ │ + printheight: 0, │ │ │ │ + printwidth: 0, │ │ │ │ + scalesymbols: false │ │ │ │ + }, │ │ │ │ + layerlist: [], │ │ │ │ + /* no support for legends */ │ │ │ │ + output: { │ │ │ │ + baseurl: "", │ │ │ │ + legendbaseurl: "", │ │ │ │ + legendname: "", │ │ │ │ + legendpath: "", │ │ │ │ + legendurl: "", │ │ │ │ + name: "", │ │ │ │ + path: "", │ │ │ │ + type: "jpg", │ │ │ │ + url: "" │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setCenter │ │ │ │ - * As soon as a baseLayer has been loaded, we center and zoom │ │ │ │ - * ...and remove the handler. │ │ │ │ - */ │ │ │ │ - setCenter: function() { │ │ │ │ + get_feature: { │ │ │ │ + layer: "", │ │ │ │ + query: { │ │ │ │ + isspatial: false, │ │ │ │ + featurecoordsys: { │ │ │ │ + id: 0, │ │ │ │ + string: "", │ │ │ │ + datumtransformid: 0, │ │ │ │ + datumtransformstring: "" │ │ │ │ + }, │ │ │ │ + filtercoordsys: { │ │ │ │ + id: 0, │ │ │ │ + string: "", │ │ │ │ + datumtransformid: 0, │ │ │ │ + datumtransformstring: "" │ │ │ │ + }, │ │ │ │ + buffer: 0, │ │ │ │ + where: "", │ │ │ │ + spatialfilter: { │ │ │ │ + relation: "envelope_intersection", │ │ │ │ + envelope: null │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.map.baseLayer) { │ │ │ │ - //dont need to listen for this one anymore │ │ │ │ - this.map.events.unregister('changebaselayer', this, │ │ │ │ - this.setCenter); │ │ │ │ + environment: { │ │ │ │ + separators: { │ │ │ │ + cs: " ", │ │ │ │ + ts: ";" │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.displayProjection) { │ │ │ │ - this.center.transform(this.displayProjection, │ │ │ │ - this.map.getProjectionObject()); │ │ │ │ - } │ │ │ │ + layer: [], │ │ │ │ + workspaces: [] │ │ │ │ + }; │ │ │ │ │ │ │ │ - this.map.setCenter(this.center, this.zoom); │ │ │ │ - } │ │ │ │ + return OpenLayers.Util.extend(this, defaults); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: configureLayers │ │ │ │ - * As soon as all the layers are loaded, cycle through them and │ │ │ │ - * hide or show them. │ │ │ │ - */ │ │ │ │ - configureLayers: function() { │ │ │ │ + CLASS_NAME: "OpenLayers.Format.ArcXML.Request" │ │ │ │ +}); │ │ │ │ │ │ │ │ - if (this.layers.length == this.map.layers.length) { │ │ │ │ - this.map.events.unregister('addlayer', this, this.configureLayers); │ │ │ │ +OpenLayers.Format.ArcXML.Response = OpenLayers.Class({ │ │ │ │ + initialize: function(params) { │ │ │ │ + var defaults = { │ │ │ │ + image: { │ │ │ │ + envelope: null, │ │ │ │ + output: '' │ │ │ │ + }, │ │ │ │ │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ + features: { │ │ │ │ + featurecount: 0, │ │ │ │ + envelope: null, │ │ │ │ + feature: [] │ │ │ │ + }, │ │ │ │ │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - var c = this.layers.charAt(i); │ │ │ │ + error: '' │ │ │ │ + }; │ │ │ │ │ │ │ │ - if (c == "B") { │ │ │ │ - this.map.setBaseLayer(layer); │ │ │ │ - } else if ((c == "T") || (c == "F")) { │ │ │ │ - layer.setVisibility(c == "T"); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + return OpenLayers.Util.extend(this, defaults); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ArgParser" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.ArcXML.Response" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/Permalink.js │ │ │ │ + OpenLayers/Format/CSWGetDomain.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format.js │ │ │ │ + */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Control/ArgParser.js │ │ │ │ - * @requires OpenLayers/Lang.js │ │ │ │ + * Class: OpenLayers.Format.CSWGetDomain │ │ │ │ + * Default version is 2.0.2. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Format>} A CSWGetDomain format of the given version. │ │ │ │ */ │ │ │ │ +OpenLayers.Format.CSWGetDomain = function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults( │ │ │ │ + options, OpenLayers.Format.CSWGetDomain.DEFAULTS │ │ │ │ + ); │ │ │ │ + var cls = OpenLayers.Format.CSWGetDomain["v" + options.version.replace(/\./g, "_")]; │ │ │ │ + if (!cls) { │ │ │ │ + throw "Unsupported CSWGetDomain version: " + options.version; │ │ │ │ + } │ │ │ │ + return new cls(options); │ │ │ │ +}; │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.Permalink │ │ │ │ - * The Permalink control is hyperlink that will return the user to the │ │ │ │ - * current map view. By default it is drawn in the lower right corner of the │ │ │ │ - * map. The href is updated as the map is zoomed, panned and whilst layers │ │ │ │ - * are switched. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * Constant: DEFAULTS │ │ │ │ + * {Object} Default properties for the CSWGetDomain format. │ │ │ │ */ │ │ │ │ -OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ +OpenLayers.Format.CSWGetDomain.DEFAULTS = { │ │ │ │ + "version": "2.0.2" │ │ │ │ +}; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WFS.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: argParserClass │ │ │ │ - * {Class} The ArgParser control class (not instance) to use with this │ │ │ │ - * control. │ │ │ │ - */ │ │ │ │ - argParserClass: OpenLayers.Control.ArgParser, │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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: element │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - element: null, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/GML.js │ │ │ │ + * @requires OpenLayers/Console.js │ │ │ │ + * @requires OpenLayers/Lang.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WFS │ │ │ │ + * Read/Write WFS. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.GML> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WFS = OpenLayers.Class(OpenLayers.Format.GML, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: anchor │ │ │ │ - * {Boolean} This option changes 3 things: │ │ │ │ - * the character '#' is used in place of the character '?', │ │ │ │ - * the window.href is updated if no element is provided. │ │ │ │ - * When this option is set to true it's not recommend to provide │ │ │ │ - * a base without provide an element. │ │ │ │ + * Property: layer │ │ │ │ + * {<OpenLayers.Layer>} │ │ │ │ */ │ │ │ │ - anchor: false, │ │ │ │ + layer: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: base │ │ │ │ + /** │ │ │ │ + * APIProperty: wfsns │ │ │ │ * {String} │ │ │ │ */ │ │ │ │ - base: '', │ │ │ │ + wfsns: "http://www.opengis.net/wfs", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: displayProjection │ │ │ │ - * {<OpenLayers.Projection>} Requires proj4js support. Projection used │ │ │ │ - * when creating the coordinates in the link. This will reproject the │ │ │ │ - * map coordinates into display coordinates. If you are using this │ │ │ │ - * functionality, the permalink which is last added to the map will │ │ │ │ - * determine the coordinate type which is read from the URL, which │ │ │ │ - * means you should not add permalinks with different │ │ │ │ - * displayProjections to the same map. │ │ │ │ + /** │ │ │ │ + * Property: ogcns │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - displayProjection: null, │ │ │ │ + ogcns: "http://www.opengis.net/ogc", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.Permalink │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Format.WFS │ │ │ │ + * Create a WFS-T formatter. This requires a layer: that layer should │ │ │ │ + * have two properties: geometry_column and typename. The parser │ │ │ │ + * for this format is subclassed entirely from GML: There is a writer │ │ │ │ + * only, which uses most of the code from the GML layer, and wraps │ │ │ │ + * it in transactional elements. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * element - {DOMElement} │ │ │ │ - * base - {String} │ │ │ │ - * options - {Object} options to the control. │ │ │ │ - * │ │ │ │ - * Or for anchor: │ │ │ │ - * options - {Object} options to the control. │ │ │ │ + * options - {Object} │ │ │ │ + * layer - {<OpenLayers.Layer>} │ │ │ │ */ │ │ │ │ - initialize: function(element, base, options) { │ │ │ │ - if (element !== null && typeof element == 'object' && !OpenLayers.Util.isElement(element)) { │ │ │ │ - options = element; │ │ │ │ - this.base = document.location.href; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - if (this.element != null) { │ │ │ │ - this.element = OpenLayers.Util.getElement(this.element); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.element = OpenLayers.Util.getElement(element); │ │ │ │ - this.base = base || document.location.href; │ │ │ │ + initialize: function(options, layer) { │ │ │ │ + OpenLayers.Format.GML.prototype.initialize.apply(this, [options]); │ │ │ │ + this.layer = layer; │ │ │ │ + if (this.layer.featureNS) { │ │ │ │ + this.featureNS = this.layer.featureNS; │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.element && this.element.parentNode == this.div) { │ │ │ │ - this.div.removeChild(this.element); │ │ │ │ - this.element = null; │ │ │ │ + if (this.layer.options.geometry_column) { │ │ │ │ + this.geometryName = this.layer.options.geometry_column; │ │ │ │ } │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.unregister('moveend', this, this.updateLink); │ │ │ │ + if (this.layer.options.typename) { │ │ │ │ + this.featureName = this.layer.options.typename; │ │ │ │ } │ │ │ │ - │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the control. │ │ │ │ - * │ │ │ │ + * Method: write │ │ │ │ + * Takes a feature list, and generates a WFS-T Transaction │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - │ │ │ │ - //make sure we have an arg parser attached │ │ │ │ - for (var i = 0, len = this.map.controls.length; i < len; i++) { │ │ │ │ - var control = this.map.controls[i]; │ │ │ │ - if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) { │ │ │ │ - │ │ │ │ - // If a permalink is added to the map, and an ArgParser already │ │ │ │ - // exists, we override the displayProjection to be the one │ │ │ │ - // on the permalink. │ │ │ │ - if (control.displayProjection != this.displayProjection) { │ │ │ │ - this.displayProjection = control.displayProjection; │ │ │ │ - } │ │ │ │ + write: function(features) { │ │ │ │ │ │ │ │ - break; │ │ │ │ + var transaction = this.createElementNS(this.wfsns, 'wfs:Transaction'); │ │ │ │ + transaction.setAttribute("version", "1.0.0"); │ │ │ │ + transaction.setAttribute("service", "WFS"); │ │ │ │ + for (var i = 0; i < features.length; i++) { │ │ │ │ + switch (features[i].state) { │ │ │ │ + case OpenLayers.State.INSERT: │ │ │ │ + transaction.appendChild(this.insert(features[i])); │ │ │ │ + break; │ │ │ │ + case OpenLayers.State.UPDATE: │ │ │ │ + transaction.appendChild(this.update(features[i])); │ │ │ │ + break; │ │ │ │ + case OpenLayers.State.DELETE: │ │ │ │ + transaction.appendChild(this.remove(features[i])); │ │ │ │ + break; │ │ │ │ } │ │ │ │ } │ │ │ │ - if (i == this.map.controls.length) { │ │ │ │ - this.map.addControl(new this.argParserClass({ │ │ │ │ - 'displayProjection': this.displayProjection │ │ │ │ - })); │ │ │ │ - } │ │ │ │ │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [transaction]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ + * Method: createFeatureXML │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - │ │ │ │ - if (!this.element && !this.anchor) { │ │ │ │ - this.element = document.createElement("a"); │ │ │ │ - this.element.innerHTML = OpenLayers.i18n("Permalink"); │ │ │ │ - this.element.href = ""; │ │ │ │ - this.div.appendChild(this.element); │ │ │ │ + createFeatureXML: function(feature) { │ │ │ │ + var geometryNode = this.buildGeometryNode(feature.geometry); │ │ │ │ + var geomContainer = this.createElementNS(this.featureNS, "feature:" + this.geometryName); │ │ │ │ + geomContainer.appendChild(geometryNode); │ │ │ │ + var featureContainer = this.createElementNS(this.featureNS, "feature:" + this.featureName); │ │ │ │ + featureContainer.appendChild(geomContainer); │ │ │ │ + for (var attr in feature.attributes) { │ │ │ │ + var attrText = this.createTextNode(feature.attributes[attr]); │ │ │ │ + var nodename = attr; │ │ │ │ + if (attr.search(":") != -1) { │ │ │ │ + nodename = attr.split(":")[1]; │ │ │ │ + } │ │ │ │ + var attrContainer = this.createElementNS(this.featureNS, "feature:" + nodename); │ │ │ │ + attrContainer.appendChild(attrText); │ │ │ │ + featureContainer.appendChild(attrContainer); │ │ │ │ } │ │ │ │ - this.map.events.on({ │ │ │ │ - 'moveend': this.updateLink, │ │ │ │ - 'changelayer': this.updateLink, │ │ │ │ - 'changebaselayer': this.updateLink, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - │ │ │ │ - // Make it so there is at least a link even though the map may not have │ │ │ │ - // moved yet. │ │ │ │ - this.updateLink(); │ │ │ │ - │ │ │ │ - return this.div; │ │ │ │ + return featureContainer; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: updateLink │ │ │ │ + * Method: insert │ │ │ │ + * Takes a feature, and generates a WFS-T Transaction "Insert" │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - updateLink: function() { │ │ │ │ - var separator = this.anchor ? '#' : '?'; │ │ │ │ - var href = this.base; │ │ │ │ - var anchor = null; │ │ │ │ - if (href.indexOf("#") != -1 && this.anchor == false) { │ │ │ │ - anchor = href.substring(href.indexOf("#"), href.length); │ │ │ │ - } │ │ │ │ - if (href.indexOf(separator) != -1) { │ │ │ │ - href = href.substring(0, href.indexOf(separator)); │ │ │ │ - } │ │ │ │ - var splits = href.split("#"); │ │ │ │ - href = splits[0] + separator + OpenLayers.Util.getParameterString(this.createParams()); │ │ │ │ - if (anchor) { │ │ │ │ - href += anchor; │ │ │ │ - } │ │ │ │ - if (this.anchor && !this.element) { │ │ │ │ - window.location.href = href; │ │ │ │ - } else { │ │ │ │ - this.element.href = href; │ │ │ │ - } │ │ │ │ + insert: function(feature) { │ │ │ │ + var insertNode = this.createElementNS(this.wfsns, 'wfs:Insert'); │ │ │ │ + insertNode.appendChild(this.createFeatureXML(feature)); │ │ │ │ + return insertNode; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: createParams │ │ │ │ - * Creates the parameters that need to be encoded into the permalink url. │ │ │ │ - * │ │ │ │ + * Method: update │ │ │ │ + * Takes a feature, and generates a WFS-T Transaction "Update" │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * center - {<OpenLayers.LonLat>} center to encode in the permalink. │ │ │ │ - * Defaults to the current map center. │ │ │ │ - * zoom - {Integer} zoom level to encode in the permalink. Defaults to the │ │ │ │ - * current map zoom level. │ │ │ │ - * layers - {Array(<OpenLayers.Layer>)} layers to encode in the permalink. │ │ │ │ - * Defaults to the current map layers. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Hash of parameters that will be url-encoded into the │ │ │ │ - * permalink. │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - createParams: function(center, zoom, layers) { │ │ │ │ - center = center || this.map.getCenter(); │ │ │ │ + update: function(feature) { │ │ │ │ + if (!feature.fid) { │ │ │ │ + OpenLayers.Console.userError(OpenLayers.i18n("noFID")); │ │ │ │ + } │ │ │ │ + var updateNode = this.createElementNS(this.wfsns, 'wfs:Update'); │ │ │ │ + updateNode.setAttribute("typeName", this.featurePrefix + ':' + this.featureName); │ │ │ │ + updateNode.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); │ │ │ │ │ │ │ │ - var params = OpenLayers.Util.getParameters(this.base); │ │ │ │ + var propertyNode = this.createElementNS(this.wfsns, 'wfs:Property'); │ │ │ │ + var nameNode = this.createElementNS(this.wfsns, 'wfs:Name'); │ │ │ │ │ │ │ │ - // If there's still no center, map is not initialized yet. │ │ │ │ - // Break out of this function, and simply return the params from the │ │ │ │ - // base link. │ │ │ │ - if (center) { │ │ │ │ + var txtNode = this.createTextNode(this.geometryName); │ │ │ │ + nameNode.appendChild(txtNode); │ │ │ │ + propertyNode.appendChild(nameNode); │ │ │ │ │ │ │ │ - //zoom │ │ │ │ - params.zoom = zoom || this.map.getZoom(); │ │ │ │ + var valueNode = this.createElementNS(this.wfsns, 'wfs:Value'); │ │ │ │ │ │ │ │ - //lon,lat │ │ │ │ - var lat = center.lat; │ │ │ │ - var lon = center.lon; │ │ │ │ + var geometryNode = this.buildGeometryNode(feature.geometry); │ │ │ │ │ │ │ │ - if (this.displayProjection) { │ │ │ │ - var mapPosition = OpenLayers.Projection.transform({ │ │ │ │ - x: lon, │ │ │ │ - y: lat │ │ │ │ - }, │ │ │ │ - this.map.getProjectionObject(), │ │ │ │ - this.displayProjection); │ │ │ │ - lon = mapPosition.x; │ │ │ │ - lat = mapPosition.y; │ │ │ │ - } │ │ │ │ - params.lat = Math.round(lat * 100000) / 100000; │ │ │ │ - params.lon = Math.round(lon * 100000) / 100000; │ │ │ │ + if (feature.layer) { │ │ │ │ + geometryNode.setAttribute( │ │ │ │ + "srsName", feature.layer.projection.getCode() │ │ │ │ + ); │ │ │ │ + } │ │ │ │ │ │ │ │ - //layers │ │ │ │ - layers = layers || this.map.layers; │ │ │ │ - params.layers = ''; │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - var layer = layers[i]; │ │ │ │ + valueNode.appendChild(geometryNode); │ │ │ │ │ │ │ │ - if (layer.isBaseLayer) { │ │ │ │ - params.layers += (layer == this.map.baseLayer) ? "B" : "0"; │ │ │ │ - } else { │ │ │ │ - params.layers += (layer.getVisibility()) ? "T" : "F"; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + propertyNode.appendChild(valueNode); │ │ │ │ + updateNode.appendChild(propertyNode); │ │ │ │ + │ │ │ │ + // add in attributes │ │ │ │ + for (var propName in feature.attributes) { │ │ │ │ + propertyNode = this.createElementNS(this.wfsns, 'wfs:Property'); │ │ │ │ + nameNode = this.createElementNS(this.wfsns, 'wfs:Name'); │ │ │ │ + nameNode.appendChild(this.createTextNode(propName)); │ │ │ │ + propertyNode.appendChild(nameNode); │ │ │ │ + valueNode = this.createElementNS(this.wfsns, 'wfs:Value'); │ │ │ │ + valueNode.appendChild(this.createTextNode(feature.attributes[propName])); │ │ │ │ + propertyNode.appendChild(valueNode); │ │ │ │ + updateNode.appendChild(propertyNode); │ │ │ │ } │ │ │ │ │ │ │ │ - return params; │ │ │ │ + │ │ │ │ + var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter'); │ │ │ │ + var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId'); │ │ │ │ + filterIdNode.setAttribute("fid", feature.fid); │ │ │ │ + filterNode.appendChild(filterIdNode); │ │ │ │ + updateNode.appendChild(filterNode); │ │ │ │ + │ │ │ │ + return updateNode; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Permalink" │ │ │ │ + /** │ │ │ │ + * Method: remove │ │ │ │ + * Takes a feature, and generates a WFS-T Transaction "Delete" │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + */ │ │ │ │ + remove: function(feature) { │ │ │ │ + if (!feature.fid) { │ │ │ │ + OpenLayers.Console.userError(OpenLayers.i18n("noFID")); │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + var deleteNode = this.createElementNS(this.wfsns, 'wfs:Delete'); │ │ │ │ + deleteNode.setAttribute("typeName", this.featurePrefix + ':' + this.featureName); │ │ │ │ + deleteNode.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); │ │ │ │ + │ │ │ │ + var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter'); │ │ │ │ + var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId'); │ │ │ │ + filterIdNode.setAttribute("fid", feature.fid); │ │ │ │ + filterNode.appendChild(filterIdNode); │ │ │ │ + deleteNode.appendChild(filterNode); │ │ │ │ + │ │ │ │ + return deleteNode; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Remove ciruclar ref to layer │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.layer = null; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFS" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/Measure.js │ │ │ │ + OpenLayers/Format/Atom.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Format/GML/v3.js │ │ │ │ * @requires OpenLayers/Feature/Vector.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.Measure │ │ │ │ - * Allows for drawing of features for measurements. │ │ │ │ + * Class: OpenLayers.Format.Atom │ │ │ │ + * Read/write Atom feeds. Create a new instance with the │ │ │ │ + * <OpenLayers.Format.AtomFeed> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * measure - Triggered when a measurement sketch is complete. Listeners │ │ │ │ - * will receive an event with measure, units, order, and geometry │ │ │ │ - * properties. │ │ │ │ - * measurepartial - Triggered when a new point is added to the │ │ │ │ - * measurement sketch or if the <immediate> property is true and the │ │ │ │ - * measurement sketch is modified. Listeners receive an event with measure, │ │ │ │ - * units, order, and geometry. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: handlerOptions │ │ │ │ - * {Object} Used to set non-default properties on the control's handler │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: callbacks │ │ │ │ - * {Object} The functions that are sent to the handler for callback │ │ │ │ - */ │ │ │ │ - callbacks: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: displaySystem │ │ │ │ - * {String} Display system for output measurements. Supported values │ │ │ │ - * are 'english', 'metric', and 'geographic'. Default is 'metric'. │ │ │ │ - */ │ │ │ │ - displaySystem: 'metric', │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: geodesic │ │ │ │ - * {Boolean} Calculate geodesic metrics instead of planar metrics. This │ │ │ │ - * requires that geometries can be transformed into Geographic/WGS84 │ │ │ │ - * (if that is not already the map projection). Default is false. │ │ │ │ - */ │ │ │ │ - geodesic: false, │ │ │ │ +OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: displaySystemUnits │ │ │ │ - * {Object} Units for various measurement systems. Values are arrays │ │ │ │ - * of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing │ │ │ │ - * order of length. │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. Properties │ │ │ │ + * of this object should not be set individually. Read-only. All │ │ │ │ + * XML subclasses should have their own namespaces object. Use │ │ │ │ + * <setNamespace> to add or set a namespace alias after construction. │ │ │ │ */ │ │ │ │ - displaySystemUnits: { │ │ │ │ - geographic: ['dd'], │ │ │ │ - english: ['mi', 'ft', 'in'], │ │ │ │ - metric: ['km', 'm'] │ │ │ │ + namespaces: { │ │ │ │ + atom: "http://www.w3.org/2005/Atom", │ │ │ │ + georss: "http://www.georss.org/georss" │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: delay │ │ │ │ - * {Number} Number of milliseconds between clicks before the event is │ │ │ │ - * considered a double-click. The "measurepartial" event will not │ │ │ │ - * be triggered if the sketch is completed within this time. This │ │ │ │ - * is required for IE where creating a browser reflow (if a listener │ │ │ │ - * is modifying the DOM by displaying the measurement values) messes │ │ │ │ - * with the dblclick listener in the sketch handler. │ │ │ │ + * APIProperty: feedTitle │ │ │ │ + * {String} Atom feed elements require a title. Default is "untitled". │ │ │ │ */ │ │ │ │ - partialDelay: 300, │ │ │ │ + feedTitle: "untitled", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: delayedTrigger │ │ │ │ - * {Number} Timeout id of trigger for measurepartial. │ │ │ │ + * APIProperty: defaultEntryTitle │ │ │ │ + * {String} Atom entry elements require a title. In cases where one is │ │ │ │ + * not provided in the feature attributes, this will be used. Default │ │ │ │ + * is "untitled". │ │ │ │ */ │ │ │ │ - delayedTrigger: null, │ │ │ │ + defaultEntryTitle: "untitled", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: persist │ │ │ │ - * {Boolean} Keep the temporary measurement sketch drawn after the │ │ │ │ - * measurement is complete. The geometry will persist until a new │ │ │ │ - * measurement is started, the control is deactivated, or <cancel> is │ │ │ │ - * called. │ │ │ │ + * Property: gmlParse │ │ │ │ + * {Object} GML Format object for parsing features │ │ │ │ + * Non-API and only created if necessary │ │ │ │ */ │ │ │ │ - persist: false, │ │ │ │ + gmlParser: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: immediate │ │ │ │ - * {Boolean} Activates the immediate measurement so that the "measurepartial" │ │ │ │ - * event is also fired once the measurement sketch is modified. │ │ │ │ - * Default is false. │ │ │ │ + * APIProperty: xy │ │ │ │ + * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x) │ │ │ │ + * For GeoRSS the default is (y,x), therefore: false │ │ │ │ */ │ │ │ │ - immediate: false, │ │ │ │ + xy: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.Measure │ │ │ │ + * Constructor: OpenLayers.Format.AtomEntry │ │ │ │ + * Create a new parser for Atom. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * handler - {<OpenLayers.Handler>} │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ - initialize: function(handler, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - var callbacks = { │ │ │ │ - done: this.measureComplete, │ │ │ │ - point: this.measurePartial │ │ │ │ - }; │ │ │ │ - if (this.immediate) { │ │ │ │ - callbacks.modify = this.measureImmediate; │ │ │ │ - } │ │ │ │ - this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); │ │ │ │ - │ │ │ │ - // let the handler options override, so old code that passes 'persist' │ │ │ │ - // directly to the handler does not need an update │ │ │ │ - this.handlerOptions = OpenLayers.Util.extend({ │ │ │ │ - persist: this.persist │ │ │ │ - }, this.handlerOptions); │ │ │ │ - this.handler = new handler(this, this.callbacks, this.handlerOptions); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - this.cancelDelay(); │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: cancel │ │ │ │ - * Stop the control from measuring. If <persist> is true, the temporary │ │ │ │ - * sketch will be erased. │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - cancel: function() { │ │ │ │ - this.cancelDelay(); │ │ │ │ - this.handler.cancel(); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setImmediate │ │ │ │ - * Sets the <immediate> property. Changes the activity of immediate │ │ │ │ - * measurement. │ │ │ │ + * APIMethod: read │ │ │ │ + * Return a list of features from an Atom feed or entry document. │ │ │ │ + │ │ │ │ + * Parameters: │ │ │ │ + * doc - {Element} or {String} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * Array({<OpenLayers.Feature.Vector>}) │ │ │ │ */ │ │ │ │ - setImmediate: function(immediate) { │ │ │ │ - this.immediate = immediate; │ │ │ │ - if (this.immediate) { │ │ │ │ - this.callbacks.modify = this.measureImmediate; │ │ │ │ - } else { │ │ │ │ - delete this.callbacks.modify; │ │ │ │ + read: function(doc) { │ │ │ │ + if (typeof doc == "string") { │ │ │ │ + doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); │ │ │ │ } │ │ │ │ + return this.parseFeatures(doc); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: updateHandler │ │ │ │ + * APIMethod: write │ │ │ │ + * Serialize or more feature nodes to Atom documents. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * handler - {Function} One of the sketch handler constructors. │ │ │ │ - * options - {Object} Options for the handler. │ │ │ │ + * features - {<OpenLayers.Feature.Vector>} or Array({<OpenLayers.Feature.Vector>}) │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} an Atom entry document if passed one feature node, or a feed │ │ │ │ + * document if passed an array of feature nodes. │ │ │ │ */ │ │ │ │ - updateHandler: function(handler, options) { │ │ │ │ - var active = this.active; │ │ │ │ - if (active) { │ │ │ │ - this.deactivate(); │ │ │ │ - } │ │ │ │ - this.handler = new handler(this, this.callbacks, options); │ │ │ │ - if (active) { │ │ │ │ - this.activate(); │ │ │ │ + write: function(features) { │ │ │ │ + var doc; │ │ │ │ + if (OpenLayers.Util.isArray(features)) { │ │ │ │ + doc = this.createElementNSPlus("atom:feed"); │ │ │ │ + doc.appendChild( │ │ │ │ + this.createElementNSPlus("atom:title", { │ │ │ │ + value: this.feedTitle │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + for (var i = 0, ii = features.length; i < ii; i++) { │ │ │ │ + doc.appendChild(this.buildEntryNode(features[i])); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + doc = this.buildEntryNode(features); │ │ │ │ } │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [doc]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: measureComplete │ │ │ │ - * Called when the measurement sketch is done. │ │ │ │ + * Method: buildContentNode │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - */ │ │ │ │ - measureComplete: function(geometry) { │ │ │ │ - this.cancelDelay(); │ │ │ │ - this.measure(geometry, "measure"); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: measurePartial │ │ │ │ - * Called each time a new point is added to the measurement sketch. │ │ │ │ + * content - {Object} │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} The last point added. │ │ │ │ - * geometry - {<OpenLayers.Geometry>} The sketch geometry. │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} an Atom content node. │ │ │ │ + * │ │ │ │ + * TODO: types other than text. │ │ │ │ */ │ │ │ │ - measurePartial: function(point, geometry) { │ │ │ │ - this.cancelDelay(); │ │ │ │ - geometry = geometry.clone(); │ │ │ │ - // when we're wating for a dblclick, we have to trigger measurepartial │ │ │ │ - // after some delay to deal with reflow issues in IE │ │ │ │ - if (this.handler.freehandMode(this.handler.evt)) { │ │ │ │ - // no dblclick in freehand mode │ │ │ │ - this.measure(geometry, "measurepartial"); │ │ │ │ + buildContentNode: function(content) { │ │ │ │ + var node = this.createElementNSPlus("atom:content", { │ │ │ │ + attributes: { │ │ │ │ + type: content.type || null │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + if (content.src) { │ │ │ │ + node.setAttribute("src", content.src); │ │ │ │ } else { │ │ │ │ - this.delayedTrigger = window.setTimeout( │ │ │ │ - OpenLayers.Function.bind(function() { │ │ │ │ - this.delayedTrigger = null; │ │ │ │ - this.measure(geometry, "measurepartial"); │ │ │ │ - }, this), │ │ │ │ - this.partialDelay │ │ │ │ - ); │ │ │ │ + if (content.type == "text" || content.type == null) { │ │ │ │ + node.appendChild( │ │ │ │ + this.createTextNode(content.value) │ │ │ │ + ); │ │ │ │ + } else if (content.type == "html") { │ │ │ │ + if (typeof content.value != "string") { │ │ │ │ + throw "HTML content must be in form of an escaped string"; │ │ │ │ + } │ │ │ │ + node.appendChild( │ │ │ │ + this.createTextNode(content.value) │ │ │ │ + ); │ │ │ │ + } else if (content.type == "xhtml") { │ │ │ │ + node.appendChild(content.value); │ │ │ │ + } else if (content.type == "xhtml" || │ │ │ │ + content.type.match(/(\+|\/)xml$/)) { │ │ │ │ + node.appendChild(content.value); │ │ │ │ + } else { // MUST be a valid Base64 encoding │ │ │ │ + node.appendChild( │ │ │ │ + this.createTextNode(content.value) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ } │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: measureImmediate │ │ │ │ - * Called each time the measurement sketch is modified. │ │ │ │ + * Method: buildEntryNode │ │ │ │ + * Build an Atom entry node from a feature object. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} The point at the mouse position. │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The sketch feature. │ │ │ │ - * drawing - {Boolean} Indicates whether we're currently drawing. │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} an Atom entry node. │ │ │ │ + * │ │ │ │ + * These entries are geared for publication using AtomPub. │ │ │ │ + * │ │ │ │ + * TODO: support extension elements │ │ │ │ */ │ │ │ │ - measureImmediate: function(point, feature, drawing) { │ │ │ │ - if (drawing && !this.handler.freehandMode(this.handler.evt)) { │ │ │ │ - this.cancelDelay(); │ │ │ │ - this.measure(feature.geometry, "measurepartial"); │ │ │ │ + buildEntryNode: function(feature) { │ │ │ │ + var attrib = feature.attributes; │ │ │ │ + var atomAttrib = attrib.atom || {}; │ │ │ │ + var entryNode = this.createElementNSPlus("atom:entry"); │ │ │ │ + │ │ │ │ + // atom:author │ │ │ │ + if (atomAttrib.authors) { │ │ │ │ + var authors = OpenLayers.Util.isArray(atomAttrib.authors) ? │ │ │ │ + atomAttrib.authors : [atomAttrib.authors]; │ │ │ │ + for (var i = 0, ii = authors.length; i < ii; i++) { │ │ │ │ + entryNode.appendChild( │ │ │ │ + this.buildPersonConstructNode( │ │ │ │ + "author", authors[i] │ │ │ │ + ) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: cancelDelay │ │ │ │ - * Cancels the delay measurement that measurePartial began. │ │ │ │ - */ │ │ │ │ - cancelDelay: function() { │ │ │ │ - if (this.delayedTrigger !== null) { │ │ │ │ - window.clearTimeout(this.delayedTrigger); │ │ │ │ - this.delayedTrigger = null; │ │ │ │ + // atom:category │ │ │ │ + if (atomAttrib.categories) { │ │ │ │ + var categories = OpenLayers.Util.isArray(atomAttrib.categories) ? │ │ │ │ + atomAttrib.categories : [atomAttrib.categories]; │ │ │ │ + var category; │ │ │ │ + for (var i = 0, ii = categories.length; i < ii; i++) { │ │ │ │ + category = categories[i]; │ │ │ │ + entryNode.appendChild( │ │ │ │ + this.createElementNSPlus("atom:category", { │ │ │ │ + attributes: { │ │ │ │ + term: category.term, │ │ │ │ + scheme: category.scheme || null, │ │ │ │ + label: category.label || null │ │ │ │ + } │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // atom:content │ │ │ │ + if (atomAttrib.content) { │ │ │ │ + entryNode.appendChild(this.buildContentNode(atomAttrib.content)); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // atom:contributor │ │ │ │ + if (atomAttrib.contributors) { │ │ │ │ + var contributors = OpenLayers.Util.isArray(atomAttrib.contributors) ? │ │ │ │ + atomAttrib.contributors : [atomAttrib.contributors]; │ │ │ │ + for (var i = 0, ii = contributors.length; i < ii; i++) { │ │ │ │ + entryNode.appendChild( │ │ │ │ + this.buildPersonConstructNode( │ │ │ │ + "contributor", │ │ │ │ + contributors[i] │ │ │ │ + ) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // atom:id │ │ │ │ + if (feature.fid) { │ │ │ │ + entryNode.appendChild( │ │ │ │ + this.createElementNSPlus("atom:id", { │ │ │ │ + value: feature.fid │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // atom:link │ │ │ │ + if (atomAttrib.links) { │ │ │ │ + var links = OpenLayers.Util.isArray(atomAttrib.links) ? │ │ │ │ + atomAttrib.links : [atomAttrib.links]; │ │ │ │ + var link; │ │ │ │ + for (var i = 0, ii = links.length; i < ii; i++) { │ │ │ │ + link = links[i]; │ │ │ │ + entryNode.appendChild( │ │ │ │ + this.createElementNSPlus("atom:link", { │ │ │ │ + attributes: { │ │ │ │ + href: link.href, │ │ │ │ + rel: link.rel || null, │ │ │ │ + type: link.type || null, │ │ │ │ + hreflang: link.hreflang || null, │ │ │ │ + title: link.title || null, │ │ │ │ + length: link.length || null │ │ │ │ + } │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // atom:published │ │ │ │ + if (atomAttrib.published) { │ │ │ │ + entryNode.appendChild( │ │ │ │ + this.createElementNSPlus("atom:published", { │ │ │ │ + value: atomAttrib.published │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // atom:rights │ │ │ │ + if (atomAttrib.rights) { │ │ │ │ + entryNode.appendChild( │ │ │ │ + this.createElementNSPlus("atom:rights", { │ │ │ │ + value: atomAttrib.rights │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // atom:source not implemented │ │ │ │ + │ │ │ │ + // atom:summary │ │ │ │ + if (atomAttrib.summary || attrib.description) { │ │ │ │ + entryNode.appendChild( │ │ │ │ + this.createElementNSPlus("atom:summary", { │ │ │ │ + value: atomAttrib.summary || attrib.description │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // atom:title │ │ │ │ + entryNode.appendChild( │ │ │ │ + this.createElementNSPlus("atom:title", { │ │ │ │ + value: atomAttrib.title || attrib.title || this.defaultEntryTitle │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + │ │ │ │ + // atom:updated │ │ │ │ + if (atomAttrib.updated) { │ │ │ │ + entryNode.appendChild( │ │ │ │ + this.createElementNSPlus("atom:updated", { │ │ │ │ + value: atomAttrib.updated │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // georss:where │ │ │ │ + if (feature.geometry) { │ │ │ │ + var whereNode = this.createElementNSPlus("georss:where"); │ │ │ │ + whereNode.appendChild( │ │ │ │ + this.buildGeometryNode(feature.geometry) │ │ │ │ + ); │ │ │ │ + entryNode.appendChild(whereNode); │ │ │ │ } │ │ │ │ + │ │ │ │ + return entryNode; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: measure │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * eventType - {String} │ │ │ │ + * Method: initGmlParser │ │ │ │ + * Creates a GML parser. │ │ │ │ */ │ │ │ │ - measure: function(geometry, eventType) { │ │ │ │ - var stat, order; │ │ │ │ - if (geometry.CLASS_NAME.indexOf('LineString') > -1) { │ │ │ │ - stat = this.getBestLength(geometry); │ │ │ │ - order = 1; │ │ │ │ - } else { │ │ │ │ - stat = this.getBestArea(geometry); │ │ │ │ - order = 2; │ │ │ │ - } │ │ │ │ - this.events.triggerEvent(eventType, { │ │ │ │ - measure: stat[0], │ │ │ │ - units: stat[1], │ │ │ │ - order: order, │ │ │ │ - geometry: geometry │ │ │ │ + initGmlParser: function() { │ │ │ │ + this.gmlParser = new OpenLayers.Format.GML.v3({ │ │ │ │ + xy: this.xy, │ │ │ │ + featureNS: "http://example.com#feature", │ │ │ │ + internalProjection: this.internalProjection, │ │ │ │ + externalProjection: this.externalProjection │ │ │ │ }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getBestArea │ │ │ │ - * Based on the <displaySystem> returns the area of a geometry. │ │ │ │ + * Method: buildGeometryNode │ │ │ │ + * builds a GeoRSS node with a given geometry │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ * geometry - {<OpenLayers.Geometry>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array([Float, String])} Returns a two item array containing the │ │ │ │ - * area and the units abbreviation. │ │ │ │ + * {DOMElement} A gml node. │ │ │ │ */ │ │ │ │ - getBestArea: function(geometry) { │ │ │ │ - var units = this.displaySystemUnits[this.displaySystem]; │ │ │ │ - var unit, area; │ │ │ │ - for (var i = 0, len = units.length; i < len; ++i) { │ │ │ │ - unit = units[i]; │ │ │ │ - area = this.getArea(geometry, unit); │ │ │ │ - if (area > 1) { │ │ │ │ - break; │ │ │ │ - } │ │ │ │ + buildGeometryNode: function(geometry) { │ │ │ │ + if (!this.gmlParser) { │ │ │ │ + this.initGmlParser(); │ │ │ │ } │ │ │ │ - return [area, unit]; │ │ │ │ + var node = this.gmlParser.writeNode("feature:_geometry", geometry); │ │ │ │ + return node.firstChild; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getArea │ │ │ │ + * Method: buildPersonConstructNode │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * units - {String} Unit abbreviation │ │ │ │ + * name - {String} │ │ │ │ + * value - {Object} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Float} The geometry area in the given units. │ │ │ │ + * {DOMElement} an Atom person construct node. │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * >>> buildPersonConstructNode("author", {name: "John Smith"}) │ │ │ │ + * {<author><name>John Smith</name></author>} │ │ │ │ + * │ │ │ │ + * TODO: how to specify extension elements? Add to the oNames array? │ │ │ │ */ │ │ │ │ - getArea: function(geometry, units) { │ │ │ │ - var area, geomUnits; │ │ │ │ - if (this.geodesic) { │ │ │ │ - area = geometry.getGeodesicArea(this.map.getProjectionObject()); │ │ │ │ - geomUnits = "m"; │ │ │ │ - } else { │ │ │ │ - area = geometry.getArea(); │ │ │ │ - geomUnits = this.map.getUnits(); │ │ │ │ - } │ │ │ │ - var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units]; │ │ │ │ - if (inPerDisplayUnit) { │ │ │ │ - var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits]; │ │ │ │ - area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2); │ │ │ │ + buildPersonConstructNode: function(name, value) { │ │ │ │ + var oNames = ["uri", "email"]; │ │ │ │ + var personNode = this.createElementNSPlus("atom:" + name); │ │ │ │ + personNode.appendChild( │ │ │ │ + this.createElementNSPlus("atom:name", { │ │ │ │ + value: value.name │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + for (var i = 0, ii = oNames.length; i < ii; i++) { │ │ │ │ + if (value[oNames[i]]) { │ │ │ │ + personNode.appendChild( │ │ │ │ + this.createElementNSPlus("atom:" + oNames[i], { │ │ │ │ + value: value[oNames[i]] │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ } │ │ │ │ - return area; │ │ │ │ + return personNode; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getBestLength │ │ │ │ - * Based on the <displaySystem> returns the length of a geometry. │ │ │ │ + * Method: getFirstChildValue │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * node - {DOMElement} │ │ │ │ + * nsuri - {String} Child node namespace uri ("*" for any). │ │ │ │ + * name - {String} Child node name. │ │ │ │ + * def - {String} Optional string default to return if no child found. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array([Float, String])} Returns a two item array containing the │ │ │ │ - * length and the units abbreviation. │ │ │ │ + * {String} The value of the first child with the given tag name. Returns │ │ │ │ + * default value or empty string if none found. │ │ │ │ */ │ │ │ │ - getBestLength: function(geometry) { │ │ │ │ - var units = this.displaySystemUnits[this.displaySystem]; │ │ │ │ - var unit, length; │ │ │ │ - for (var i = 0, len = units.length; i < len; ++i) { │ │ │ │ - unit = units[i]; │ │ │ │ - length = this.getLength(geometry, unit); │ │ │ │ - if (length > 1) { │ │ │ │ - break; │ │ │ │ - } │ │ │ │ + getFirstChildValue: function(node, nsuri, name, def) { │ │ │ │ + var value; │ │ │ │ + var nodes = this.getElementsByTagNameNS(node, nsuri, name); │ │ │ │ + if (nodes && nodes.length > 0) { │ │ │ │ + value = this.getChildValue(nodes[0], def); │ │ │ │ + } else { │ │ │ │ + value = def; │ │ │ │ } │ │ │ │ - return [length, unit]; │ │ │ │ + return value; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getLength │ │ │ │ + * Method: parseFeature │ │ │ │ + * Parse feature from an Atom entry node.. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * units - {String} Unit abbreviation │ │ │ │ + * node - {DOMElement} An Atom entry or feed node. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Float} The geometry length in the given units. │ │ │ │ + * {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - getLength: function(geometry, units) { │ │ │ │ - var length, geomUnits; │ │ │ │ - if (this.geodesic) { │ │ │ │ - length = geometry.getGeodesicLength(this.map.getProjectionObject()); │ │ │ │ - geomUnits = "m"; │ │ │ │ - } else { │ │ │ │ - length = geometry.getLength(); │ │ │ │ - geomUnits = this.map.getUnits(); │ │ │ │ + parseFeature: function(node) { │ │ │ │ + var atomAttrib = {}; │ │ │ │ + var value = null; │ │ │ │ + var nodes = null; │ │ │ │ + var attval = null; │ │ │ │ + var atomns = this.namespaces.atom; │ │ │ │ + │ │ │ │ + // atomAuthor* │ │ │ │ + this.parsePersonConstructs(node, "author", atomAttrib); │ │ │ │ + │ │ │ │ + // atomCategory* │ │ │ │ + nodes = this.getElementsByTagNameNS(node, atomns, "category"); │ │ │ │ + if (nodes.length > 0) { │ │ │ │ + atomAttrib.categories = []; │ │ │ │ } │ │ │ │ - var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units]; │ │ │ │ - if (inPerDisplayUnit) { │ │ │ │ - var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits]; │ │ │ │ - length *= (inPerMapUnit / inPerDisplayUnit); │ │ │ │ + for (var i = 0, ii = nodes.length; i < ii; i++) { │ │ │ │ + value = {}; │ │ │ │ + value.term = nodes[i].getAttribute("term"); │ │ │ │ + attval = nodes[i].getAttribute("scheme"); │ │ │ │ + if (attval) { │ │ │ │ + value.scheme = attval; │ │ │ │ + } │ │ │ │ + attval = nodes[i].getAttribute("label"); │ │ │ │ + if (attval) { │ │ │ │ + value.label = attval; │ │ │ │ + } │ │ │ │ + atomAttrib.categories.push(value); │ │ │ │ } │ │ │ │ - return length; │ │ │ │ - }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Measure" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Handler/Box.js │ │ │ │ - ====================================================================== */ │ │ │ │ + // atomContent? │ │ │ │ + nodes = this.getElementsByTagNameNS(node, atomns, "content"); │ │ │ │ + if (nodes.length > 0) { │ │ │ │ + value = {}; │ │ │ │ + attval = nodes[0].getAttribute("type"); │ │ │ │ + if (attval) { │ │ │ │ + value.type = attval; │ │ │ │ + } │ │ │ │ + attval = nodes[0].getAttribute("src"); │ │ │ │ + if (attval) { │ │ │ │ + value.src = attval; │ │ │ │ + } else { │ │ │ │ + if (value.type == "text" || │ │ │ │ + value.type == "html" || │ │ │ │ + value.type == null) { │ │ │ │ + value.value = this.getFirstChildValue( │ │ │ │ + node, │ │ │ │ + atomns, │ │ │ │ + "content", │ │ │ │ + null │ │ │ │ + ); │ │ │ │ + } else if (value.type == "xhtml" || │ │ │ │ + value.type.match(/(\+|\/)xml$/)) { │ │ │ │ + value.value = this.getChildEl(nodes[0]); │ │ │ │ + } else { // MUST be base64 encoded │ │ │ │ + value.value = this.getFirstChildValue( │ │ │ │ + node, │ │ │ │ + atomns, │ │ │ │ + "content", │ │ │ │ + null │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + atomAttrib.content = value; │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + // atomContributor* │ │ │ │ + this.parsePersonConstructs(node, "contributor", atomAttrib); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Handler.js │ │ │ │ - * @requires OpenLayers/Handler/Drag.js │ │ │ │ - */ │ │ │ │ + // atomId │ │ │ │ + atomAttrib.id = this.getFirstChildValue(node, atomns, "id", null); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Handler.Box │ │ │ │ - * Handler for dragging a rectangle across the map. Box is displayed │ │ │ │ - * on mouse down, moves on mouse move, and is finished on mouse up. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Handler> │ │ │ │ - */ │ │ │ │ -OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + // atomLink* │ │ │ │ + nodes = this.getElementsByTagNameNS(node, atomns, "link"); │ │ │ │ + if (nodes.length > 0) { │ │ │ │ + atomAttrib.links = new Array(nodes.length); │ │ │ │ + } │ │ │ │ + var oAtts = ["rel", "type", "hreflang", "title", "length"]; │ │ │ │ + for (var i = 0, ii = nodes.length; i < ii; i++) { │ │ │ │ + value = {}; │ │ │ │ + value.href = nodes[i].getAttribute("href"); │ │ │ │ + for (var j = 0, jj = oAtts.length; j < jj; j++) { │ │ │ │ + attval = nodes[i].getAttribute(oAtts[j]); │ │ │ │ + if (attval) { │ │ │ │ + value[oAtts[j]] = attval; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + atomAttrib.links[i] = value; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: dragHandler │ │ │ │ - * {<OpenLayers.Handler.Drag>} │ │ │ │ - */ │ │ │ │ - dragHandler: null, │ │ │ │ + // atomPublished? │ │ │ │ + value = this.getFirstChildValue(node, atomns, "published", null); │ │ │ │ + if (value) { │ │ │ │ + atomAttrib.published = value; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: boxDivClassName │ │ │ │ - * {String} The CSS class to use for drawing the box. Default is │ │ │ │ - * olHandlerBoxZoomBox │ │ │ │ - */ │ │ │ │ - boxDivClassName: 'olHandlerBoxZoomBox', │ │ │ │ + // atomRights? │ │ │ │ + value = this.getFirstChildValue(node, atomns, "rights", null); │ │ │ │ + if (value) { │ │ │ │ + atomAttrib.rights = value; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: boxOffsets │ │ │ │ - * {Object} Caches box offsets from css. This is used by the getBoxOffsets │ │ │ │ - * method. │ │ │ │ - */ │ │ │ │ - boxOffsets: null, │ │ │ │ + // atomSource? -- not implemented │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Handler.Box │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} │ │ │ │ - * callbacks - {Object} An object with a properties whose values are │ │ │ │ - * functions. Various callbacks described below. │ │ │ │ - * options - {Object} │ │ │ │ - * │ │ │ │ - * Named callbacks: │ │ │ │ - * start - Called when the box drag operation starts. │ │ │ │ - * done - Called when the box drag operation is finished. │ │ │ │ - * The callback should expect to receive a single argument, the box │ │ │ │ - * bounds or a pixel. If the box dragging didn't span more than a 5 │ │ │ │ - * pixel distance, a pixel will be returned instead of a bounds object. │ │ │ │ - */ │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - this.dragHandler = new OpenLayers.Handler.Drag( │ │ │ │ - this, { │ │ │ │ - down: this.startBox, │ │ │ │ - move: this.moveBox, │ │ │ │ - out: this.removeBox, │ │ │ │ - up: this.endBox │ │ │ │ - }, { │ │ │ │ - keyMask: this.keyMask │ │ │ │ - } │ │ │ │ + // atomSummary? │ │ │ │ + value = this.getFirstChildValue(node, atomns, "summary", null); │ │ │ │ + if (value) { │ │ │ │ + atomAttrib.summary = value; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // atomTitle │ │ │ │ + atomAttrib.title = this.getFirstChildValue( │ │ │ │ + node, atomns, "title", null │ │ │ │ ); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ - if (this.dragHandler) { │ │ │ │ - this.dragHandler.destroy(); │ │ │ │ - this.dragHandler = null; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + // atomUpdated │ │ │ │ + atomAttrib.updated = this.getFirstChildValue( │ │ │ │ + node, atomns, "updated", null │ │ │ │ + ); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Handler.prototype.setMap.apply(this, arguments); │ │ │ │ - if (this.dragHandler) { │ │ │ │ - this.dragHandler.setMap(map); │ │ │ │ - } │ │ │ │ + var featureAttrib = { │ │ │ │ + title: atomAttrib.title, │ │ │ │ + description: atomAttrib.summary, │ │ │ │ + atom: atomAttrib │ │ │ │ + }; │ │ │ │ + var geometry = this.parseLocations(node)[0]; │ │ │ │ + var feature = new OpenLayers.Feature.Vector(geometry, featureAttrib); │ │ │ │ + feature.fid = atomAttrib.id; │ │ │ │ + return feature; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: startBox │ │ │ │ + * Method: parseFeatures │ │ │ │ + * Return features from an Atom entry or feed. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * xy - {<OpenLayers.Pixel>} │ │ │ │ + * node - {DOMElement} An Atom entry or feed node. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * Array({<OpenLayers.Feature.Vector>}) │ │ │ │ */ │ │ │ │ - startBox: function(xy) { │ │ │ │ - this.callback("start", []); │ │ │ │ - this.zoomBox = OpenLayers.Util.createDiv('zoomBox', { │ │ │ │ - x: -9999, │ │ │ │ - y: -9999 │ │ │ │ - }); │ │ │ │ - this.zoomBox.className = this.boxDivClassName; │ │ │ │ - this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1; │ │ │ │ - │ │ │ │ - this.map.viewPortDiv.appendChild(this.zoomBox); │ │ │ │ - │ │ │ │ - OpenLayers.Element.addClass( │ │ │ │ - this.map.viewPortDiv, "olDrawBox" │ │ │ │ + parseFeatures: function(node) { │ │ │ │ + var features = []; │ │ │ │ + var entries = this.getElementsByTagNameNS( │ │ │ │ + node, this.namespaces.atom, "entry" │ │ │ │ ); │ │ │ │ + if (entries.length == 0) { │ │ │ │ + entries = [node]; │ │ │ │ + } │ │ │ │ + for (var i = 0, ii = entries.length; i < ii; i++) { │ │ │ │ + features.push(this.parseFeature(entries[i])); │ │ │ │ + } │ │ │ │ + return features; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveBox │ │ │ │ + * Method: parseLocations │ │ │ │ + * Parse the locations from an Atom entry or feed. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} An Atom entry or feed node. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * Array({<OpenLayers.Geometry>}) │ │ │ │ */ │ │ │ │ - moveBox: function(xy) { │ │ │ │ - var startX = this.dragHandler.start.x; │ │ │ │ - var startY = this.dragHandler.start.y; │ │ │ │ - var deltaX = Math.abs(startX - xy.x); │ │ │ │ - var deltaY = Math.abs(startY - xy.y); │ │ │ │ - │ │ │ │ - var offset = this.getBoxOffsets(); │ │ │ │ - this.zoomBox.style.width = (deltaX + offset.width + 1) + "px"; │ │ │ │ - this.zoomBox.style.height = (deltaY + offset.height + 1) + "px"; │ │ │ │ - this.zoomBox.style.left = (xy.x < startX ? │ │ │ │ - startX - deltaX - offset.left : startX - offset.left) + "px"; │ │ │ │ - this.zoomBox.style.top = (xy.y < startY ? │ │ │ │ - startY - deltaY - offset.top : startY - offset.top) + "px"; │ │ │ │ - }, │ │ │ │ + parseLocations: function(node) { │ │ │ │ + var georssns = this.namespaces.georss; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: endBox │ │ │ │ - */ │ │ │ │ - endBox: function(end) { │ │ │ │ - var result; │ │ │ │ - if (Math.abs(this.dragHandler.start.x - end.x) > 5 || │ │ │ │ - Math.abs(this.dragHandler.start.y - end.y) > 5) { │ │ │ │ - var start = this.dragHandler.start; │ │ │ │ - var top = Math.min(start.y, end.y); │ │ │ │ - var bottom = Math.max(start.y, end.y); │ │ │ │ - var left = Math.min(start.x, end.x); │ │ │ │ - var right = Math.max(start.x, end.x); │ │ │ │ - result = new OpenLayers.Bounds(left, bottom, right, top); │ │ │ │ - } else { │ │ │ │ - result = this.dragHandler.start.clone(); // i.e. OL.Pixel │ │ │ │ + var locations = { │ │ │ │ + components: [] │ │ │ │ + }; │ │ │ │ + var where = this.getElementsByTagNameNS(node, georssns, "where"); │ │ │ │ + if (where && where.length > 0) { │ │ │ │ + if (!this.gmlParser) { │ │ │ │ + this.initGmlParser(); │ │ │ │ + } │ │ │ │ + for (var i = 0, ii = where.length; i < ii; i++) { │ │ │ │ + this.gmlParser.readChildNodes(where[i], locations); │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.removeBox(); │ │ │ │ - │ │ │ │ - this.callback("done", [result]); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: removeBox │ │ │ │ - * Remove the zoombox from the screen and nullify our reference to it. │ │ │ │ - */ │ │ │ │ - removeBox: function() { │ │ │ │ - this.map.viewPortDiv.removeChild(this.zoomBox); │ │ │ │ - this.zoomBox = null; │ │ │ │ - this.boxOffsets = null; │ │ │ │ - OpenLayers.Element.removeClass( │ │ │ │ - this.map.viewPortDiv, "olDrawBox" │ │ │ │ - ); │ │ │ │ + var components = locations.components; │ │ │ │ + var point = this.getElementsByTagNameNS(node, georssns, "point"); │ │ │ │ + if (point && point.length > 0) { │ │ │ │ + for (var i = 0, ii = point.length; i < ii; i++) { │ │ │ │ + var xy = OpenLayers.String.trim( │ │ │ │ + point[i].firstChild.nodeValue │ │ │ │ + ).split(/\s+/); │ │ │ │ + if (xy.length != 2) { │ │ │ │ + xy = OpenLayers.String.trim( │ │ │ │ + point[i].firstChild.nodeValue │ │ │ │ + ).split(/\s*,\s*/); │ │ │ │ + } │ │ │ │ + components.push(new OpenLayers.Geometry.Point(xy[1], xy[0])); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - }, │ │ │ │ + var line = this.getElementsByTagNameNS(node, georssns, "line"); │ │ │ │ + if (line && line.length > 0) { │ │ │ │ + var coords; │ │ │ │ + var p; │ │ │ │ + var points; │ │ │ │ + for (var i = 0, ii = line.length; i < ii; i++) { │ │ │ │ + coords = OpenLayers.String.trim( │ │ │ │ + line[i].firstChild.nodeValue │ │ │ │ + ).split(/\s+/); │ │ │ │ + points = []; │ │ │ │ + for (var j = 0, jj = coords.length; j < jj; j += 2) { │ │ │ │ + p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]); │ │ │ │ + points.push(p); │ │ │ │ + } │ │ │ │ + components.push( │ │ │ │ + new OpenLayers.Geometry.LineString(points) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: activate │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.dragHandler.activate(); │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ + var polygon = this.getElementsByTagNameNS(node, georssns, "polygon"); │ │ │ │ + if (polygon && polygon.length > 0) { │ │ │ │ + var coords; │ │ │ │ + var p; │ │ │ │ + var points; │ │ │ │ + for (var i = 0, ii = polygon.length; i < ii; i++) { │ │ │ │ + coords = OpenLayers.String.trim( │ │ │ │ + polygon[i].firstChild.nodeValue │ │ │ │ + ).split(/\s+/); │ │ │ │ + points = []; │ │ │ │ + for (var j = 0, jj = coords.length; j < jj; j += 2) { │ │ │ │ + p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]); │ │ │ │ + points.push(p); │ │ │ │ + } │ │ │ │ + components.push( │ │ │ │ + new OpenLayers.Geometry.Polygon( │ │ │ │ + [new OpenLayers.Geometry.LinearRing(points)] │ │ │ │ + ) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: deactivate │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - if (this.dragHandler.deactivate()) { │ │ │ │ - if (this.zoomBox) { │ │ │ │ - this.removeBox(); │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + for (var i = 0, ii = components.length; i < ii; i++) { │ │ │ │ + if (components[i]) { │ │ │ │ + components[i].transform( │ │ │ │ + this.externalProjection, │ │ │ │ + this.internalProjection │ │ │ │ + ); │ │ │ │ } │ │ │ │ } │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ } │ │ │ │ + │ │ │ │ + return components; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getBoxOffsets │ │ │ │ - * Determines border offsets for a box, according to the box model. │ │ │ │ - * │ │ │ │ + * Method: parsePersonConstruct │ │ │ │ + * Parse Atom person constructs from an Atom entry node. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} An Atom entry or feed node. │ │ │ │ + * name - {String} Construcy name ("author" or "contributor") │ │ │ │ + * data = {Object} Object in which to put parsed persons. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Object} an object with the following offsets: │ │ │ │ - * - left │ │ │ │ - * - right │ │ │ │ - * - top │ │ │ │ - * - bottom │ │ │ │ - * - width │ │ │ │ - * - height │ │ │ │ + * An {Object}. │ │ │ │ */ │ │ │ │ - getBoxOffsets: function() { │ │ │ │ - if (!this.boxOffsets) { │ │ │ │ - // Determine the box model. If the testDiv's clientWidth is 3, then │ │ │ │ - // the borders are outside and we are dealing with the w3c box │ │ │ │ - // model. Otherwise, the browser uses the traditional box model and │ │ │ │ - // the borders are inside the box bounds, leaving us with a │ │ │ │ - // clientWidth of 1. │ │ │ │ - var testDiv = document.createElement("div"); │ │ │ │ - //testDiv.style.visibility = "hidden"; │ │ │ │ - testDiv.style.position = "absolute"; │ │ │ │ - testDiv.style.border = "1px solid black"; │ │ │ │ - testDiv.style.width = "3px"; │ │ │ │ - document.body.appendChild(testDiv); │ │ │ │ - var w3cBoxModel = testDiv.clientWidth == 3; │ │ │ │ - document.body.removeChild(testDiv); │ │ │ │ - │ │ │ │ - var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, │ │ │ │ - "border-left-width")); │ │ │ │ - var right = parseInt(OpenLayers.Element.getStyle( │ │ │ │ - this.zoomBox, "border-right-width")); │ │ │ │ - var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, │ │ │ │ - "border-top-width")); │ │ │ │ - var bottom = parseInt(OpenLayers.Element.getStyle( │ │ │ │ - this.zoomBox, "border-bottom-width")); │ │ │ │ - this.boxOffsets = { │ │ │ │ - left: left, │ │ │ │ - right: right, │ │ │ │ - top: top, │ │ │ │ - bottom: bottom, │ │ │ │ - width: w3cBoxModel === false ? left + right : 0, │ │ │ │ - height: w3cBoxModel === false ? top + bottom : 0 │ │ │ │ - }; │ │ │ │ + parsePersonConstructs: function(node, name, data) { │ │ │ │ + var persons = []; │ │ │ │ + var atomns = this.namespaces.atom; │ │ │ │ + var nodes = this.getElementsByTagNameNS(node, atomns, name); │ │ │ │ + var oAtts = ["uri", "email"]; │ │ │ │ + for (var i = 0, ii = nodes.length; i < ii; i++) { │ │ │ │ + var value = {}; │ │ │ │ + value.name = this.getFirstChildValue( │ │ │ │ + nodes[i], │ │ │ │ + atomns, │ │ │ │ + "name", │ │ │ │ + null │ │ │ │ + ); │ │ │ │ + for (var j = 0, jj = oAtts.length; j < jj; j++) { │ │ │ │ + var attval = this.getFirstChildValue( │ │ │ │ + nodes[i], │ │ │ │ + atomns, │ │ │ │ + oAtts[j], │ │ │ │ + null); │ │ │ │ + if (attval) { │ │ │ │ + value[oAtts[j]] = attval; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + persons.push(value); │ │ │ │ + } │ │ │ │ + if (persons.length > 0) { │ │ │ │ + data[name + "s"] = persons; │ │ │ │ } │ │ │ │ - return this.boxOffsets; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Box" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.Atom" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/ZoomBox.js │ │ │ │ + OpenLayers/Format/WPSCapabilities.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Handler/Box.js │ │ │ │ + * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.ZoomBox │ │ │ │ - * The ZoomBox control enables zooming directly to a given extent, by drawing │ │ │ │ - * a box on the map. The box is drawn by holding down shift, whilst dragging │ │ │ │ - * the mouse. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Format.WPSCapabilities │ │ │ │ + * Read WPS Capabilities. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - /** │ │ │ │ - * Property: type │ │ │ │ - * {OpenLayers.Control.TYPE} │ │ │ │ - */ │ │ │ │ - type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: out │ │ │ │ - * {Boolean} Should the control be used for zooming out? │ │ │ │ - */ │ │ │ │ - out: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: keyMask │ │ │ │ - * {Integer} Zoom only occurs if the keyMask matches the combination of │ │ │ │ - * keys down. Use bitwise operators and one or more of the │ │ │ │ - * <OpenLayers.Handler> constants to construct a keyMask. Leave null if │ │ │ │ - * not used mask. Default is null. │ │ │ │ - */ │ │ │ │ - keyMask: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: alwaysZoom │ │ │ │ - * {Boolean} Always zoom in/out when box drawn, even if the zoom level does │ │ │ │ - * not change. │ │ │ │ - */ │ │ │ │ - alwaysZoom: false, │ │ │ │ +OpenLayers.Format.WPSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: zoomOnClick │ │ │ │ - * {Boolean} Should we zoom when no box was dragged, i.e. the user only │ │ │ │ - * clicked? Default is true. │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ + * {String} Version number to assume if none found. Default is "1.0.0". │ │ │ │ */ │ │ │ │ - zoomOnClick: true, │ │ │ │ + defaultVersion: "1.0.0", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ + * Constructor: OpenLayers.Format.WPSCapabilities │ │ │ │ + * Create a new parser for WPS Capabilities. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - this.handler = new OpenLayers.Handler.Box(this, { │ │ │ │ - done: this.zoomBox │ │ │ │ - }, { │ │ │ │ - keyMask: this.keyMask │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: zoomBox │ │ │ │ + * APIMethod: read │ │ │ │ + * Read capabilities data from a string, and return information about │ │ │ │ + * the service. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>} │ │ │ │ + * Returns: │ │ │ │ + * {Object} Info about the WPS │ │ │ │ */ │ │ │ │ - zoomBox: function(position) { │ │ │ │ - if (position instanceof OpenLayers.Bounds) { │ │ │ │ - var bounds, │ │ │ │ - targetCenterPx = position.getCenterPixel(); │ │ │ │ - if (!this.out) { │ │ │ │ - var minXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.left, │ │ │ │ - y: position.bottom │ │ │ │ - }); │ │ │ │ - var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.right, │ │ │ │ - y: position.top │ │ │ │ - }); │ │ │ │ - bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, │ │ │ │ - maxXY.lon, maxXY.lat); │ │ │ │ - } else { │ │ │ │ - var pixWidth = position.right - position.left; │ │ │ │ - var pixHeight = position.bottom - position.top; │ │ │ │ - var zoomFactor = Math.min((this.map.size.h / pixHeight), │ │ │ │ - (this.map.size.w / pixWidth)); │ │ │ │ - var extent = this.map.getExtent(); │ │ │ │ - var center = this.map.getLonLatFromPixel(targetCenterPx); │ │ │ │ - var xmin = center.lon - (extent.getWidth() / 2) * zoomFactor; │ │ │ │ - var xmax = center.lon + (extent.getWidth() / 2) * zoomFactor; │ │ │ │ - var ymin = center.lat - (extent.getHeight() / 2) * zoomFactor; │ │ │ │ - var ymax = center.lat + (extent.getHeight() / 2) * zoomFactor; │ │ │ │ - bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax); │ │ │ │ - } │ │ │ │ - // always zoom in/out │ │ │ │ - var lastZoom = this.map.getZoom(), │ │ │ │ - size = this.map.getSize(), │ │ │ │ - centerPx = { │ │ │ │ - x: size.w / 2, │ │ │ │ - y: size.h / 2 │ │ │ │ - }, │ │ │ │ - zoom = this.map.getZoomForExtent(bounds), │ │ │ │ - oldRes = this.map.getResolution(), │ │ │ │ - newRes = this.map.getResolutionForZoom(zoom); │ │ │ │ - if (oldRes == newRes) { │ │ │ │ - this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx)); │ │ │ │ - } else { │ │ │ │ - var zoomOriginPx = { │ │ │ │ - x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / │ │ │ │ - (oldRes - newRes), │ │ │ │ - y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / │ │ │ │ - (oldRes - newRes) │ │ │ │ - }; │ │ │ │ - this.map.zoomTo(zoom, zoomOriginPx); │ │ │ │ - } │ │ │ │ - if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) { │ │ │ │ - this.map.zoomTo(lastZoom + (this.out ? -1 : 1)); │ │ │ │ - } │ │ │ │ - } else if (this.zoomOnClick) { // it's a pixel │ │ │ │ - if (!this.out) { │ │ │ │ - this.map.zoomTo(this.map.getZoom() + 1, position); │ │ │ │ - } else { │ │ │ │ - this.map.zoomTo(this.map.getZoom() - 1, position); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ZoomBox" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WPSCapabilities" │ │ │ │ + │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/DragPan.js │ │ │ │ + OpenLayers/Format/Context.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Handler/Drag.js │ │ │ │ + * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.DragPan │ │ │ │ - * The DragPan control pans the map with a drag of the mouse. │ │ │ │ + * Class: OpenLayers.Format.Context │ │ │ │ + * Base class for both Format.WMC and Format.OWSContext │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: type │ │ │ │ - * {OpenLayers.Control.TYPES} │ │ │ │ - */ │ │ │ │ - type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ +OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: panned │ │ │ │ - * {Boolean} The map moved. │ │ │ │ + * Property: layerOptions │ │ │ │ + * {Object} Default options for layers created by the parser. These │ │ │ │ + * options are overridden by the options which are read from the │ │ │ │ + * capabilities document. │ │ │ │ */ │ │ │ │ - panned: false, │ │ │ │ + layerOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: interval │ │ │ │ - * {Integer} The number of milliseconds that should ellapse before │ │ │ │ - * panning the map again. Defaults to 0 milliseconds, which means that │ │ │ │ - * no separate cycle is used for panning. In most cases you won't want │ │ │ │ - * to change this value. For slow machines/devices larger values can be │ │ │ │ - * tried out. │ │ │ │ + * Property: layerParams │ │ │ │ + * {Object} Default parameters for layers created by the parser. This │ │ │ │ + * can be used e.g. to override DEFAULT_PARAMS for │ │ │ │ + * OpenLayers.Layer.WMS. │ │ │ │ */ │ │ │ │ - interval: 0, │ │ │ │ + layerParams: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: documentDrag │ │ │ │ - * {Boolean} If set to true, mouse dragging will continue even if the │ │ │ │ - * mouse cursor leaves the map viewport. Default is false. │ │ │ │ + * Constructor: OpenLayers.Format.Context │ │ │ │ + * Create a new parser for Context documents. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - documentDrag: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: kinetic │ │ │ │ - * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object. │ │ │ │ + * APIMethod: read │ │ │ │ + * Read Context data from a string, and return an object with map │ │ │ │ + * properties and a list of layers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * options - {Object} The options object must contain a map property. If │ │ │ │ + * the map property is a string, it must be the id of a dom element │ │ │ │ + * where the new map will be placed. If the map property is an │ │ │ │ + * <OpenLayers.Map>, the layers from the context document will be added │ │ │ │ + * to the map. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Map>} A map based on the context. │ │ │ │ */ │ │ │ │ - kinetic: null, │ │ │ │ + read: function(data, options) { │ │ │ │ + var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this, │ │ │ │ + arguments); │ │ │ │ + var map; │ │ │ │ + if (options && options.map) { │ │ │ │ + this.context = context; │ │ │ │ + if (options.map instanceof OpenLayers.Map) { │ │ │ │ + map = this.mergeContextToMap(context, options.map); │ │ │ │ + } else { │ │ │ │ + var mapOptions = options.map; │ │ │ │ + if (OpenLayers.Util.isElement(mapOptions) || │ │ │ │ + typeof mapOptions == "string") { │ │ │ │ + // we assume mapOptions references a div │ │ │ │ + // element │ │ │ │ + mapOptions = { │ │ │ │ + div: mapOptions │ │ │ │ + }; │ │ │ │ + } │ │ │ │ + map = this.contextToMap(context, mapOptions); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + // not documented as part of the API, provided as a non-API option │ │ │ │ + map = context; │ │ │ │ + } │ │ │ │ + return map; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: enableKinetic │ │ │ │ - * {Boolean} Set this option to enable "kinetic dragging". Can be │ │ │ │ - * set to true or to an object. If set to an object this │ │ │ │ - * object will be passed to the {<OpenLayers.Kinetic>} │ │ │ │ - * constructor. Defaults to true. │ │ │ │ - * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is │ │ │ │ - * included in your build config. │ │ │ │ + * Method: getLayerFromContext │ │ │ │ + * Create a WMS layer from a layerContext object. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layerContext - {Object} An object representing a WMS layer. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.WMS>} A WMS layer. │ │ │ │ */ │ │ │ │ - enableKinetic: true, │ │ │ │ + getLayerFromContext: function(layerContext) { │ │ │ │ + var i, len; │ │ │ │ + // fill initial options object from layerContext │ │ │ │ + var options = { │ │ │ │ + queryable: layerContext.queryable, //keep queryable for api compatibility │ │ │ │ + visibility: layerContext.visibility, │ │ │ │ + maxExtent: layerContext.maxExtent, │ │ │ │ + metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, { │ │ │ │ + styles: layerContext.styles, │ │ │ │ + formats: layerContext.formats, │ │ │ │ + "abstract": layerContext["abstract"], │ │ │ │ + dataURL: layerContext.dataURL │ │ │ │ + }), │ │ │ │ + numZoomLevels: layerContext.numZoomLevels, │ │ │ │ + units: layerContext.units, │ │ │ │ + isBaseLayer: layerContext.isBaseLayer, │ │ │ │ + opacity: layerContext.opacity, │ │ │ │ + displayInLayerSwitcher: layerContext.displayInLayerSwitcher, │ │ │ │ + singleTile: layerContext.singleTile, │ │ │ │ + tileSize: (layerContext.tileSize) ? │ │ │ │ + new OpenLayers.Size( │ │ │ │ + layerContext.tileSize.width, │ │ │ │ + layerContext.tileSize.height │ │ │ │ + ) : undefined, │ │ │ │ + minScale: layerContext.minScale || layerContext.maxScaleDenominator, │ │ │ │ + maxScale: layerContext.maxScale || layerContext.minScaleDenominator, │ │ │ │ + srs: layerContext.srs, │ │ │ │ + dimensions: layerContext.dimensions, │ │ │ │ + metadataURL: layerContext.metadataURL │ │ │ │ + }; │ │ │ │ + if (this.layerOptions) { │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.layerOptions); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: kineticInterval │ │ │ │ - * {Integer} Interval in milliseconds between 2 steps in the "kinetic │ │ │ │ - * scrolling". Applies only if enableKinetic is set. Defaults │ │ │ │ - * to 10 milliseconds. │ │ │ │ - */ │ │ │ │ - kineticInterval: 10, │ │ │ │ + var params = { │ │ │ │ + layers: layerContext.name, │ │ │ │ + transparent: layerContext.transparent, │ │ │ │ + version: layerContext.version │ │ │ │ + }; │ │ │ │ + if (layerContext.formats && layerContext.formats.length > 0) { │ │ │ │ + // set default value for params if current attribute is not positionned │ │ │ │ + params.format = layerContext.formats[0].value; │ │ │ │ + for (i = 0, len = layerContext.formats.length; i < len; i++) { │ │ │ │ + var format = layerContext.formats[i]; │ │ │ │ + if (format.current == true) { │ │ │ │ + params.format = format.value; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (layerContext.styles && layerContext.styles.length > 0) { │ │ │ │ + for (i = 0, len = layerContext.styles.length; i < len; i++) { │ │ │ │ + var style = layerContext.styles[i]; │ │ │ │ + if (style.current == true) { │ │ │ │ + // three style types to consider │ │ │ │ + // 1) linked SLD │ │ │ │ + // 2) inline SLD │ │ │ │ + // 3) named style │ │ │ │ + if (style.href) { │ │ │ │ + params.sld = style.href; │ │ │ │ + } else if (style.body) { │ │ │ │ + params.sld_body = style.body; │ │ │ │ + } else { │ │ │ │ + params.styles = style.name; │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (this.layerParams) { │ │ │ │ + OpenLayers.Util.applyDefaults(params, this.layerParams); │ │ │ │ + } │ │ │ │ │ │ │ │ + var layer = null; │ │ │ │ + var service = layerContext.service; │ │ │ │ + if (service == OpenLayers.Format.Context.serviceTypes.WFS) { │ │ │ │ + options.strategies = [new OpenLayers.Strategy.BBOX()]; │ │ │ │ + options.protocol = new OpenLayers.Protocol.WFS({ │ │ │ │ + url: layerContext.url, │ │ │ │ + // since we do not know featureNS, let the protocol │ │ │ │ + // determine it automagically using featurePrefix │ │ │ │ + featurePrefix: layerContext.name.split(":")[0], │ │ │ │ + featureType: layerContext.name.split(":").pop() │ │ │ │ + }); │ │ │ │ + layer = new OpenLayers.Layer.Vector( │ │ │ │ + layerContext.title || layerContext.name, │ │ │ │ + options │ │ │ │ + ); │ │ │ │ + } else if (service == OpenLayers.Format.Context.serviceTypes.KML) { │ │ │ │ + // use a vector layer with an HTTP Protcol and a Fixed strategy │ │ │ │ + options.strategies = [new OpenLayers.Strategy.Fixed()]; │ │ │ │ + options.protocol = new OpenLayers.Protocol.HTTP({ │ │ │ │ + url: layerContext.url, │ │ │ │ + format: new OpenLayers.Format.KML() │ │ │ │ + }); │ │ │ │ + layer = new OpenLayers.Layer.Vector( │ │ │ │ + layerContext.title || layerContext.name, │ │ │ │ + options │ │ │ │ + ); │ │ │ │ + } else if (service == OpenLayers.Format.Context.serviceTypes.GML) { │ │ │ │ + // use a vector layer with a HTTP Protocol and a Fixed strategy │ │ │ │ + options.strategies = [new OpenLayers.Strategy.Fixed()]; │ │ │ │ + options.protocol = new OpenLayers.Protocol.HTTP({ │ │ │ │ + url: layerContext.url, │ │ │ │ + format: new OpenLayers.Format.GML() │ │ │ │ + }); │ │ │ │ + layer = new OpenLayers.Layer.Vector( │ │ │ │ + layerContext.title || layerContext.name, │ │ │ │ + options │ │ │ │ + ); │ │ │ │ + } else if (layerContext.features) { │ │ │ │ + // inline GML or KML features │ │ │ │ + layer = new OpenLayers.Layer.Vector( │ │ │ │ + layerContext.title || layerContext.name, │ │ │ │ + options │ │ │ │ + ); │ │ │ │ + layer.addFeatures(layerContext.features); │ │ │ │ + } else if (layerContext.categoryLayer !== true) { │ │ │ │ + layer = new OpenLayers.Layer.WMS( │ │ │ │ + layerContext.title || layerContext.name, │ │ │ │ + layerContext.url, │ │ │ │ + params, │ │ │ │ + options │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return layer; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ - * Creates a Drag handler, using <panMap> and │ │ │ │ - * <panMapDone> as callbacks. │ │ │ │ + * Method: getLayersFromContext │ │ │ │ + * Create an array of layers from an array of layerContext objects. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layersContext - {Array(Object)} An array of objects representing layers. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Layer>)} An array of layers. │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - if (this.enableKinetic && OpenLayers.Kinetic) { │ │ │ │ - var config = { │ │ │ │ - interval: this.kineticInterval │ │ │ │ - }; │ │ │ │ - if (typeof this.enableKinetic === "object") { │ │ │ │ - config = OpenLayers.Util.extend(config, this.enableKinetic); │ │ │ │ + getLayersFromContext: function(layersContext) { │ │ │ │ + var layers = []; │ │ │ │ + for (var i = 0, len = layersContext.length; i < len; i++) { │ │ │ │ + var layer = this.getLayerFromContext(layersContext[i]); │ │ │ │ + if (layer !== null) { │ │ │ │ + layers.push(layer); │ │ │ │ } │ │ │ │ - this.kinetic = new OpenLayers.Kinetic(config); │ │ │ │ } │ │ │ │ - this.handler = new OpenLayers.Handler.Drag(this, { │ │ │ │ - "move": this.panMap, │ │ │ │ - "done": this.panMapDone, │ │ │ │ - "down": this.panMapStart │ │ │ │ - }, { │ │ │ │ - interval: this.interval, │ │ │ │ - documentDrag: this.documentDrag │ │ │ │ - }); │ │ │ │ + return layers; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: panMapStart │ │ │ │ + * Method: contextToMap │ │ │ │ + * Create a map given a context object. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * context - {Object} The context object. │ │ │ │ + * options - {Object} Default map options. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Map>} A map based on the context object. │ │ │ │ */ │ │ │ │ - panMapStart: function() { │ │ │ │ - if (this.kinetic) { │ │ │ │ - this.kinetic.begin(); │ │ │ │ + contextToMap: function(context, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults({ │ │ │ │ + maxExtent: context.maxExtent, │ │ │ │ + projection: context.projection, │ │ │ │ + units: context.units │ │ │ │ + }, options); │ │ │ │ + │ │ │ │ + if (options.maxExtent) { │ │ │ │ + options.maxResolution = │ │ │ │ + options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH; │ │ │ │ } │ │ │ │ + │ │ │ │ + var metadata = { │ │ │ │ + contactInformation: context.contactInformation, │ │ │ │ + "abstract": context["abstract"], │ │ │ │ + keywords: context.keywords, │ │ │ │ + logo: context.logo, │ │ │ │ + descriptionURL: context.descriptionURL │ │ │ │ + }; │ │ │ │ + │ │ │ │ + options.metadata = metadata; │ │ │ │ + │ │ │ │ + var map = new OpenLayers.Map(options); │ │ │ │ + map.addLayers(this.getLayersFromContext(context.layersContext)); │ │ │ │ + map.setCenter( │ │ │ │ + context.bounds.getCenterLonLat(), │ │ │ │ + map.getZoomForExtent(context.bounds, true) │ │ │ │ + ); │ │ │ │ + return map; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: panMap │ │ │ │ + * Method: mergeContextToMap │ │ │ │ + * Add layers from a context object to a map. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * xy - {<OpenLayers.Pixel>} Pixel of the mouse position │ │ │ │ + * context - {Object} The context object. │ │ │ │ + * map - {<OpenLayers.Map>} The map. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Map>} The same map with layers added. │ │ │ │ */ │ │ │ │ - panMap: function(xy) { │ │ │ │ - if (this.kinetic) { │ │ │ │ - this.kinetic.update(xy); │ │ │ │ - } │ │ │ │ - this.panned = true; │ │ │ │ - this.map.pan( │ │ │ │ - this.handler.last.x - xy.x, │ │ │ │ - this.handler.last.y - xy.y, { │ │ │ │ - dragging: true, │ │ │ │ - animate: false │ │ │ │ - } │ │ │ │ - ); │ │ │ │ + mergeContextToMap: function(context, map) { │ │ │ │ + map.addLayers(this.getLayersFromContext(context.layersContext)); │ │ │ │ + return map; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: panMapDone │ │ │ │ - * Finish the panning operation. Only call setCenter (through <panMap>) │ │ │ │ - * if the map has actually been moved. │ │ │ │ + * APIMethod: write │ │ │ │ + * Write a context document given a map. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * xy - {<OpenLayers.Pixel>} Pixel of the mouse position │ │ │ │ + * obj - {<OpenLayers.Map> | Object} A map or context object. │ │ │ │ + * options - {Object} Optional configuration object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A context document string. │ │ │ │ */ │ │ │ │ - panMapDone: function(xy) { │ │ │ │ - if (this.panned) { │ │ │ │ - var res = null; │ │ │ │ - if (this.kinetic) { │ │ │ │ - res = this.kinetic.end(xy); │ │ │ │ - } │ │ │ │ - this.map.pan( │ │ │ │ - this.handler.last.x - xy.x, │ │ │ │ - this.handler.last.y - xy.y, { │ │ │ │ - dragging: !!res, │ │ │ │ - animate: false │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - if (res) { │ │ │ │ - var self = this; │ │ │ │ - this.kinetic.move(res, function(x, y, end) { │ │ │ │ - self.map.pan(x, y, { │ │ │ │ - dragging: !end, │ │ │ │ - animate: false │ │ │ │ - }); │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - this.panned = false; │ │ │ │ - } │ │ │ │ + write: function(obj, options) { │ │ │ │ + obj = this.toContext(obj); │ │ │ │ + return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this, │ │ │ │ + arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.DragPan" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.Context" │ │ │ │ }); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Format.Context.serviceTypes │ │ │ │ + * Enumeration for service types │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.Context.serviceTypes = { │ │ │ │ + "WMS": "urn:ogc:serviceType:WMS", │ │ │ │ + "WFS": "urn:ogc:serviceType:WFS", │ │ │ │ + "WCS": "urn:ogc:serviceType:WCS", │ │ │ │ + "GML": "urn:ogc:serviceType:GML", │ │ │ │ + "SLD": "urn:ogc:serviceType:SLD", │ │ │ │ + "FES": "urn:ogc:serviceType:FES", │ │ │ │ + "KML": "urn:ogc:serviceType:KML" │ │ │ │ +}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Handler/MouseWheel.js │ │ │ │ + OpenLayers/Format/KML.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Handler.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Date.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Geometry/Point.js │ │ │ │ + * @requires OpenLayers/Geometry/LineString.js │ │ │ │ + * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ + * @requires OpenLayers/Geometry/Collection.js │ │ │ │ + * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ + * @requires OpenLayers/Projection.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Handler.MouseWheel │ │ │ │ - * Handler for wheel up/down events. │ │ │ │ + * Class: OpenLayers.Format.KML │ │ │ │ + * Read/Write KML. Create a new instance with the <OpenLayers.Format.KML> │ │ │ │ + * constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Handler> │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - /** │ │ │ │ - * Property: wheelListener │ │ │ │ - * {function} │ │ │ │ - */ │ │ │ │ - wheelListener: null, │ │ │ │ +OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: interval │ │ │ │ - * {Integer} In order to increase server performance, an interval (in │ │ │ │ - * milliseconds) can be set to reduce the number of up/down events │ │ │ │ - * called. If set, a new up/down event will not be set until the │ │ │ │ - * interval has passed. │ │ │ │ - * Defaults to 0, meaning no interval. │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ */ │ │ │ │ - interval: 0, │ │ │ │ + namespaces: { │ │ │ │ + kml: "http://www.opengis.net/kml/2.2", │ │ │ │ + gx: "http://www.google.com/kml/ext/2.2" │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: maxDelta │ │ │ │ - * {Integer} Maximum delta to collect before breaking from the current │ │ │ │ - * interval. In cumulative mode, this also limits the maximum delta │ │ │ │ - * returned from the handler. Default is Number.POSITIVE_INFINITY. │ │ │ │ + * APIProperty: kmlns │ │ │ │ + * {String} KML Namespace to use. Defaults to 2.0 namespace. │ │ │ │ */ │ │ │ │ - maxDelta: Number.POSITIVE_INFINITY, │ │ │ │ + kmlns: "http://earth.google.com/kml/2.0", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: delta │ │ │ │ - * {Integer} When interval is set, delta collects the mousewheel z-deltas │ │ │ │ - * of the events that occur within the interval. │ │ │ │ - * See also the cumulative option │ │ │ │ + /** │ │ │ │ + * APIProperty: placemarksDesc │ │ │ │ + * {String} Name of the placemarks. Default is "No description available". │ │ │ │ */ │ │ │ │ - delta: 0, │ │ │ │ + placemarksDesc: "No description available", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: cumulative │ │ │ │ - * {Boolean} When interval is set: true to collect all the mousewheel │ │ │ │ - * z-deltas, false to only record the delta direction (positive or │ │ │ │ - * negative) │ │ │ │ + /** │ │ │ │ + * APIProperty: foldersName │ │ │ │ + * {String} Name of the folders. Default is "OpenLayers export". │ │ │ │ + * If set to null, no name element will be created. │ │ │ │ */ │ │ │ │ - cumulative: true, │ │ │ │ + foldersName: "OpenLayers export", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: foldersDesc │ │ │ │ + * {String} Description of the folders. Default is "Exported on [date]." │ │ │ │ + * If set to null, no description element will be created. │ │ │ │ + */ │ │ │ │ + foldersDesc: "Exported on " + new Date(), │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Handler.MouseWheel │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} │ │ │ │ - * callbacks - {Object} An object containing a single function to be │ │ │ │ - * called when the drag operation is finished. │ │ │ │ - * The callback should expect to recieve a single │ │ │ │ - * argument, the point geometry. │ │ │ │ - * options - {Object} │ │ │ │ + * APIProperty: extractAttributes │ │ │ │ + * {Boolean} Extract attributes from KML. Default is true. │ │ │ │ + * Extracting styleUrls requires this to be set to true │ │ │ │ + * Note that currently only Data and SimpleData │ │ │ │ + * elements are handled. │ │ │ │ */ │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - this.wheelListener = OpenLayers.Function.bindAsEventListener( │ │ │ │ - this.onWheelEvent, this │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ + extractAttributes: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ + * APIProperty: kvpAttributes │ │ │ │ + * {Boolean} Only used if extractAttributes is true. │ │ │ │ + * If set to true, attributes will be simple │ │ │ │ + * key-value pairs, compatible with other formats, │ │ │ │ + * Any displayName elements will be ignored. │ │ │ │ + * If set to false, attributes will be objects, │ │ │ │ + * retaining any displayName elements, but not │ │ │ │ + * compatible with other formats. Any CDATA in │ │ │ │ + * displayName will be read in as a string value. │ │ │ │ + * Default is false. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ - this.wheelListener = null; │ │ │ │ - }, │ │ │ │ + kvpAttributes: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/ │ │ │ │ + * Property: extractStyles │ │ │ │ + * {Boolean} Extract styles from KML. Default is false. │ │ │ │ + * Extracting styleUrls also requires extractAttributes to be │ │ │ │ + * set to true │ │ │ │ */ │ │ │ │ + extractStyles: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: onWheelEvent │ │ │ │ - * Catch the wheel event and handle it xbrowserly │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * APIProperty: extractTracks │ │ │ │ + * {Boolean} Extract gx:Track elements from Placemark elements. Default │ │ │ │ + * is false. If true, features will be generated for all points in │ │ │ │ + * all gx:Track elements. Features will have a when (Date) attribute │ │ │ │ + * based on when elements in the track. If tracks include angle │ │ │ │ + * elements, features will have heading, tilt, and roll attributes. │ │ │ │ + * If track point coordinates have three values, features will have │ │ │ │ + * an altitude attribute with the third coordinate value. │ │ │ │ + */ │ │ │ │ + extractTracks: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: trackAttributes │ │ │ │ + * {Array} If <extractTracks> is true, points within gx:Track elements will │ │ │ │ + * be parsed as features with when, heading, tilt, and roll attributes. │ │ │ │ + * Any additional attribute names can be provided in <trackAttributes>. │ │ │ │ + */ │ │ │ │ + trackAttributes: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: internalns │ │ │ │ + * {String} KML Namespace to use -- defaults to the namespace of the │ │ │ │ + * Placemark node being parsed, but falls back to kmlns. │ │ │ │ + */ │ │ │ │ + internalns: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: features │ │ │ │ + * {Array} Array of features │ │ │ │ + * │ │ │ │ + */ │ │ │ │ + features: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: styles │ │ │ │ + * {Object} Storage of style objects │ │ │ │ + * │ │ │ │ + */ │ │ │ │ + styles: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: styleBaseUrl │ │ │ │ + * {String} │ │ │ │ + */ │ │ │ │ + styleBaseUrl: "", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: fetched │ │ │ │ + * {Object} Storage of KML URLs that have been fetched before │ │ │ │ + * in order to prevent reloading them. │ │ │ │ + */ │ │ │ │ + fetched: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: maxDepth │ │ │ │ + * {Integer} Maximum depth for recursive loading external KML URLs │ │ │ │ + * Defaults to 0: do no external fetching │ │ │ │ + */ │ │ │ │ + maxDepth: 0, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.KML │ │ │ │ + * Create a new parser for KML. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * e - {Event} │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - onWheelEvent: function(e) { │ │ │ │ + initialize: function(options) { │ │ │ │ + // compile regular expressions once instead of every time they are used │ │ │ │ + this.regExes = { │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ + trimComma: (/\s*,\s*/g), │ │ │ │ + kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/), │ │ │ │ + kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/), │ │ │ │ + straightBracket: (/\$\[(.*?)\]/g) │ │ │ │ + }; │ │ │ │ + // KML coordinates are always in longlat WGS84 │ │ │ │ + this.externalProjection = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ │ │ │ │ - // make sure we have a map and check keyboard modifiers │ │ │ │ - if (!this.map || !this.checkModifiers(e)) { │ │ │ │ - return; │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read data from a string, and return a list of features. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} List of features. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + this.features = []; │ │ │ │ + this.styles = {}; │ │ │ │ + this.fetched = {}; │ │ │ │ + │ │ │ │ + // Set default options │ │ │ │ + var options = { │ │ │ │ + depth: 0, │ │ │ │ + styleBaseUrl: this.styleBaseUrl │ │ │ │ + }; │ │ │ │ + │ │ │ │ + return this.parseData(data, options); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: parseData │ │ │ │ + * Read data from a string, and return a list of features. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * options - {Object} Hash of options │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} List of features. │ │ │ │ + */ │ │ │ │ + parseData: function(data, options) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ } │ │ │ │ │ │ │ │ - // Ride up the element's DOM hierarchy to determine if it or any of │ │ │ │ - // its ancestors was: │ │ │ │ - // * specifically marked as scrollable (CSS overflow property) │ │ │ │ - // * one of our layer divs or a div marked as scrollable │ │ │ │ - // ('olScrollable' CSS class) │ │ │ │ - // * the map div │ │ │ │ - // │ │ │ │ - var overScrollableDiv = false; │ │ │ │ - var allowScroll = false; │ │ │ │ - var overMapDiv = false; │ │ │ │ + // Loop throught the following node types in this order and │ │ │ │ + // process the nodes found │ │ │ │ + var types = ["Link", "NetworkLink", "Style", "StyleMap", "Placemark"]; │ │ │ │ + for (var i = 0, len = types.length; i < len; ++i) { │ │ │ │ + var type = types[i]; │ │ │ │ │ │ │ │ - var elem = OpenLayers.Event.element(e); │ │ │ │ - while ((elem != null) && !overMapDiv && !overScrollableDiv) { │ │ │ │ + var nodes = this.getElementsByTagNameNS(data, "*", type); │ │ │ │ │ │ │ │ - if (!overScrollableDiv) { │ │ │ │ - try { │ │ │ │ - var overflow; │ │ │ │ - if (elem.currentStyle) { │ │ │ │ - overflow = elem.currentStyle["overflow"]; │ │ │ │ - } else { │ │ │ │ - var style = │ │ │ │ - document.defaultView.getComputedStyle(elem, null); │ │ │ │ - overflow = style.getPropertyValue("overflow"); │ │ │ │ - } │ │ │ │ - overScrollableDiv = (overflow && │ │ │ │ - (overflow == "auto") || (overflow == "scroll")); │ │ │ │ - } catch (err) { │ │ │ │ - //sometimes when scrolling in a popup, this causes │ │ │ │ - // obscure browser error │ │ │ │ - } │ │ │ │ + // skip to next type if no nodes are found │ │ │ │ + if (nodes.length == 0) { │ │ │ │ + continue; │ │ │ │ } │ │ │ │ │ │ │ │ - if (!allowScroll) { │ │ │ │ - allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable'); │ │ │ │ - if (!allowScroll) { │ │ │ │ - for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ - // Are we in the layer div? Note that we have two cases │ │ │ │ - // here: one is to catch EventPane layers, which have a │ │ │ │ - // pane above the layer (layer.pane) │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - if (elem == layer.div || elem == layer.pane) { │ │ │ │ - allowScroll = true; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ + switch (type.toLowerCase()) { │ │ │ │ + │ │ │ │ + // Fetch external links │ │ │ │ + case "link": │ │ │ │ + case "networklink": │ │ │ │ + this.parseLinks(nodes, options); │ │ │ │ + break; │ │ │ │ + │ │ │ │ + // parse style information │ │ │ │ + case "style": │ │ │ │ + if (this.extractStyles) { │ │ │ │ + this.parseStyles(nodes, options); │ │ │ │ } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - overMapDiv = (elem == this.map.div); │ │ │ │ + break; │ │ │ │ + case "stylemap": │ │ │ │ + if (this.extractStyles) { │ │ │ │ + this.parseStyleMaps(nodes, options); │ │ │ │ + } │ │ │ │ + break; │ │ │ │ │ │ │ │ - elem = elem.parentNode; │ │ │ │ + // parse features │ │ │ │ + case "placemark": │ │ │ │ + this.parseFeatures(nodes, options); │ │ │ │ + break; │ │ │ │ + } │ │ │ │ } │ │ │ │ │ │ │ │ - // Logic below is the following: │ │ │ │ - // │ │ │ │ - // If we are over a scrollable div or not over the map div: │ │ │ │ - // * do nothing (let the browser handle scrolling) │ │ │ │ - // │ │ │ │ - // otherwise │ │ │ │ - // │ │ │ │ - // If we are over the layer div or a 'olScrollable' div: │ │ │ │ - // * zoom/in out │ │ │ │ - // then │ │ │ │ - // * kill event (so as not to also scroll the page after zooming) │ │ │ │ - // │ │ │ │ - // otherwise │ │ │ │ - // │ │ │ │ - // Kill the event (dont scroll the page if we wheel over the │ │ │ │ - // layerswitcher or the pan/zoom control) │ │ │ │ - // │ │ │ │ - if (!overScrollableDiv && overMapDiv) { │ │ │ │ - if (allowScroll) { │ │ │ │ - var delta = 0; │ │ │ │ + return this.features; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (e.wheelDelta) { │ │ │ │ - delta = e.wheelDelta; │ │ │ │ - if (delta % 160 === 0) { │ │ │ │ - // opera have steps of 160 instead of 120 │ │ │ │ - delta = delta * 0.75; │ │ │ │ - } │ │ │ │ - delta = delta / 120; │ │ │ │ - } else if (e.detail) { │ │ │ │ - // detail in Firefox on OS X is 1/3 of Windows │ │ │ │ - // so force delta 1 / -1 │ │ │ │ - delta = -(e.detail / Math.abs(e.detail)); │ │ │ │ - } │ │ │ │ - this.delta += delta; │ │ │ │ + /** │ │ │ │ + * Method: parseLinks │ │ │ │ + * Finds URLs of linked KML documents and fetches them │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * nodes - {Array} of {DOMElement} data to read/parse. │ │ │ │ + * options - {Object} Hash of options │ │ │ │ + * │ │ │ │ + */ │ │ │ │ + parseLinks: function(nodes, options) { │ │ │ │ │ │ │ │ - window.clearTimeout(this._timeoutId); │ │ │ │ - if (this.interval && Math.abs(this.delta) < this.maxDelta) { │ │ │ │ - // store e because window.event might change during delay │ │ │ │ - var evt = OpenLayers.Util.extend({}, e); │ │ │ │ - this._timeoutId = window.setTimeout( │ │ │ │ - OpenLayers.Function.bind(function() { │ │ │ │ - this.wheelZoom(evt); │ │ │ │ - }, this), │ │ │ │ - this.interval │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - this.wheelZoom(e); │ │ │ │ + // Fetch external links <NetworkLink> and <Link> │ │ │ │ + // Don't do anything if we have reached our maximum depth for recursion │ │ │ │ + if (options.depth >= this.maxDepth) { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // increase depth │ │ │ │ + var newOptions = OpenLayers.Util.extend({}, options); │ │ │ │ + newOptions.depth++; │ │ │ │ + │ │ │ │ + for (var i = 0, len = nodes.length; i < len; i++) { │ │ │ │ + var href = this.parseProperty(nodes[i], "*", "href"); │ │ │ │ + if (href && !this.fetched[href]) { │ │ │ │ + this.fetched[href] = true; // prevent reloading the same urls │ │ │ │ + var data = this.fetchLink(href); │ │ │ │ + if (data) { │ │ │ │ + this.parseData(data, newOptions); │ │ │ │ } │ │ │ │ } │ │ │ │ - OpenLayers.Event.stop(e); │ │ │ │ } │ │ │ │ + │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: wheelZoom │ │ │ │ - * Given the wheel event, we carry out the appropriate zooming in or out, │ │ │ │ - * based on the 'wheelDelta' or 'detail' property of the event. │ │ │ │ + * Method: fetchLink │ │ │ │ + * Fetches a URL and returns the result │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * href - {String} url to be fetched │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * e - {Event} │ │ │ │ */ │ │ │ │ - wheelZoom: function(e) { │ │ │ │ - var delta = this.delta; │ │ │ │ - this.delta = 0; │ │ │ │ - │ │ │ │ - if (delta) { │ │ │ │ - e.xy = this.map.events.getMousePosition(e); │ │ │ │ - if (delta < 0) { │ │ │ │ - this.callback("down", │ │ │ │ - [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]); │ │ │ │ - } else { │ │ │ │ - this.callback("up", │ │ │ │ - [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]); │ │ │ │ - } │ │ │ │ + fetchLink: function(href) { │ │ │ │ + var request = OpenLayers.Request.GET({ │ │ │ │ + url: href, │ │ │ │ + async: false │ │ │ │ + }); │ │ │ │ + if (request) { │ │ │ │ + return request.responseText; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: activate │ │ │ │ + * Method: parseStyles │ │ │ │ + * Parses <Style> nodes │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * nodes - {Array} of {DOMElement} data to read/parse. │ │ │ │ + * options - {Object} Hash of options │ │ │ │ + * │ │ │ │ */ │ │ │ │ - activate: function(evt) { │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - //register mousewheel events specifically on the window and document │ │ │ │ - var wheelListener = this.wheelListener; │ │ │ │ - OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener); │ │ │ │ - OpenLayers.Event.observe(window, "mousewheel", wheelListener); │ │ │ │ - OpenLayers.Event.observe(document, "mousewheel", wheelListener); │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ + parseStyles: function(nodes, options) { │ │ │ │ + for (var i = 0, len = nodes.length; i < len; i++) { │ │ │ │ + var style = this.parseStyle(nodes[i]); │ │ │ │ + if (style) { │ │ │ │ + var styleName = (options.styleBaseUrl || "") + "#" + style.id; │ │ │ │ + │ │ │ │ + this.styles[styleName] = style; │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: deactivate │ │ │ │ + * Method: parseKmlColor │ │ │ │ + * Parses a kml color (in 'aabbggrr' format) and returns the corresponding │ │ │ │ + * color and opacity or null if the color is invalid. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * kmlColor - {String} a kml formated color │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} │ │ │ │ */ │ │ │ │ - deactivate: function(evt) { │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - // unregister mousewheel events specifically on the window and document │ │ │ │ - var wheelListener = this.wheelListener; │ │ │ │ - OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener); │ │ │ │ - OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener); │ │ │ │ - OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener); │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ + parseKmlColor: function(kmlColor) { │ │ │ │ + var color = null; │ │ │ │ + if (kmlColor) { │ │ │ │ + var matches = kmlColor.match(this.regExes.kmlColor); │ │ │ │ + if (matches) { │ │ │ │ + color = { │ │ │ │ + color: '#' + matches[4] + matches[3] + matches[2], │ │ │ │ + opacity: parseInt(matches[1], 16) / 255 │ │ │ │ + }; │ │ │ │ + } │ │ │ │ } │ │ │ │ + return color; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.MouseWheel" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/Navigation.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * Method: parseStyle │ │ │ │ + * Parses the children of a <Style> node and builds the style hash │ │ │ │ + * accordingly │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} <Style> node │ │ │ │ + * │ │ │ │ + */ │ │ │ │ + parseStyle: function(node) { │ │ │ │ + var style = {}; │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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 types = ["LineStyle", "PolyStyle", "IconStyle", "BalloonStyle", │ │ │ │ + "LabelStyle" │ │ │ │ + ]; │ │ │ │ + var type, styleTypeNode, nodeList, geometry, parser; │ │ │ │ + for (var i = 0, len = types.length; i < len; ++i) { │ │ │ │ + type = types[i]; │ │ │ │ + styleTypeNode = this.getElementsByTagNameNS(node, "*", type)[0]; │ │ │ │ + if (!styleTypeNode) { │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control/ZoomBox.js │ │ │ │ - * @requires OpenLayers/Control/DragPan.js │ │ │ │ - * @requires OpenLayers/Handler/MouseWheel.js │ │ │ │ - * @requires OpenLayers/Handler/Click.js │ │ │ │ - */ │ │ │ │ + // only deal with first geometry of this type │ │ │ │ + switch (type.toLowerCase()) { │ │ │ │ + case "linestyle": │ │ │ │ + var kmlColor = this.parseProperty(styleTypeNode, "*", "color"); │ │ │ │ + var color = this.parseKmlColor(kmlColor); │ │ │ │ + if (color) { │ │ │ │ + style["strokeColor"] = color.color; │ │ │ │ + style["strokeOpacity"] = color.opacity; │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.Navigation │ │ │ │ - * The navigation control handles map browsing with mouse events (dragging, │ │ │ │ - * double-clicking, and scrolling the wheel). Create a new navigation │ │ │ │ - * control with the <OpenLayers.Control.Navigation> control. │ │ │ │ - * │ │ │ │ - * Note that this control is added to the map by default (if no controls │ │ │ │ - * array is sent in the options object to the <OpenLayers.Map> │ │ │ │ - * constructor). │ │ │ │ - * │ │ │ │ - * Inherits: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + var width = this.parseProperty(styleTypeNode, "*", "width"); │ │ │ │ + if (width) { │ │ │ │ + style["strokeWidth"] = width; │ │ │ │ + } │ │ │ │ + break; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: dragPan │ │ │ │ - * {<OpenLayers.Control.DragPan>} │ │ │ │ - */ │ │ │ │ - dragPan: null, │ │ │ │ + case "polystyle": │ │ │ │ + var kmlColor = this.parseProperty(styleTypeNode, "*", "color"); │ │ │ │ + var color = this.parseKmlColor(kmlColor); │ │ │ │ + if (color) { │ │ │ │ + style["fillOpacity"] = color.opacity; │ │ │ │ + style["fillColor"] = color.color; │ │ │ │ + } │ │ │ │ + // Check if fill is disabled │ │ │ │ + var fill = this.parseProperty(styleTypeNode, "*", "fill"); │ │ │ │ + if (fill == "0") { │ │ │ │ + style["fillColor"] = "none"; │ │ │ │ + } │ │ │ │ + // Check if outline is disabled │ │ │ │ + var outline = this.parseProperty(styleTypeNode, "*", "outline"); │ │ │ │ + if (outline == "0") { │ │ │ │ + style["strokeWidth"] = "0"; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: dragPanOptions │ │ │ │ - * {Object} Options passed to the DragPan control. │ │ │ │ - */ │ │ │ │ - dragPanOptions: null, │ │ │ │ + break; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: pinchZoom │ │ │ │ - * {<OpenLayers.Control.PinchZoom>} │ │ │ │ - */ │ │ │ │ - pinchZoom: null, │ │ │ │ + case "iconstyle": │ │ │ │ + // set scale │ │ │ │ + var scale = parseFloat(this.parseProperty(styleTypeNode, │ │ │ │ + "*", "scale") || 1); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: pinchZoomOptions │ │ │ │ - * {Object} Options passed to the PinchZoom control. │ │ │ │ - */ │ │ │ │ - pinchZoomOptions: null, │ │ │ │ + // set default width and height of icon │ │ │ │ + var width = 32 * scale; │ │ │ │ + var height = 32 * scale; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: documentDrag │ │ │ │ - * {Boolean} Allow panning of the map by dragging outside map viewport. │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - documentDrag: false, │ │ │ │ + var iconNode = this.getElementsByTagNameNS(styleTypeNode, │ │ │ │ + "*", │ │ │ │ + "Icon")[0]; │ │ │ │ + if (iconNode) { │ │ │ │ + var href = this.parseProperty(iconNode, "*", "href"); │ │ │ │ + if (href) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: zoomBox │ │ │ │ - * {<OpenLayers.Control.ZoomBox>} │ │ │ │ - */ │ │ │ │ - zoomBox: null, │ │ │ │ + var w = this.parseProperty(iconNode, "*", "w"); │ │ │ │ + var h = this.parseProperty(iconNode, "*", "h"); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomBoxEnabled │ │ │ │ - * {Boolean} Whether the user can draw a box to zoom │ │ │ │ - */ │ │ │ │ - zoomBoxEnabled: true, │ │ │ │ + // Settings for Google specific icons that are 64x64 │ │ │ │ + // We set the width and height to 64 and halve the │ │ │ │ + // scale to prevent icons from being too big │ │ │ │ + var google = "http://maps.google.com/mapfiles/kml"; │ │ │ │ + if (OpenLayers.String.startsWith( │ │ │ │ + href, google) && !w && !h) { │ │ │ │ + w = 64; │ │ │ │ + h = 64; │ │ │ │ + scale = scale / 2; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomWheelEnabled │ │ │ │ - * {Boolean} Whether the mousewheel should zoom the map │ │ │ │ - */ │ │ │ │ - zoomWheelEnabled: true, │ │ │ │ + // if only dimension is defined, make sure the │ │ │ │ + // other one has the same value │ │ │ │ + w = w || h; │ │ │ │ + h = h || w; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: mouseWheelOptions │ │ │ │ - * {Object} Options passed to the MouseWheel control (only useful if │ │ │ │ - * <zoomWheelEnabled> is set to true). Default is no options for maps │ │ │ │ - * with fractionalZoom set to true, otherwise │ │ │ │ - * {cumulative: false, interval: 50, maxDelta: 6} │ │ │ │ - */ │ │ │ │ - mouseWheelOptions: null, │ │ │ │ + if (w) { │ │ │ │ + width = parseInt(w) * scale; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: handleRightClicks │ │ │ │ - * {Boolean} Whether or not to handle right clicks. Default is false. │ │ │ │ - */ │ │ │ │ - handleRightClicks: false, │ │ │ │ + if (h) { │ │ │ │ + height = parseInt(h) * scale; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomBoxKeyMask │ │ │ │ - * {Integer} <OpenLayers.Handler> key code of the key, which has to be │ │ │ │ - * pressed, while drawing the zoom box with the mouse on the screen. │ │ │ │ - * You should probably set handleRightClicks to true if you use this │ │ │ │ - * with MOD_CTRL, to disable the context menu for machines which use │ │ │ │ - * CTRL-Click as a right click. │ │ │ │ - * Default: <OpenLayers.Handler.MOD_SHIFT> │ │ │ │ - */ │ │ │ │ - zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT, │ │ │ │ + // support for internal icons │ │ │ │ + // (/root://icons/palette-x.png) │ │ │ │ + // x and y tell the position on the palette: │ │ │ │ + // - in pixels │ │ │ │ + // - starting from the left bottom │ │ │ │ + // We translate that to a position in the list │ │ │ │ + // and request the appropriate icon from the │ │ │ │ + // google maps website │ │ │ │ + var matches = href.match(this.regExes.kmlIconPalette); │ │ │ │ + if (matches) { │ │ │ │ + var palette = matches[1]; │ │ │ │ + var file_extension = matches[2]; │ │ │ │ + │ │ │ │ + var x = this.parseProperty(iconNode, "*", "x"); │ │ │ │ + var y = this.parseProperty(iconNode, "*", "y"); │ │ │ │ + │ │ │ │ + var posX = x ? x / 32 : 0; │ │ │ │ + var posY = y ? (7 - y / 32) : 7; │ │ │ │ + │ │ │ │ + var pos = posY * 8 + posX; │ │ │ │ + href = "http://maps.google.com/mapfiles/kml/pal" + │ │ │ │ + palette + "/icon" + pos + file_extension; │ │ │ │ + } │ │ │ │ + │ │ │ │ + style["graphicOpacity"] = 1; // fully opaque │ │ │ │ + style["externalGraphic"] = href; │ │ │ │ + } │ │ │ │ + │ │ │ │ + } │ │ │ │ + │ │ │ │ + │ │ │ │ + // hotSpots define the offset for an Icon │ │ │ │ + var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode, │ │ │ │ + "*", │ │ │ │ + "hotSpot")[0]; │ │ │ │ + if (hotSpotNode) { │ │ │ │ + var x = parseFloat(hotSpotNode.getAttribute("x")); │ │ │ │ + var y = parseFloat(hotSpotNode.getAttribute("y")); │ │ │ │ + │ │ │ │ + var xUnits = hotSpotNode.getAttribute("xunits"); │ │ │ │ + if (xUnits == "pixels") { │ │ │ │ + style["graphicXOffset"] = -x * scale; │ │ │ │ + } else if (xUnits == "insetPixels") { │ │ │ │ + style["graphicXOffset"] = -width + (x * scale); │ │ │ │ + } else if (xUnits == "fraction") { │ │ │ │ + style["graphicXOffset"] = -width * x; │ │ │ │ + } │ │ │ │ + │ │ │ │ + var yUnits = hotSpotNode.getAttribute("yunits"); │ │ │ │ + if (yUnits == "pixels") { │ │ │ │ + style["graphicYOffset"] = -height + (y * scale) + 1; │ │ │ │ + } else if (yUnits == "insetPixels") { │ │ │ │ + style["graphicYOffset"] = -(y * scale) + 1; │ │ │ │ + } else if (yUnits == "fraction") { │ │ │ │ + style["graphicYOffset"] = -height * (1 - y) + 1; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + style["graphicWidth"] = width; │ │ │ │ + style["graphicHeight"] = height; │ │ │ │ + break; │ │ │ │ + │ │ │ │ + case "balloonstyle": │ │ │ │ + var balloonStyle = OpenLayers.Util.getXmlNodeValue( │ │ │ │ + styleTypeNode); │ │ │ │ + if (balloonStyle) { │ │ │ │ + style["balloonStyle"] = balloonStyle.replace( │ │ │ │ + this.regExes.straightBracket, "${$1}"); │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case "labelstyle": │ │ │ │ + var kmlColor = this.parseProperty(styleTypeNode, "*", "color"); │ │ │ │ + var color = this.parseKmlColor(kmlColor); │ │ │ │ + if (color) { │ │ │ │ + style["fontColor"] = color.color; │ │ │ │ + style["fontOpacity"] = color.opacity; │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + │ │ │ │ + default: │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // Some polygons have no line color, so we use the fillColor for that │ │ │ │ + if (!style["strokeColor"] && style["fillColor"]) { │ │ │ │ + style["strokeColor"] = style["fillColor"]; │ │ │ │ + } │ │ │ │ + │ │ │ │ + var id = node.getAttribute("id"); │ │ │ │ + if (id && style) { │ │ │ │ + style.id = id; │ │ │ │ + } │ │ │ │ + │ │ │ │ + return style; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * true. │ │ │ │ + * Method: parseStyleMaps │ │ │ │ + * Parses <StyleMap> nodes, but only uses the 'normal' key │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * nodes - {Array} of {DOMElement} data to read/parse. │ │ │ │ + * options - {Object} Hash of options │ │ │ │ + * │ │ │ │ */ │ │ │ │ - autoActivate: true, │ │ │ │ + parseStyleMaps: function(nodes, options) { │ │ │ │ + // Only the default or "normal" part of the StyleMap is processed now │ │ │ │ + // To do the select or "highlight" bit, we'd need to change lots more │ │ │ │ + │ │ │ │ + for (var i = 0, len = nodes.length; i < len; i++) { │ │ │ │ + var node = nodes[i]; │ │ │ │ + var pairs = this.getElementsByTagNameNS(node, "*", │ │ │ │ + "Pair"); │ │ │ │ + │ │ │ │ + var id = node.getAttribute("id"); │ │ │ │ + for (var j = 0, jlen = pairs.length; j < jlen; j++) { │ │ │ │ + var pair = pairs[j]; │ │ │ │ + // Use the shortcut in the SLD format to quickly retrieve the │ │ │ │ + // value of a node. Maybe it's good to have a method in │ │ │ │ + // Format.XML to do this │ │ │ │ + var key = this.parseProperty(pair, "*", "key"); │ │ │ │ + var styleUrl = this.parseProperty(pair, "*", "styleUrl"); │ │ │ │ + │ │ │ │ + if (styleUrl && key == "normal") { │ │ │ │ + this.styles[(options.styleBaseUrl || "") + "#" + id] = │ │ │ │ + this.styles[(options.styleBaseUrl || "") + styleUrl]; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // TODO: implement the "select" part │ │ │ │ + //if (styleUrl && key == "highlight") { │ │ │ │ + //} │ │ │ │ + │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + }, │ │ │ │ + │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.Navigation │ │ │ │ - * Create a new navigation control │ │ │ │ + * Method: parseFeatures │ │ │ │ + * Loop through all Placemark nodes and parse them. │ │ │ │ + * Will create a list of features │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * nodes - {Array} of {DOMElement} data to read/parse. │ │ │ │ + * options - {Object} Hash of options │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * the control │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - this.handlers = {}; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ + parseFeatures: function(nodes, options) { │ │ │ │ + var features = []; │ │ │ │ + for (var i = 0, len = nodes.length; i < len; i++) { │ │ │ │ + var featureNode = nodes[i]; │ │ │ │ + var feature = this.parseFeature.apply(this, [featureNode]); │ │ │ │ + if (feature) { │ │ │ │ + │ │ │ │ + // Create reference to styleUrl │ │ │ │ + if (this.extractStyles && feature.attributes && │ │ │ │ + feature.attributes.styleUrl) { │ │ │ │ + feature.style = this.getStyle(feature.attributes.styleUrl, options); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.extractStyles) { │ │ │ │ + // Make sure that <Style> nodes within a placemark are │ │ │ │ + // processed as well │ │ │ │ + var inlineStyleNode = this.getElementsByTagNameNS(featureNode, │ │ │ │ + "*", │ │ │ │ + "Style")[0]; │ │ │ │ + if (inlineStyleNode) { │ │ │ │ + var inlineStyle = this.parseStyle(inlineStyleNode); │ │ │ │ + if (inlineStyle) { │ │ │ │ + feature.style = OpenLayers.Util.extend( │ │ │ │ + feature.style, inlineStyle │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // check if gx:Track elements should be parsed │ │ │ │ + if (this.extractTracks) { │ │ │ │ + var tracks = this.getElementsByTagNameNS( │ │ │ │ + featureNode, this.namespaces.gx, "Track" │ │ │ │ + ); │ │ │ │ + if (tracks && tracks.length > 0) { │ │ │ │ + var track = tracks[0]; │ │ │ │ + var container = { │ │ │ │ + features: [], │ │ │ │ + feature: feature │ │ │ │ + }; │ │ │ │ + this.readNode(track, container); │ │ │ │ + if (container.features.length > 0) { │ │ │ │ + features.push.apply(features, container.features); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + // add feature to list of features │ │ │ │ + features.push(feature); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + throw "Bad Placemark: " + i; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // add new features to existing feature list │ │ │ │ + this.features = this.features.concat(features); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ - * The destroy method is used to perform any clean up before the control │ │ │ │ - * is dereferenced. Typically this is where event listeners are removed │ │ │ │ - * to prevent memory leaks. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ + readers: { │ │ │ │ + "kml": { │ │ │ │ + "when": function(node, container) { │ │ │ │ + container.whens.push(OpenLayers.Date.parse( │ │ │ │ + this.getChildValue(node) │ │ │ │ + )); │ │ │ │ + }, │ │ │ │ + "_trackPointAttribute": function(node, container) { │ │ │ │ + var name = node.nodeName.split(":").pop(); │ │ │ │ + container.attributes[name].push(this.getChildValue(node)); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "gx": { │ │ │ │ + "Track": function(node, container) { │ │ │ │ + var obj = { │ │ │ │ + whens: [], │ │ │ │ + points: [], │ │ │ │ + angles: [] │ │ │ │ + }; │ │ │ │ + if (this.trackAttributes) { │ │ │ │ + var name; │ │ │ │ + obj.attributes = {}; │ │ │ │ + for (var i = 0, ii = this.trackAttributes.length; i < ii; ++i) { │ │ │ │ + name = this.trackAttributes[i]; │ │ │ │ + obj.attributes[name] = []; │ │ │ │ + if (!(name in this.readers.kml)) { │ │ │ │ + this.readers.kml[name] = this.readers.kml._trackPointAttribute; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + if (obj.whens.length !== obj.points.length) { │ │ │ │ + throw new Error("gx:Track with unequal number of when (" + │ │ │ │ + obj.whens.length + ") and gx:coord (" + │ │ │ │ + obj.points.length + ") elements."); │ │ │ │ + } │ │ │ │ + var hasAngles = obj.angles.length > 0; │ │ │ │ + if (hasAngles && obj.whens.length !== obj.angles.length) { │ │ │ │ + throw new Error("gx:Track with unequal number of when (" + │ │ │ │ + obj.whens.length + ") and gx:angles (" + │ │ │ │ + obj.angles.length + ") elements."); │ │ │ │ + } │ │ │ │ + var feature, point, angles; │ │ │ │ + for (var i = 0, ii = obj.whens.length; i < ii; ++i) { │ │ │ │ + feature = container.feature.clone(); │ │ │ │ + feature.fid = container.feature.fid || container.feature.id; │ │ │ │ + point = obj.points[i]; │ │ │ │ + feature.geometry = point; │ │ │ │ + if ("z" in point) { │ │ │ │ + feature.attributes.altitude = point.z; │ │ │ │ + } │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + feature.geometry.transform( │ │ │ │ + this.externalProjection, this.internalProjection │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (this.trackAttributes) { │ │ │ │ + for (var j = 0, jj = this.trackAttributes.length; j < jj; ++j) { │ │ │ │ + var name = this.trackAttributes[j]; │ │ │ │ + feature.attributes[name] = obj.attributes[name][i]; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + feature.attributes.when = obj.whens[i]; │ │ │ │ + feature.attributes.trackId = container.feature.id; │ │ │ │ + if (hasAngles) { │ │ │ │ + angles = obj.angles[i]; │ │ │ │ + feature.attributes.heading = parseFloat(angles[0]); │ │ │ │ + feature.attributes.tilt = parseFloat(angles[1]); │ │ │ │ + feature.attributes.roll = parseFloat(angles[2]); │ │ │ │ + } │ │ │ │ + container.features.push(feature); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "coord": function(node, container) { │ │ │ │ + var str = this.getChildValue(node); │ │ │ │ + var coords = str.replace(this.regExes.trimSpace, "").split(/\s+/); │ │ │ │ + var point = new OpenLayers.Geometry.Point(coords[0], coords[1]); │ │ │ │ + if (coords.length > 2) { │ │ │ │ + point.z = parseFloat(coords[2]); │ │ │ │ + } │ │ │ │ + container.points.push(point); │ │ │ │ + }, │ │ │ │ + "angles": function(node, container) { │ │ │ │ + var str = this.getChildValue(node); │ │ │ │ + var parts = str.replace(this.regExes.trimSpace, "").split(/\s+/); │ │ │ │ + container.angles.push(parts); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.dragPan) { │ │ │ │ - this.dragPan.destroy(); │ │ │ │ + /** │ │ │ │ + * Method: parseFeature │ │ │ │ + * This function is the core of the KML parsing code in OpenLayers. │ │ │ │ + * It creates the geometries that are then attached to the returned │ │ │ │ + * feature, and calls parseAttributes() to get attribute data out. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Feature.Vector>} A vector feature. │ │ │ │ + */ │ │ │ │ + parseFeature: function(node) { │ │ │ │ + // only accept one geometry per feature - look for highest "order" │ │ │ │ + var order = ["MultiGeometry", "Polygon", "LineString", "Point"]; │ │ │ │ + var type, nodeList, geometry, parser; │ │ │ │ + for (var i = 0, len = order.length; i < len; ++i) { │ │ │ │ + type = order[i]; │ │ │ │ + this.internalns = node.namespaceURI ? │ │ │ │ + node.namespaceURI : this.kmlns; │ │ │ │ + nodeList = this.getElementsByTagNameNS(node, │ │ │ │ + this.internalns, type); │ │ │ │ + if (nodeList.length > 0) { │ │ │ │ + // only deal with first geometry of this type │ │ │ │ + var parser = this.parseGeometry[type.toLowerCase()]; │ │ │ │ + if (parser) { │ │ │ │ + geometry = parser.apply(this, [nodeList[0]]); │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry.transform(this.externalProjection, │ │ │ │ + this.internalProjection); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + throw new TypeError("Unsupported geometry type: " + type); │ │ │ │ + } │ │ │ │ + // stop looking for different geometry types │ │ │ │ + break; │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.dragPan = null; │ │ │ │ │ │ │ │ - if (this.zoomBox) { │ │ │ │ - this.zoomBox.destroy(); │ │ │ │ + // construct feature (optionally with attributes) │ │ │ │ + var attributes; │ │ │ │ + if (this.extractAttributes) { │ │ │ │ + attributes = this.parseAttributes(node); │ │ │ │ } │ │ │ │ - this.zoomBox = null; │ │ │ │ + var feature = new OpenLayers.Feature.Vector(geometry, attributes); │ │ │ │ │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.destroy(); │ │ │ │ + var fid = node.getAttribute("id") || node.getAttribute("name"); │ │ │ │ + if (fid != null) { │ │ │ │ + feature.fid = fid; │ │ │ │ } │ │ │ │ - this.pinchZoom = null; │ │ │ │ │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + return feature; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: activate │ │ │ │ + * Method: getStyle │ │ │ │ + * Retrieves a style from a style hash using styleUrl as the key │ │ │ │ + * If the styleUrl doesn't exist yet, we try to fetch it │ │ │ │ + * Internet │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * styleUrl - {String} URL of style │ │ │ │ + * options - {Object} Hash of options │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} - (reference to) Style hash │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - this.dragPan.activate(); │ │ │ │ - if (this.zoomWheelEnabled) { │ │ │ │ - this.handlers.wheel.activate(); │ │ │ │ - } │ │ │ │ - this.handlers.click.activate(); │ │ │ │ - if (this.zoomBoxEnabled) { │ │ │ │ - this.zoomBox.activate(); │ │ │ │ - } │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.activate(); │ │ │ │ + getStyle: function(styleUrl, options) { │ │ │ │ + │ │ │ │ + var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl); │ │ │ │ + │ │ │ │ + var newOptions = OpenLayers.Util.extend({}, options); │ │ │ │ + newOptions.depth++; │ │ │ │ + newOptions.styleBaseUrl = styleBaseUrl; │ │ │ │ + │ │ │ │ + // Fetch remote Style URLs (if not fetched before) │ │ │ │ + if (!this.styles[styleUrl] && │ │ │ │ + !OpenLayers.String.startsWith(styleUrl, "#") && │ │ │ │ + newOptions.depth <= this.maxDepth && │ │ │ │ + !this.fetched[styleBaseUrl]) { │ │ │ │ + │ │ │ │ + var data = this.fetchLink(styleBaseUrl); │ │ │ │ + if (data) { │ │ │ │ + this.parseData(data, newOptions); │ │ │ │ + } │ │ │ │ + │ │ │ │ } │ │ │ │ - return OpenLayers.Control.prototype.activate.apply(this, arguments); │ │ │ │ + │ │ │ │ + // return requested style │ │ │ │ + var style = OpenLayers.Util.extend({}, this.styles[styleUrl]); │ │ │ │ + return style; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: deactivate │ │ │ │ + * Property: parseGeometry │ │ │ │ + * Properties of this object are the functions that parse geometries based │ │ │ │ + * on their type. │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.deactivate(); │ │ │ │ + parseGeometry: { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: parseGeometry.point │ │ │ │ + * Given a KML node representing a point geometry, create an OpenLayers │ │ │ │ + * point geometry. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} A KML Point node. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry.Point>} A point geometry. │ │ │ │ + */ │ │ │ │ + point: function(node) { │ │ │ │ + var nodeList = this.getElementsByTagNameNS(node, this.internalns, │ │ │ │ + "coordinates"); │ │ │ │ + var coords = []; │ │ │ │ + if (nodeList.length > 0) { │ │ │ │ + var coordString = nodeList[0].firstChild.nodeValue; │ │ │ │ + coordString = coordString.replace(this.regExes.removeSpace, ""); │ │ │ │ + coords = coordString.split(","); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var point = null; │ │ │ │ + if (coords.length > 1) { │ │ │ │ + // preserve third dimension │ │ │ │ + if (coords.length == 2) { │ │ │ │ + coords[2] = null; │ │ │ │ + } │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[0], coords[1], │ │ │ │ + coords[2]); │ │ │ │ + } else { │ │ │ │ + throw "Bad coordinate string: " + coordString; │ │ │ │ + } │ │ │ │ + return point; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: parseGeometry.linestring │ │ │ │ + * Given a KML node representing a linestring geometry, create an │ │ │ │ + * OpenLayers linestring geometry. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} A KML LineString node. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry.LineString>} A linestring geometry. │ │ │ │ + */ │ │ │ │ + linestring: function(node, ring) { │ │ │ │ + var nodeList = this.getElementsByTagNameNS(node, this.internalns, │ │ │ │ + "coordinates"); │ │ │ │ + var line = null; │ │ │ │ + if (nodeList.length > 0) { │ │ │ │ + var coordString = this.getChildValue(nodeList[0]); │ │ │ │ + │ │ │ │ + coordString = coordString.replace(this.regExes.trimSpace, │ │ │ │ + ""); │ │ │ │ + coordString = coordString.replace(this.regExes.trimComma, │ │ │ │ + ","); │ │ │ │ + var pointList = coordString.split(this.regExes.splitSpace); │ │ │ │ + var numPoints = pointList.length; │ │ │ │ + var points = new Array(numPoints); │ │ │ │ + var coords, numCoords; │ │ │ │ + for (var i = 0; i < numPoints; ++i) { │ │ │ │ + coords = pointList[i].split(","); │ │ │ │ + numCoords = coords.length; │ │ │ │ + if (numCoords > 1) { │ │ │ │ + if (coords.length == 2) { │ │ │ │ + coords[2] = null; │ │ │ │ + } │ │ │ │ + points[i] = new OpenLayers.Geometry.Point(coords[0], │ │ │ │ + coords[1], │ │ │ │ + coords[2]); │ │ │ │ + } else { │ │ │ │ + throw "Bad LineString point coordinates: " + │ │ │ │ + pointList[i]; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (numPoints) { │ │ │ │ + if (ring) { │ │ │ │ + line = new OpenLayers.Geometry.LinearRing(points); │ │ │ │ + } else { │ │ │ │ + line = new OpenLayers.Geometry.LineString(points); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + throw "Bad LineString coordinates: " + coordString; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + return line; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: parseGeometry.polygon │ │ │ │ + * Given a KML node representing a polygon geometry, create an │ │ │ │ + * OpenLayers polygon geometry. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} A KML Polygon node. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry.Polygon>} A polygon geometry. │ │ │ │ + */ │ │ │ │ + polygon: function(node) { │ │ │ │ + var nodeList = this.getElementsByTagNameNS(node, this.internalns, │ │ │ │ + "LinearRing"); │ │ │ │ + var numRings = nodeList.length; │ │ │ │ + var components = new Array(numRings); │ │ │ │ + if (numRings > 0) { │ │ │ │ + // this assumes exterior ring first, inner rings after │ │ │ │ + var ring; │ │ │ │ + for (var i = 0, len = nodeList.length; i < len; ++i) { │ │ │ │ + ring = this.parseGeometry.linestring.apply(this, │ │ │ │ + [nodeList[i], true]); │ │ │ │ + if (ring) { │ │ │ │ + components[i] = ring; │ │ │ │ + } else { │ │ │ │ + throw "Bad LinearRing geometry: " + i; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.Polygon(components); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: parseGeometry.multigeometry │ │ │ │ + * Given a KML node representing a multigeometry, create an │ │ │ │ + * OpenLayers geometry collection. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} A KML MultiGeometry node. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry.Collection>} A geometry collection. │ │ │ │ + */ │ │ │ │ + multigeometry: function(node) { │ │ │ │ + var child, parser; │ │ │ │ + var parts = []; │ │ │ │ + var children = node.childNodes; │ │ │ │ + for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ + child = children[i]; │ │ │ │ + if (child.nodeType == 1) { │ │ │ │ + var type = (child.prefix) ? │ │ │ │ + child.nodeName.split(":")[1] : │ │ │ │ + child.nodeName; │ │ │ │ + var parser = this.parseGeometry[type.toLowerCase()]; │ │ │ │ + if (parser) { │ │ │ │ + parts.push(parser.apply(this, [child])); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.Collection(parts); │ │ │ │ } │ │ │ │ - this.zoomBox.deactivate(); │ │ │ │ - this.dragPan.deactivate(); │ │ │ │ - this.handlers.click.deactivate(); │ │ │ │ - this.handlers.wheel.deactivate(); │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply(this, arguments); │ │ │ │ + │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ + * Method: parseAttributes │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An attributes object. │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - // disable right mouse context menu for support of right click events │ │ │ │ - if (this.handleRightClicks) { │ │ │ │ - this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False; │ │ │ │ + parseAttributes: function(node) { │ │ │ │ + var attributes = {}; │ │ │ │ + │ │ │ │ + // Extended Data is parsed first. │ │ │ │ + var edNodes = node.getElementsByTagName("ExtendedData"); │ │ │ │ + if (edNodes.length) { │ │ │ │ + attributes = this.parseExtendedData(edNodes[0]); │ │ │ │ } │ │ │ │ │ │ │ │ - var clickCallbacks = { │ │ │ │ - 'click': this.defaultClick, │ │ │ │ - 'dblclick': this.defaultDblClick, │ │ │ │ - 'dblrightclick': this.defaultDblRightClick │ │ │ │ - }; │ │ │ │ - var clickOptions = { │ │ │ │ - 'double': true, │ │ │ │ - 'stopDouble': true │ │ │ │ - }; │ │ │ │ - this.handlers.click = new OpenLayers.Handler.Click( │ │ │ │ - this, clickCallbacks, clickOptions │ │ │ │ - ); │ │ │ │ - this.dragPan = new OpenLayers.Control.DragPan( │ │ │ │ - OpenLayers.Util.extend({ │ │ │ │ - map: this.map, │ │ │ │ - documentDrag: this.documentDrag │ │ │ │ - }, this.dragPanOptions) │ │ │ │ - ); │ │ │ │ - this.zoomBox = new OpenLayers.Control.ZoomBox({ │ │ │ │ - map: this.map, │ │ │ │ - keyMask: this.zoomBoxKeyMask │ │ │ │ - }); │ │ │ │ - this.dragPan.draw(); │ │ │ │ - this.zoomBox.draw(); │ │ │ │ - var wheelOptions = this.map.fractionalZoom ? {} : { │ │ │ │ - cumulative: false, │ │ │ │ - interval: 50, │ │ │ │ - maxDelta: 6 │ │ │ │ - }; │ │ │ │ - this.handlers.wheel = new OpenLayers.Handler.MouseWheel( │ │ │ │ - this, { │ │ │ │ - up: this.wheelUp, │ │ │ │ - down: this.wheelDown │ │ │ │ - }, │ │ │ │ - OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions) │ │ │ │ - ); │ │ │ │ - if (OpenLayers.Control.PinchZoom) { │ │ │ │ - this.pinchZoom = new OpenLayers.Control.PinchZoom( │ │ │ │ - OpenLayers.Util.extend({ │ │ │ │ - map: this.map │ │ │ │ - }, this.pinchZoomOptions)); │ │ │ │ + // assume attribute nodes are type 1 children with a type 3 or 4 child │ │ │ │ + var child, grandchildren, grandchild; │ │ │ │ + var children = node.childNodes; │ │ │ │ + │ │ │ │ + for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ + child = children[i]; │ │ │ │ + if (child.nodeType == 1) { │ │ │ │ + grandchildren = child.childNodes; │ │ │ │ + if (grandchildren.length >= 1 && grandchildren.length <= 3) { │ │ │ │ + var grandchild; │ │ │ │ + switch (grandchildren.length) { │ │ │ │ + case 1: │ │ │ │ + grandchild = grandchildren[0]; │ │ │ │ + break; │ │ │ │ + case 2: │ │ │ │ + var c1 = grandchildren[0]; │ │ │ │ + var c2 = grandchildren[1]; │ │ │ │ + grandchild = (c1.nodeType == 3 || c1.nodeType == 4) ? │ │ │ │ + c1 : c2; │ │ │ │ + break; │ │ │ │ + case 3: │ │ │ │ + default: │ │ │ │ + grandchild = grandchildren[1]; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + if (grandchild.nodeType == 3 || grandchild.nodeType == 4) { │ │ │ │ + var name = (child.prefix) ? │ │ │ │ + child.nodeName.split(":")[1] : │ │ │ │ + child.nodeName; │ │ │ │ + var value = OpenLayers.Util.getXmlNodeValue(grandchild); │ │ │ │ + if (value) { │ │ │ │ + value = value.replace(this.regExes.trimSpace, ""); │ │ │ │ + attributes[name] = value; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + return attributes; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: defaultClick │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * Method: parseExtendedData │ │ │ │ + * Parse ExtendedData from KML. Limited support for schemas/datatypes. │ │ │ │ + * See http://code.google.com/apis/kml/documentation/kmlreference.html#extendeddata │ │ │ │ + * for more information on extendeddata. │ │ │ │ */ │ │ │ │ - defaultClick: function(evt) { │ │ │ │ - if (evt.lastTouches && evt.lastTouches.length == 2) { │ │ │ │ - this.map.zoomOut(); │ │ │ │ + parseExtendedData: function(node) { │ │ │ │ + var attributes = {}; │ │ │ │ + var i, len, data, key; │ │ │ │ + var dataNodes = node.getElementsByTagName("Data"); │ │ │ │ + for (i = 0, len = dataNodes.length; i < len; i++) { │ │ │ │ + data = dataNodes[i]; │ │ │ │ + key = data.getAttribute("name"); │ │ │ │ + var ed = {}; │ │ │ │ + var valueNode = data.getElementsByTagName("value"); │ │ │ │ + if (valueNode.length) { │ │ │ │ + ed['value'] = this.getChildValue(valueNode[0]); │ │ │ │ + } │ │ │ │ + if (this.kvpAttributes) { │ │ │ │ + attributes[key] = ed['value']; │ │ │ │ + } else { │ │ │ │ + var nameNode = data.getElementsByTagName("displayName"); │ │ │ │ + if (nameNode.length) { │ │ │ │ + ed['displayName'] = this.getChildValue(nameNode[0]); │ │ │ │ + } │ │ │ │ + attributes[key] = ed; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var simpleDataNodes = node.getElementsByTagName("SimpleData"); │ │ │ │ + for (i = 0, len = simpleDataNodes.length; i < len; i++) { │ │ │ │ + var ed = {}; │ │ │ │ + data = simpleDataNodes[i]; │ │ │ │ + key = data.getAttribute("name"); │ │ │ │ + ed['value'] = this.getChildValue(data); │ │ │ │ + if (this.kvpAttributes) { │ │ │ │ + attributes[key] = ed['value']; │ │ │ │ + } else { │ │ │ │ + ed['displayName'] = key; │ │ │ │ + attributes[key] = ed; │ │ │ │ + } │ │ │ │ } │ │ │ │ + │ │ │ │ + return attributes; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: defaultDblClick │ │ │ │ - * │ │ │ │ + * Method: parseProperty │ │ │ │ + * Convenience method to find a node and return its value │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * xmlNode - {<DOMElement>} │ │ │ │ + * namespace - {String} namespace of the node to find │ │ │ │ + * tagName - {String} name of the property to parse │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The value for the requested property (defaults to null) │ │ │ │ */ │ │ │ │ - defaultDblClick: function(evt) { │ │ │ │ - this.map.zoomTo(this.map.zoom + 1, evt.xy); │ │ │ │ + parseProperty: function(xmlNode, namespace, tagName) { │ │ │ │ + var value; │ │ │ │ + var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName); │ │ │ │ + try { │ │ │ │ + value = OpenLayers.Util.getXmlNodeValue(nodeList[0]); │ │ │ │ + } catch (e) { │ │ │ │ + value = null; │ │ │ │ + } │ │ │ │ + │ │ │ │ + return value; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: defaultDblRightClick │ │ │ │ + * APIMethod: write │ │ │ │ + * Accept Feature Collection, and return a string. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} An array of features. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A KML string. │ │ │ │ */ │ │ │ │ - defaultDblRightClick: function(evt) { │ │ │ │ - this.map.zoomTo(this.map.zoom - 1, evt.xy); │ │ │ │ + write: function(features) { │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ + features = [features]; │ │ │ │ + } │ │ │ │ + var kml = this.createElementNS(this.kmlns, "kml"); │ │ │ │ + var folder = this.createFolderXML(); │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + folder.appendChild(this.createPlacemarkXML(features[i])); │ │ │ │ + } │ │ │ │ + kml.appendChild(folder); │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [kml]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: wheelChange │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * deltaZ - {Integer} │ │ │ │ + * Method: createFolderXML │ │ │ │ + * Creates and returns a KML folder node │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - wheelChange: function(evt, deltaZ) { │ │ │ │ - if (!this.map.fractionalZoom) { │ │ │ │ - deltaZ = Math.round(deltaZ); │ │ │ │ + createFolderXML: function() { │ │ │ │ + // Folder │ │ │ │ + var folder = this.createElementNS(this.kmlns, "Folder"); │ │ │ │ + │ │ │ │ + // Folder name │ │ │ │ + if (this.foldersName) { │ │ │ │ + var folderName = this.createElementNS(this.kmlns, "name"); │ │ │ │ + var folderNameText = this.createTextNode(this.foldersName); │ │ │ │ + folderName.appendChild(folderNameText); │ │ │ │ + folder.appendChild(folderName); │ │ │ │ } │ │ │ │ - var currentZoom = this.map.getZoom(), │ │ │ │ - newZoom = currentZoom + deltaZ; │ │ │ │ - newZoom = Math.max(newZoom, 0); │ │ │ │ - newZoom = Math.min(newZoom, this.map.getNumZoomLevels()); │ │ │ │ - if (newZoom === currentZoom) { │ │ │ │ - return; │ │ │ │ + │ │ │ │ + // Folder description │ │ │ │ + if (this.foldersDesc) { │ │ │ │ + var folderDesc = this.createElementNS(this.kmlns, "description"); │ │ │ │ + var folderDescText = this.createTextNode(this.foldersDesc); │ │ │ │ + folderDesc.appendChild(folderDescText); │ │ │ │ + folder.appendChild(folderDesc); │ │ │ │ } │ │ │ │ - this.map.zoomTo(newZoom, evt.xy); │ │ │ │ + │ │ │ │ + return folder; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: wheelUp │ │ │ │ - * User spun scroll wheel up │ │ │ │ + /** │ │ │ │ + * Method: createPlacemarkXML │ │ │ │ + * Creates and returns a KML placemark node representing the given feature. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * delta - {Integer} │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - wheelUp: function(evt, delta) { │ │ │ │ - this.wheelChange(evt, delta || 1); │ │ │ │ + createPlacemarkXML: function(feature) { │ │ │ │ + // Placemark name │ │ │ │ + var placemarkName = this.createElementNS(this.kmlns, "name"); │ │ │ │ + var label = (feature.style && feature.style.label) ? feature.style.label : feature.id; │ │ │ │ + var name = feature.attributes.name || label; │ │ │ │ + placemarkName.appendChild(this.createTextNode(name)); │ │ │ │ + │ │ │ │ + // Placemark description │ │ │ │ + var placemarkDesc = this.createElementNS(this.kmlns, "description"); │ │ │ │ + var desc = feature.attributes.description || this.placemarksDesc; │ │ │ │ + placemarkDesc.appendChild(this.createTextNode(desc)); │ │ │ │ + │ │ │ │ + // Placemark │ │ │ │ + var placemarkNode = this.createElementNS(this.kmlns, "Placemark"); │ │ │ │ + if (feature.fid != null) { │ │ │ │ + placemarkNode.setAttribute("id", feature.fid); │ │ │ │ + } │ │ │ │ + placemarkNode.appendChild(placemarkName); │ │ │ │ + placemarkNode.appendChild(placemarkDesc); │ │ │ │ + │ │ │ │ + // Geometry node (Point, LineString, etc. nodes) │ │ │ │ + var geometryNode = this.buildGeometryNode(feature.geometry); │ │ │ │ + placemarkNode.appendChild(geometryNode); │ │ │ │ + │ │ │ │ + // output attributes as extendedData │ │ │ │ + if (feature.attributes) { │ │ │ │ + var edNode = this.buildExtendedData(feature.attributes); │ │ │ │ + if (edNode) { │ │ │ │ + placemarkNode.appendChild(edNode); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + return placemarkNode; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: wheelDown │ │ │ │ - * User spun scroll wheel down │ │ │ │ + /** │ │ │ │ + * Method: buildGeometryNode │ │ │ │ + * Builds and returns a KML geometry node with the given geometry. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * delta - {Integer} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - wheelDown: function(evt, delta) { │ │ │ │ - this.wheelChange(evt, delta || -1); │ │ │ │ + buildGeometryNode: function(geometry) { │ │ │ │ + var className = geometry.CLASS_NAME; │ │ │ │ + var type = className.substring(className.lastIndexOf(".") + 1); │ │ │ │ + var builder = this.buildGeometry[type.toLowerCase()]; │ │ │ │ + var node = null; │ │ │ │ + if (builder) { │ │ │ │ + node = builder.apply(this, [geometry]); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: disableZoomBox │ │ │ │ + * Property: buildGeometry │ │ │ │ + * Object containing methods to do the actual geometry node building │ │ │ │ + * based on geometry type. │ │ │ │ */ │ │ │ │ - disableZoomBox: function() { │ │ │ │ - this.zoomBoxEnabled = false; │ │ │ │ - this.zoomBox.deactivate(); │ │ │ │ + buildGeometry: { │ │ │ │ + // TBD: Anybody care about namespace aliases here (these nodes have │ │ │ │ + // no prefixes)? │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: buildGeometry.point │ │ │ │ + * Given an OpenLayers point geometry, create a KML point. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry.Point>} A point geometry. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A KML point node. │ │ │ │ + */ │ │ │ │ + point: function(geometry) { │ │ │ │ + var kml = this.createElementNS(this.kmlns, "Point"); │ │ │ │ + kml.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ + return kml; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: buildGeometry.multipoint │ │ │ │ + * Given an OpenLayers multipoint geometry, create a KML │ │ │ │ + * GeometryCollection. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry.Point>} A multipoint geometry. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A KML GeometryCollection node. │ │ │ │ + */ │ │ │ │ + multipoint: function(geometry) { │ │ │ │ + return this.buildGeometry.collection.apply(this, [geometry]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: buildGeometry.linestring │ │ │ │ + * Given an OpenLayers linestring geometry, create a KML linestring. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A KML linestring node. │ │ │ │ + */ │ │ │ │ + linestring: function(geometry) { │ │ │ │ + var kml = this.createElementNS(this.kmlns, "LineString"); │ │ │ │ + kml.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ + return kml; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: buildGeometry.multilinestring │ │ │ │ + * Given an OpenLayers multilinestring geometry, create a KML │ │ │ │ + * GeometryCollection. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry.Point>} A multilinestring geometry. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A KML GeometryCollection node. │ │ │ │ + */ │ │ │ │ + multilinestring: function(geometry) { │ │ │ │ + return this.buildGeometry.collection.apply(this, [geometry]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: buildGeometry.linearring │ │ │ │ + * Given an OpenLayers linearring geometry, create a KML linearring. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A KML linearring node. │ │ │ │ + */ │ │ │ │ + linearring: function(geometry) { │ │ │ │ + var kml = this.createElementNS(this.kmlns, "LinearRing"); │ │ │ │ + kml.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ + return kml; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: buildGeometry.polygon │ │ │ │ + * Given an OpenLayers polygon geometry, create a KML polygon. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A KML polygon node. │ │ │ │ + */ │ │ │ │ + polygon: function(geometry) { │ │ │ │ + var kml = this.createElementNS(this.kmlns, "Polygon"); │ │ │ │ + var rings = geometry.components; │ │ │ │ + var ringMember, ringGeom, type; │ │ │ │ + for (var i = 0, len = rings.length; i < len; ++i) { │ │ │ │ + type = (i == 0) ? "outerBoundaryIs" : "innerBoundaryIs"; │ │ │ │ + ringMember = this.createElementNS(this.kmlns, type); │ │ │ │ + ringGeom = this.buildGeometry.linearring.apply(this, │ │ │ │ + [rings[i]]); │ │ │ │ + ringMember.appendChild(ringGeom); │ │ │ │ + kml.appendChild(ringMember); │ │ │ │ + } │ │ │ │ + return kml; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: buildGeometry.multipolygon │ │ │ │ + * Given an OpenLayers multipolygon geometry, create a KML │ │ │ │ + * GeometryCollection. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry.Point>} A multipolygon geometry. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A KML GeometryCollection node. │ │ │ │ + */ │ │ │ │ + multipolygon: function(geometry) { │ │ │ │ + return this.buildGeometry.collection.apply(this, [geometry]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: buildGeometry.collection │ │ │ │ + * Given an OpenLayers geometry collection, create a KML MultiGeometry. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry.Collection>} A geometry collection. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A KML MultiGeometry node. │ │ │ │ + */ │ │ │ │ + collection: function(geometry) { │ │ │ │ + var kml = this.createElementNS(this.kmlns, "MultiGeometry"); │ │ │ │ + var child; │ │ │ │ + for (var i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ + child = this.buildGeometryNode.apply(this, │ │ │ │ + [geometry.components[i]]); │ │ │ │ + if (child) { │ │ │ │ + kml.appendChild(child); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return kml; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: enableZoomBox │ │ │ │ + * Method: buildCoordinatesNode │ │ │ │ + * Builds and returns the KML coordinates node with the given geometry │ │ │ │ + * <coordinates>...</coordinates> │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - enableZoomBox: function() { │ │ │ │ - this.zoomBoxEnabled = true; │ │ │ │ - if (this.active) { │ │ │ │ - this.zoomBox.activate(); │ │ │ │ + buildCoordinatesNode: function(geometry) { │ │ │ │ + var coordinatesNode = this.createElementNS(this.kmlns, "coordinates"); │ │ │ │ + │ │ │ │ + var path; │ │ │ │ + var points = geometry.components; │ │ │ │ + if (points) { │ │ │ │ + // LineString or LinearRing │ │ │ │ + var point; │ │ │ │ + var numPoints = points.length; │ │ │ │ + var parts = new Array(numPoints); │ │ │ │ + for (var i = 0; i < numPoints; ++i) { │ │ │ │ + point = points[i]; │ │ │ │ + parts[i] = this.buildCoordinates(point); │ │ │ │ + } │ │ │ │ + path = parts.join(" "); │ │ │ │ + } else { │ │ │ │ + // Point │ │ │ │ + path = this.buildCoordinates(geometry); │ │ │ │ } │ │ │ │ + │ │ │ │ + var txtNode = this.createTextNode(path); │ │ │ │ + coordinatesNode.appendChild(txtNode); │ │ │ │ + │ │ │ │ + return coordinatesNode; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: disableZoomWheel │ │ │ │ + * Method: buildCoordinates │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} │ │ │ │ + * │ │ │ │ + * Returns │ │ │ │ + * {String} a coordinate pair │ │ │ │ */ │ │ │ │ - │ │ │ │ - disableZoomWheel: function() { │ │ │ │ - this.zoomWheelEnabled = false; │ │ │ │ - this.handlers.wheel.deactivate(); │ │ │ │ + buildCoordinates: function(point) { │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + point = point.clone(); │ │ │ │ + point.transform(this.internalProjection, │ │ │ │ + this.externalProjection); │ │ │ │ + } │ │ │ │ + return point.x + "," + point.y; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: enableZoomWheel │ │ │ │ + * Method: buildExtendedData │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * attributes - {Object} │ │ │ │ + * │ │ │ │ + * Returns │ │ │ │ + * {DOMElement} A KML ExtendedData node or {null} if no attributes. │ │ │ │ */ │ │ │ │ - │ │ │ │ - enableZoomWheel: function() { │ │ │ │ - this.zoomWheelEnabled = true; │ │ │ │ - if (this.active) { │ │ │ │ - this.handlers.wheel.activate(); │ │ │ │ + buildExtendedData: function(attributes) { │ │ │ │ + var extendedData = this.createElementNS(this.kmlns, "ExtendedData"); │ │ │ │ + for (var attributeName in attributes) { │ │ │ │ + // empty, name, description, styleUrl attributes ignored │ │ │ │ + if (attributes[attributeName] && attributeName != "name" && attributeName != "description" && attributeName != "styleUrl") { │ │ │ │ + var data = this.createElementNS(this.kmlns, "Data"); │ │ │ │ + data.setAttribute("name", attributeName); │ │ │ │ + var value = this.createElementNS(this.kmlns, "value"); │ │ │ │ + if (typeof attributes[attributeName] == "object") { │ │ │ │ + // cater for object attributes with 'value' properties │ │ │ │ + // other object properties will output an empty node │ │ │ │ + if (attributes[attributeName].value) { │ │ │ │ + value.appendChild(this.createTextNode(attributes[attributeName].value)); │ │ │ │ + } │ │ │ │ + if (attributes[attributeName].displayName) { │ │ │ │ + var displayName = this.createElementNS(this.kmlns, "displayName"); │ │ │ │ + // displayName always written as CDATA │ │ │ │ + displayName.appendChild(this.getXMLDoc().createCDATASection(attributes[attributeName].displayName)); │ │ │ │ + data.appendChild(displayName); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + value.appendChild(this.createTextNode(attributes[attributeName])); │ │ │ │ + } │ │ │ │ + data.appendChild(value); │ │ │ │ + extendedData.appendChild(data); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (this.isSimpleContent(extendedData)) { │ │ │ │ + return null; │ │ │ │ + } else { │ │ │ │ + return extendedData; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Navigation" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.KML" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/NavToolbar.js │ │ │ │ + OpenLayers/Format/SOSCapabilities.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control/Panel.js │ │ │ │ - * @requires OpenLayers/Control/Navigation.js │ │ │ │ - * @requires OpenLayers/Control/ZoomBox.js │ │ │ │ + * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.NavToolbar │ │ │ │ - * This Toolbar is an alternative to the Navigation control that displays │ │ │ │ - * the state of the control, and provides a UI for changing state to │ │ │ │ - * use the zoomBox via a Panel control. │ │ │ │ - * │ │ │ │ - * If you wish to change the properties of the Navigation control used │ │ │ │ - * in the NavToolbar, see: │ │ │ │ - * http://trac.openlayers.org/wiki/Toolbars#SubclassingNavToolbar │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Format.SOSCapabilities │ │ │ │ + * Read SOS Capabilities. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control.Panel> │ │ │ │ + * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.NavToolbar = OpenLayers.Class(OpenLayers.Control.Panel, { │ │ │ │ +OpenLayers.Format.SOSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.NavToolbar │ │ │ │ - * Add our two mousedefaults controls. │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ + * {String} Version number to assume if none found. Default is "1.0.0". │ │ │ │ + */ │ │ │ │ + defaultVersion: "1.0.0", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.SOSCapabilities │ │ │ │ + * Create a new parser for SOS Capabilities. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be used │ │ │ │ - * to extend the control. │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); │ │ │ │ - this.addControls([ │ │ │ │ - new OpenLayers.Control.Navigation(), │ │ │ │ - new OpenLayers.Control.ZoomBox() │ │ │ │ - ]); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ - * calls the default draw, and then activates mouse defaults. │ │ │ │ + * APIMethod: read │ │ │ │ + * Read capabilities data from a string, and return information about │ │ │ │ + * the service (offering and observedProperty mostly). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} Info about the SOS │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments); │ │ │ │ - if (this.defaultControl === null) { │ │ │ │ - this.defaultControl = this.controls[0]; │ │ │ │ - } │ │ │ │ - return div; │ │ │ │ - }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.NavToolbar" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.SOSCapabilities" │ │ │ │ + │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/TransformFeature.js │ │ │ │ + OpenLayers/Format/SLD.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Control/DragFeature.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ - * @requires OpenLayers/Geometry/LineString.js │ │ │ │ - * @requires OpenLayers/Geometry/Point.js │ │ │ │ + * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ + * @requires OpenLayers/Style.js │ │ │ │ + * @requires OpenLayers/Rule.js │ │ │ │ + * @requires OpenLayers/Filter/FeatureId.js │ │ │ │ + * @requires OpenLayers/Filter/Logical.js │ │ │ │ + * @requires OpenLayers/Filter/Comparison.js │ │ │ │ + * @requires OpenLayers/Filter/Spatial.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.TransformFeature │ │ │ │ - * Control to transform features with a standard transformation box. │ │ │ │ - * │ │ │ │ - * Inherits From: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * Class: OpenLayers.Format.SLD │ │ │ │ + * Read/Write SLD. Create a new instance with the <OpenLayers.Format.SLD> │ │ │ │ + * constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.TransformFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * beforesetfeature - Triggered before a feature is set for │ │ │ │ - * tranformation. The feature will not be set if a listener returns │ │ │ │ - * false. Listeners receive a *feature* property, with the feature │ │ │ │ - * that will be set for transformation. Listeners are allowed to │ │ │ │ - * set the control's *scale*, *ratio* and *rotation* properties, │ │ │ │ - * which will set the initial scale, ratio and rotation of the │ │ │ │ - * feature, like the <setFeature> method's initialParams argument. │ │ │ │ - * setfeature - Triggered when a feature is set for tranformation. │ │ │ │ - * Listeners receive a *feature* property, with the feature that │ │ │ │ - * is now set for transformation. │ │ │ │ - * beforetransform - Triggered while dragging, before a feature is │ │ │ │ - * transformed. The feature will not be transformed if a listener │ │ │ │ - * returns false (but the box still will). Listeners receive one or │ │ │ │ - * more of *center*, *scale*, *ratio* and *rotation*. The *center* │ │ │ │ - * property is an <OpenLayers.Geometry.Point> object with the new │ │ │ │ - * center of the transformed feature, the others are Floats with the │ │ │ │ - * scale, ratio or rotation change since the last transformation. │ │ │ │ - * transform - Triggered while dragging, when a feature is transformed. │ │ │ │ - * Listeners receive an event object with one or more of *center*, │ │ │ │ - * scale*, *ratio* and *rotation*. The *center* property is an │ │ │ │ - * <OpenLayers.Geometry.Point> object with the new center of the │ │ │ │ - * transformed feature, the others are Floats with the scale, ratio │ │ │ │ - * or rotation change of the feature since the last transformation. │ │ │ │ - * transformcomplete - Triggered after dragging. Listeners receive │ │ │ │ - * an event object with the transformed *feature*. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: geometryTypes │ │ │ │ - * {Array(String)} To restrict transformation to a limited set of geometry │ │ │ │ - * types, send a list of strings corresponding to the geometry class │ │ │ │ - * names. │ │ │ │ - */ │ │ │ │ - geometryTypes: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: layer │ │ │ │ - * {<OpenLayers.Layer.Vector>} │ │ │ │ - */ │ │ │ │ - layer: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: preserveAspectRatio │ │ │ │ - * {Boolean} set to true to not change the feature's aspect ratio. │ │ │ │ - */ │ │ │ │ - preserveAspectRatio: false, │ │ │ │ +OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: rotate │ │ │ │ - * {Boolean} set to false if rotation should be disabled. Default is true. │ │ │ │ - * To be passed with the constructor or set when the control is not │ │ │ │ - * active. │ │ │ │ + * APIProperty: profile │ │ │ │ + * {String} If provided, use a custom profile. │ │ │ │ + * │ │ │ │ + * Currently supported profiles: │ │ │ │ + * - GeoServer - parses GeoServer vendor specific capabilities for SLD. │ │ │ │ */ │ │ │ │ - rotate: true, │ │ │ │ + profile: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: feature │ │ │ │ - * {<OpenLayers.Feature.Vector>} Feature currently available for │ │ │ │ - * transformation. Read-only, use <setFeature> to set it manually. │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ + * {String} Version number to assume if none found. Default is "1.0.0". │ │ │ │ */ │ │ │ │ - feature: null, │ │ │ │ + defaultVersion: "1.0.0", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: renderIntent │ │ │ │ - * {String|Object} Render intent for the transformation box and │ │ │ │ - * handles. A symbolizer object can also be provided here. │ │ │ │ + * APIProperty: stringifyOutput │ │ │ │ + * {Boolean} If true, write will return a string otherwise a DOMElement. │ │ │ │ + * Default is true. │ │ │ │ */ │ │ │ │ - renderIntent: "temporary", │ │ │ │ + stringifyOutput: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: rotationHandleSymbolizer │ │ │ │ - * {Object|String} Optional. A custom symbolizer for the rotation handles. │ │ │ │ - * A render intent can also be provided here. Defaults to │ │ │ │ - * (code) │ │ │ │ - * { │ │ │ │ - * stroke: false, │ │ │ │ - * pointRadius: 10, │ │ │ │ - * fillOpacity: 0, │ │ │ │ - * cursor: "pointer" │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ + * APIProperty: namedLayersAsArray │ │ │ │ + * {Boolean} Generate a namedLayers array. If false, the namedLayers │ │ │ │ + * property value will be an object keyed by layer name. Default is │ │ │ │ + * false. │ │ │ │ */ │ │ │ │ - rotationHandleSymbolizer: null, │ │ │ │ + namedLayersAsArray: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: box │ │ │ │ - * {<OpenLayers.Feature.Vector>} The transformation box rectangle. │ │ │ │ - * Read-only. │ │ │ │ + * APIMethod: write │ │ │ │ + * Write a SLD document given a list of styles. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * sld - {Object} An object representing the SLD. │ │ │ │ + * options - {Object} Optional configuration object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} An SLD document string. │ │ │ │ */ │ │ │ │ - box: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: center │ │ │ │ - * {<OpenLayers.Geometry.Point>} The center of the feature bounds. │ │ │ │ - * Read-only. │ │ │ │ + * APIMethod: read │ │ │ │ + * Read and SLD doc and return an object representing the SLD. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String | DOMElement} Data to read. │ │ │ │ + * options - {Object} Options for the reader. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An object representing the SLD. │ │ │ │ */ │ │ │ │ - center: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: scale │ │ │ │ - * {Float} The scale of the feature, relative to the scale the time the │ │ │ │ - * feature was set. Read-only, except for *beforesetfeature* │ │ │ │ - * listeners. │ │ │ │ - */ │ │ │ │ - scale: 1, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.SLD" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/OSM.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: ratio │ │ │ │ - * {Float} The ratio of the feature relative to the ratio the time the │ │ │ │ - * feature was set. Read-only, except for *beforesetfeature* │ │ │ │ - * listeners. │ │ │ │ - */ │ │ │ │ - ratio: 1, │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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: rotation │ │ │ │ - * {Integer} the current rotation angle of the box. Read-only, except for │ │ │ │ - * *beforesetfeature* listeners. │ │ │ │ - */ │ │ │ │ - rotation: 0, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Geometry/Point.js │ │ │ │ + * @requires OpenLayers/Geometry/LineString.js │ │ │ │ + * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ + * @requires OpenLayers/Projection.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: handles │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} The 8 handles currently available │ │ │ │ - * for scaling/resizing. Numbered counterclockwise, starting from the │ │ │ │ - * southwest corner. Read-only. │ │ │ │ - */ │ │ │ │ - handles: null, │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.OSM │ │ │ │ + * OSM parser. Create a new instance with the │ │ │ │ + * <OpenLayers.Format.OSM> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.OSM = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: rotationHandles │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} The 4 rotation handles currently │ │ │ │ - * available for rotating. Numbered counterclockwise, starting from │ │ │ │ - * the southwest corner. Read-only. │ │ │ │ + * APIProperty: checkTags │ │ │ │ + * {Boolean} Should tags be checked to determine whether something │ │ │ │ + * should be treated as a seperate node. Will slow down parsing. │ │ │ │ + * Default is false. │ │ │ │ */ │ │ │ │ - rotationHandles: null, │ │ │ │ + checkTags: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: dragControl │ │ │ │ - * {<OpenLayers.Control.DragFeature>} │ │ │ │ + * Property: interestingTagsExclude │ │ │ │ + * {Array} List of tags to exclude from 'interesting' checks on nodes. │ │ │ │ + * Must be set when creating the format. Will only be used if checkTags │ │ │ │ + * is set. │ │ │ │ */ │ │ │ │ - dragControl: null, │ │ │ │ + interestingTagsExclude: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: irregular │ │ │ │ - * {Boolean} Make scaling/resizing work irregularly. If true then │ │ │ │ - * dragging a handle causes the feature to resize in the direction │ │ │ │ - * of movement. If false then the feature resizes symetrically │ │ │ │ - * about it's center. │ │ │ │ + * APIProperty: areaTags │ │ │ │ + * {Array} List of tags indicating that something is an area. │ │ │ │ + * Must be set when creating the format. Will only be used if │ │ │ │ + * checkTags is true. │ │ │ │ */ │ │ │ │ - irregular: false, │ │ │ │ + areaTags: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.TransformFeature │ │ │ │ - * Create a new transform feature control. │ │ │ │ + * Constructor: OpenLayers.Format.OSM │ │ │ │ + * Create a new parser for OSM. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that │ │ │ │ - * will be transformed. │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * control. │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - initialize: function(layer, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + initialize: function(options) { │ │ │ │ + var layer_defaults = { │ │ │ │ + 'interestingTagsExclude': ['source', 'source_ref', │ │ │ │ + 'source:ref', 'history', 'attribution', 'created_by' │ │ │ │ + ], │ │ │ │ + 'areaTags': ['area', 'building', 'leisure', 'tourism', 'ruins', │ │ │ │ + 'historic', 'landuse', 'military', 'natural', 'sport' │ │ │ │ + ] │ │ │ │ + }; │ │ │ │ │ │ │ │ - this.layer = layer; │ │ │ │ + layer_defaults = OpenLayers.Util.extend(layer_defaults, options); │ │ │ │ │ │ │ │ - if (!this.rotationHandleSymbolizer) { │ │ │ │ - this.rotationHandleSymbolizer = { │ │ │ │ - stroke: false, │ │ │ │ - pointRadius: 10, │ │ │ │ - fillOpacity: 0, │ │ │ │ - cursor: "pointer" │ │ │ │ - }; │ │ │ │ + var interesting = {}; │ │ │ │ + for (var i = 0; i < layer_defaults.interestingTagsExclude.length; i++) { │ │ │ │ + interesting[layer_defaults.interestingTagsExclude[i]] = true; │ │ │ │ } │ │ │ │ + layer_defaults.interestingTagsExclude = interesting; │ │ │ │ │ │ │ │ - this.createBox(); │ │ │ │ - this.createControl(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Activates the control. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = false; │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.dragControl.activate(); │ │ │ │ - this.layer.addFeatures([this.box]); │ │ │ │ - this.rotate && this.layer.addFeatures(this.rotationHandles); │ │ │ │ - this.layer.addFeatures(this.handles); │ │ │ │ - activated = true; │ │ │ │ + var area = {}; │ │ │ │ + for (var i = 0; i < layer_defaults.areaTags.length; i++) { │ │ │ │ + area[layer_defaults.areaTags[i]] = true; │ │ │ │ } │ │ │ │ - return activated; │ │ │ │ - }, │ │ │ │ + layer_defaults.areaTags = area; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivates the control. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.layer.removeFeatures(this.handles); │ │ │ │ - this.rotate && this.layer.removeFeatures(this.rotationHandles); │ │ │ │ - this.layer.removeFeatures([this.box]); │ │ │ │ - this.dragControl.deactivate(); │ │ │ │ - deactivated = true; │ │ │ │ - } │ │ │ │ - return deactivated; │ │ │ │ - }, │ │ │ │ + // OSM coordinates are always in longlat WGS84 │ │ │ │ + this.externalProjection = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - setMap: function(map) { │ │ │ │ - this.dragControl.setMap(map); │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [layer_defaults]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setFeature │ │ │ │ - * Place the transformation box on a feature and start transforming it. │ │ │ │ - * If the control is not active, it will be activated. │ │ │ │ - * │ │ │ │ + * APIMethod: read │ │ │ │ + * Return a list of features from a OSM doc │ │ │ │ + │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * initialParams - {Object} Initial values for rotation, scale or ratio. │ │ │ │ - * Setting a rotation value here will cause the transformation box to │ │ │ │ - * start rotated. Setting a scale or ratio will not affect the │ │ │ │ - * transormation box, but applications may use this to keep track of │ │ │ │ - * scale and ratio of a feature across multiple transforms. │ │ │ │ + * doc - {Element} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * Array({<OpenLayers.Feature.Vector>}) │ │ │ │ */ │ │ │ │ - setFeature: function(feature, initialParams) { │ │ │ │ - initialParams = OpenLayers.Util.applyDefaults(initialParams, { │ │ │ │ - rotation: 0, │ │ │ │ - scale: 1, │ │ │ │ - ratio: 1 │ │ │ │ - }); │ │ │ │ + read: function(doc) { │ │ │ │ + if (typeof doc == "string") { │ │ │ │ + doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); │ │ │ │ + } │ │ │ │ │ │ │ │ - var oldRotation = this.rotation; │ │ │ │ - var oldCenter = this.center; │ │ │ │ - OpenLayers.Util.extend(this, initialParams); │ │ │ │ + var nodes = this.getNodes(doc); │ │ │ │ + var ways = this.getWays(doc); │ │ │ │ │ │ │ │ - var cont = this.events.triggerEvent("beforesetfeature", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (cont === false) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ + // Geoms will contain at least ways.length entries. │ │ │ │ + var feat_list = new Array(ways.length); │ │ │ │ │ │ │ │ - this.feature = feature; │ │ │ │ - this.activate(); │ │ │ │ + for (var i = 0; i < ways.length; i++) { │ │ │ │ + // We know the minimal of this one ahead of time. (Could be -1 │ │ │ │ + // due to areas/polygons) │ │ │ │ + var point_list = new Array(ways[i].nodes.length); │ │ │ │ │ │ │ │ - this._setfeature = true; │ │ │ │ + var poly = this.isWayArea(ways[i]) ? 1 : 0; │ │ │ │ + for (var j = 0; j < ways[i].nodes.length; j++) { │ │ │ │ + var node = nodes[ways[i].nodes[j]]; │ │ │ │ │ │ │ │ - var featureBounds = this.feature.geometry.getBounds(); │ │ │ │ - this.box.move(featureBounds.getCenterLonLat()); │ │ │ │ - this.box.geometry.rotate(-oldRotation, oldCenter); │ │ │ │ - this._angle = 0; │ │ │ │ + var point = new OpenLayers.Geometry.Point(node.lon, node.lat); │ │ │ │ │ │ │ │ - var ll; │ │ │ │ - if (this.rotation) { │ │ │ │ - var geom = feature.geometry.clone(); │ │ │ │ - geom.rotate(-this.rotation, this.center); │ │ │ │ - var box = new OpenLayers.Feature.Vector( │ │ │ │ - geom.getBounds().toGeometry()); │ │ │ │ - box.geometry.rotate(this.rotation, this.center); │ │ │ │ - this.box.geometry.rotate(this.rotation, this.center); │ │ │ │ - this.box.move(box.geometry.getBounds().getCenterLonLat()); │ │ │ │ - var llGeom = box.geometry.components[0].components[0]; │ │ │ │ - ll = llGeom.getBounds().getCenterLonLat(); │ │ │ │ - } else { │ │ │ │ - ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom); │ │ │ │ + // Since OSM is topological, we stash the node ID internally. │ │ │ │ + point.osm_id = parseInt(ways[i].nodes[j]); │ │ │ │ + point_list[j] = point; │ │ │ │ + │ │ │ │ + // We don't display nodes if they're used inside other │ │ │ │ + // elements. │ │ │ │ + node.used = true; │ │ │ │ + } │ │ │ │ + var geometry = null; │ │ │ │ + if (poly) { │ │ │ │ + geometry = new OpenLayers.Geometry.Polygon( │ │ │ │ + new OpenLayers.Geometry.LinearRing(point_list)); │ │ │ │ + } else { │ │ │ │ + geometry = new OpenLayers.Geometry.LineString(point_list); │ │ │ │ + } │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry.transform(this.externalProjection, │ │ │ │ + this.internalProjection); │ │ │ │ + } │ │ │ │ + var feat = new OpenLayers.Feature.Vector(geometry, │ │ │ │ + ways[i].tags); │ │ │ │ + feat.osm_id = parseInt(ways[i].id); │ │ │ │ + feat.fid = "way." + feat.osm_id; │ │ │ │ + feat_list[i] = feat; │ │ │ │ } │ │ │ │ - this.handles[0].move(ll); │ │ │ │ + for (var node_id in nodes) { │ │ │ │ + var node = nodes[node_id]; │ │ │ │ + if (!node.used || this.checkTags) { │ │ │ │ + var tags = null; │ │ │ │ │ │ │ │ - delete this._setfeature; │ │ │ │ + if (this.checkTags) { │ │ │ │ + var result = this.getTags(node.node, true); │ │ │ │ + if (node.used && !result[1]) { │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ + tags = result[0]; │ │ │ │ + } else { │ │ │ │ + tags = this.getTags(node.node); │ │ │ │ + } │ │ │ │ │ │ │ │ - this.events.triggerEvent("setfeature", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ + var feat = new OpenLayers.Feature.Vector( │ │ │ │ + new OpenLayers.Geometry.Point(node['lon'], node['lat']), │ │ │ │ + tags); │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + feat.geometry.transform(this.externalProjection, │ │ │ │ + this.internalProjection); │ │ │ │ + } │ │ │ │ + feat.osm_id = parseInt(node_id); │ │ │ │ + feat.fid = "node." + feat.osm_id; │ │ │ │ + feat_list.push(feat); │ │ │ │ + } │ │ │ │ + // Memory cleanup │ │ │ │ + node.node = null; │ │ │ │ + } │ │ │ │ + return feat_list; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: unsetFeature │ │ │ │ - * Remove the transformation box off any feature. │ │ │ │ - * If the control is active, it will be deactivated first. │ │ │ │ + * Method: getNodes │ │ │ │ + * Return the node items from a doc. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * doc - {DOMElement} node to parse tags from │ │ │ │ */ │ │ │ │ - unsetFeature: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.deactivate(); │ │ │ │ - } else { │ │ │ │ - this.feature = null; │ │ │ │ - this.rotation = 0; │ │ │ │ - this.scale = 1; │ │ │ │ - this.ratio = 1; │ │ │ │ + getNodes: function(doc) { │ │ │ │ + var node_list = doc.getElementsByTagName("node"); │ │ │ │ + var nodes = {}; │ │ │ │ + for (var i = 0; i < node_list.length; i++) { │ │ │ │ + var node = node_list[i]; │ │ │ │ + var id = node.getAttribute("id"); │ │ │ │ + nodes[id] = { │ │ │ │ + 'lat': node.getAttribute("lat"), │ │ │ │ + 'lon': node.getAttribute("lon"), │ │ │ │ + 'node': node │ │ │ │ + }; │ │ │ │ } │ │ │ │ + return nodes; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createBox │ │ │ │ - * Creates the box with all handles and transformation handles. │ │ │ │ + * Method: getWays │ │ │ │ + * Return the way items from a doc. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * doc - {DOMElement} node to parse tags from │ │ │ │ */ │ │ │ │ - createBox: function() { │ │ │ │ - var control = this; │ │ │ │ + getWays: function(doc) { │ │ │ │ + var way_list = doc.getElementsByTagName("way"); │ │ │ │ + var return_ways = []; │ │ │ │ + for (var i = 0; i < way_list.length; i++) { │ │ │ │ + var way = way_list[i]; │ │ │ │ + var way_object = { │ │ │ │ + id: way.getAttribute("id") │ │ │ │ + }; │ │ │ │ │ │ │ │ - this.center = new OpenLayers.Geometry.Point(0, 0); │ │ │ │ - this.box = new OpenLayers.Feature.Vector( │ │ │ │ - new OpenLayers.Geometry.LineString([ │ │ │ │ - new OpenLayers.Geometry.Point(-1, -1), │ │ │ │ - new OpenLayers.Geometry.Point(0, -1), │ │ │ │ - new OpenLayers.Geometry.Point(1, -1), │ │ │ │ - new OpenLayers.Geometry.Point(1, 0), │ │ │ │ - new OpenLayers.Geometry.Point(1, 1), │ │ │ │ - new OpenLayers.Geometry.Point(0, 1), │ │ │ │ - new OpenLayers.Geometry.Point(-1, 1), │ │ │ │ - new OpenLayers.Geometry.Point(-1, 0), │ │ │ │ - new OpenLayers.Geometry.Point(-1, -1) │ │ │ │ - ]), null, │ │ │ │ - typeof this.renderIntent == "string" ? null : this.renderIntent │ │ │ │ - ); │ │ │ │ + way_object.tags = this.getTags(way); │ │ │ │ │ │ │ │ - // Override for box move - make sure that the center gets updated │ │ │ │ - this.box.geometry.move = function(x, y) { │ │ │ │ - control._moving = true; │ │ │ │ - OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments); │ │ │ │ - control.center.move(x, y); │ │ │ │ - delete control._moving; │ │ │ │ - }; │ │ │ │ + var node_list = way.getElementsByTagName("nd"); │ │ │ │ │ │ │ │ - // Overrides for vertex move, resize and rotate - make sure that │ │ │ │ - // handle and rotationHandle geometries are also moved, resized and │ │ │ │ - // rotated. │ │ │ │ - var vertexMoveFn = function(x, y) { │ │ │ │ - OpenLayers.Geometry.Point.prototype.move.apply(this, arguments); │ │ │ │ - this._rotationHandle && this._rotationHandle.geometry.move(x, y); │ │ │ │ - this._handle.geometry.move(x, y); │ │ │ │ - }; │ │ │ │ - var vertexResizeFn = function(scale, center, ratio) { │ │ │ │ - OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments); │ │ │ │ - this._rotationHandle && this._rotationHandle.geometry.resize( │ │ │ │ - scale, center, ratio); │ │ │ │ - this._handle.geometry.resize(scale, center, ratio); │ │ │ │ - }; │ │ │ │ - var vertexRotateFn = function(angle, center) { │ │ │ │ - OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments); │ │ │ │ - this._rotationHandle && this._rotationHandle.geometry.rotate( │ │ │ │ - angle, center); │ │ │ │ - this._handle.geometry.rotate(angle, center); │ │ │ │ - }; │ │ │ │ + way_object.nodes = new Array(node_list.length); │ │ │ │ │ │ │ │ - // Override for handle move - make sure that the box and other handles │ │ │ │ - // are updated, and finally transform the feature. │ │ │ │ - var handleMoveFn = function(x, y) { │ │ │ │ - var oldX = this.x, │ │ │ │ - oldY = this.y; │ │ │ │ - OpenLayers.Geometry.Point.prototype.move.call(this, x, y); │ │ │ │ - if (control._moving) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - var evt = control.dragControl.handlers.drag.evt; │ │ │ │ - var preserveAspectRatio = !control._setfeature && │ │ │ │ - control.preserveAspectRatio; │ │ │ │ - var reshape = !preserveAspectRatio && !(evt && evt.shiftKey); │ │ │ │ - var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY); │ │ │ │ - var centerGeometry = control.center; │ │ │ │ - this.rotate(-control.rotation, centerGeometry); │ │ │ │ - oldGeom.rotate(-control.rotation, centerGeometry); │ │ │ │ - var dx1 = this.x - centerGeometry.x; │ │ │ │ - var dy1 = this.y - centerGeometry.y; │ │ │ │ - var dx0 = dx1 - (this.x - oldGeom.x); │ │ │ │ - var dy0 = dy1 - (this.y - oldGeom.y); │ │ │ │ - if (control.irregular && !control._setfeature) { │ │ │ │ - dx1 -= (this.x - oldGeom.x) / 2; │ │ │ │ - dy1 -= (this.y - oldGeom.y) / 2; │ │ │ │ - } │ │ │ │ - this.x = oldX; │ │ │ │ - this.y = oldY; │ │ │ │ - var scale, ratio = 1; │ │ │ │ - if (reshape) { │ │ │ │ - scale = Math.abs(dy0) < 0.00001 ? 1 : dy1 / dy0; │ │ │ │ - ratio = (Math.abs(dx0) < 0.00001 ? 1 : (dx1 / dx0)) / scale; │ │ │ │ - } else { │ │ │ │ - var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0)); │ │ │ │ - var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1)); │ │ │ │ - scale = l1 / l0; │ │ │ │ + for (var j = 0; j < node_list.length; j++) { │ │ │ │ + way_object.nodes[j] = node_list[j].getAttribute("ref"); │ │ │ │ } │ │ │ │ + return_ways.push(way_object); │ │ │ │ + } │ │ │ │ + return return_ways; │ │ │ │ │ │ │ │ - // rotate the box to 0 before resizing - saves us some │ │ │ │ - // calculations and is inexpensive because we don't drawFeature. │ │ │ │ - control._moving = true; │ │ │ │ - control.box.geometry.rotate(-control.rotation, centerGeometry); │ │ │ │ - delete control._moving; │ │ │ │ + }, │ │ │ │ │ │ │ │ - control.box.geometry.resize(scale, centerGeometry, ratio); │ │ │ │ - control.box.geometry.rotate(control.rotation, centerGeometry); │ │ │ │ - control.transformFeature({ │ │ │ │ - scale: scale, │ │ │ │ - ratio: ratio │ │ │ │ - }); │ │ │ │ - if (control.irregular && !control._setfeature) { │ │ │ │ - var newCenter = centerGeometry.clone(); │ │ │ │ - newCenter.x += Math.abs(oldX - centerGeometry.x) < 0.00001 ? 0 : (this.x - oldX); │ │ │ │ - newCenter.y += Math.abs(oldY - centerGeometry.y) < 0.00001 ? 0 : (this.y - oldY); │ │ │ │ - control.box.geometry.move(this.x - oldX, this.y - oldY); │ │ │ │ - control.transformFeature({ │ │ │ │ - center: newCenter │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * Method: getTags │ │ │ │ + * Return the tags list attached to a specific DOM element. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * dom_node - {DOMElement} node to parse tags from │ │ │ │ + * interesting_tags - {Boolean} whether the return from this function should │ │ │ │ + * return a boolean indicating that it has 'interesting tags' -- │ │ │ │ + * tags like attribution and source are ignored. (To change the list │ │ │ │ + * of tags, see interestingTagsExclude) │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * tags - {Object} hash of tags │ │ │ │ + * interesting - {Boolean} if interesting_tags is passed, returns │ │ │ │ + * whether there are any interesting tags on this element. │ │ │ │ + */ │ │ │ │ + getTags: function(dom_node, interesting_tags) { │ │ │ │ + var tag_list = dom_node.getElementsByTagName("tag"); │ │ │ │ + var tags = {}; │ │ │ │ + var interesting = false; │ │ │ │ + for (var j = 0; j < tag_list.length; j++) { │ │ │ │ + var key = tag_list[j].getAttribute("k"); │ │ │ │ + tags[key] = tag_list[j].getAttribute("v"); │ │ │ │ + if (interesting_tags) { │ │ │ │ + if (!this.interestingTagsExclude[key]) { │ │ │ │ + interesting = true; │ │ │ │ + } │ │ │ │ } │ │ │ │ - }; │ │ │ │ + } │ │ │ │ + return interesting_tags ? [tags, interesting] : tags; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Override for rotation handle move - make sure that the box and │ │ │ │ - // other handles are updated, and finally transform the feature. │ │ │ │ - var rotationHandleMoveFn = function(x, y) { │ │ │ │ - var oldX = this.x, │ │ │ │ - oldY = this.y; │ │ │ │ - OpenLayers.Geometry.Point.prototype.move.call(this, x, y); │ │ │ │ - if (control._moving) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - var evt = control.dragControl.handlers.drag.evt; │ │ │ │ - var constrain = (evt && evt.shiftKey) ? 45 : 1; │ │ │ │ - var centerGeometry = control.center; │ │ │ │ - var dx1 = this.x - centerGeometry.x; │ │ │ │ - var dy1 = this.y - centerGeometry.y; │ │ │ │ - var dx0 = dx1 - x; │ │ │ │ - var dy0 = dy1 - y; │ │ │ │ - this.x = oldX; │ │ │ │ - this.y = oldY; │ │ │ │ - var a0 = Math.atan2(dy0, dx0); │ │ │ │ - var a1 = Math.atan2(dy1, dx1); │ │ │ │ - var angle = a1 - a0; │ │ │ │ - angle *= 180 / Math.PI; │ │ │ │ - control._angle = (control._angle + angle) % 360; │ │ │ │ - var diff = control.rotation % constrain; │ │ │ │ - if (Math.abs(control._angle) >= constrain || diff !== 0) { │ │ │ │ - angle = Math.round(control._angle / constrain) * constrain - │ │ │ │ - diff; │ │ │ │ - control._angle = 0; │ │ │ │ - control.box.geometry.rotate(angle, centerGeometry); │ │ │ │ - control.transformFeature({ │ │ │ │ - rotation: angle │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Method: isWayArea │ │ │ │ + * Given a way object from getWays, check whether the tags and geometry │ │ │ │ + * indicate something is an area. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} │ │ │ │ + */ │ │ │ │ + isWayArea: function(way) { │ │ │ │ + var poly_shaped = false; │ │ │ │ + var poly_tags = false; │ │ │ │ │ │ │ │ - var handles = new Array(8); │ │ │ │ - var rotationHandles = new Array(4); │ │ │ │ - var geom, handle, rotationHandle; │ │ │ │ - var positions = ["sw", "s", "se", "e", "ne", "n", "nw", "w"]; │ │ │ │ - for (var i = 0; i < 8; ++i) { │ │ │ │ - geom = this.box.geometry.components[i]; │ │ │ │ - handle = new OpenLayers.Feature.Vector(geom.clone(), { │ │ │ │ - role: positions[i] + "-resize" │ │ │ │ - }, typeof this.renderIntent == "string" ? null : │ │ │ │ - this.renderIntent); │ │ │ │ - if (i % 2 == 0) { │ │ │ │ - rotationHandle = new OpenLayers.Feature.Vector(geom.clone(), { │ │ │ │ - role: positions[i] + "-rotate" │ │ │ │ - }, typeof this.rotationHandleSymbolizer == "string" ? │ │ │ │ - null : this.rotationHandleSymbolizer); │ │ │ │ - rotationHandle.geometry.move = rotationHandleMoveFn; │ │ │ │ - geom._rotationHandle = rotationHandle; │ │ │ │ - rotationHandles[i / 2] = rotationHandle; │ │ │ │ + if (way.nodes[0] == way.nodes[way.nodes.length - 1]) { │ │ │ │ + poly_shaped = true; │ │ │ │ + } │ │ │ │ + if (this.checkTags) { │ │ │ │ + for (var key in way.tags) { │ │ │ │ + if (this.areaTags[key]) { │ │ │ │ + poly_tags = true; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ } │ │ │ │ - geom.move = vertexMoveFn; │ │ │ │ - geom.resize = vertexResizeFn; │ │ │ │ - geom.rotate = vertexRotateFn; │ │ │ │ - handle.geometry.move = handleMoveFn; │ │ │ │ - geom._handle = handle; │ │ │ │ - handles[i] = handle; │ │ │ │ } │ │ │ │ - │ │ │ │ - this.rotationHandles = rotationHandles; │ │ │ │ - this.handles = handles; │ │ │ │ + return poly_shaped && (this.checkTags ? poly_tags : true); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createControl │ │ │ │ - * Creates a DragFeature control for this control. │ │ │ │ + * APIMethod: write │ │ │ │ + * Takes a list of features, returns a serialized OSM format file for use │ │ │ │ + * in tools like JOSM. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ */ │ │ │ │ - createControl: function() { │ │ │ │ - var control = this; │ │ │ │ - this.dragControl = new OpenLayers.Control.DragFeature(this.layer, { │ │ │ │ - documentDrag: true, │ │ │ │ - // avoid moving the feature itself - move the box instead │ │ │ │ - moveFeature: function(pixel) { │ │ │ │ - if (this.feature === control.feature) { │ │ │ │ - this.feature = control.box; │ │ │ │ - } │ │ │ │ - OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this, │ │ │ │ - arguments); │ │ │ │ - }, │ │ │ │ - // transform while dragging │ │ │ │ - onDrag: function(feature, pixel) { │ │ │ │ - if (feature === control.box) { │ │ │ │ - control.transformFeature({ │ │ │ │ - center: control.center │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - // set a new feature │ │ │ │ - onStart: function(feature, pixel) { │ │ │ │ - var eligible = !control.geometryTypes || │ │ │ │ - OpenLayers.Util.indexOf(control.geometryTypes, │ │ │ │ - feature.geometry.CLASS_NAME) !== -1; │ │ │ │ - var i = OpenLayers.Util.indexOf(control.handles, feature); │ │ │ │ - i += OpenLayers.Util.indexOf(control.rotationHandles, │ │ │ │ - feature); │ │ │ │ - if (feature !== control.feature && feature !== control.box && │ │ │ │ - i == -2 && eligible) { │ │ │ │ - control.setFeature(feature); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - onComplete: function(feature, pixel) { │ │ │ │ - control.events.triggerEvent("transformcomplete", { │ │ │ │ - feature: control.feature │ │ │ │ - }); │ │ │ │ + write: function(features) { │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ + features = [features]; │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.osm_id = 1; │ │ │ │ + this.created_nodes = {}; │ │ │ │ + var root_node = this.createElementNS(null, "osm"); │ │ │ │ + root_node.setAttribute("version", "0.5"); │ │ │ │ + root_node.setAttribute("generator", "OpenLayers " + OpenLayers.VERSION_NUMBER); │ │ │ │ + │ │ │ │ + // Loop backwards, because the deserializer puts nodes last, and │ │ │ │ + // we want them first if possible │ │ │ │ + for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ + var nodes = this.createFeatureNodes(features[i]); │ │ │ │ + for (var j = 0; j < nodes.length; j++) { │ │ │ │ + root_node.appendChild(nodes[j]); │ │ │ │ } │ │ │ │ - }); │ │ │ │ + } │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [root_node]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawHandles │ │ │ │ - * Draws the handles to match the box. │ │ │ │ + * Method: createFeatureNodes │ │ │ │ + * Takes a feature, returns a list of nodes from size 0->n. │ │ │ │ + * Will include all pieces of the serialization that are required which │ │ │ │ + * have not already been created. Calls out to createXML based on geometry │ │ │ │ + * type. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - drawHandles: function() { │ │ │ │ - var layer = this.layer; │ │ │ │ - for (var i = 0; i < 8; ++i) { │ │ │ │ - if (this.rotate && i % 2 === 0) { │ │ │ │ - layer.drawFeature(this.rotationHandles[i / 2], │ │ │ │ - this.rotationHandleSymbolizer); │ │ │ │ - } │ │ │ │ - layer.drawFeature(this.handles[i], this.renderIntent); │ │ │ │ + createFeatureNodes: function(feature) { │ │ │ │ + var nodes = []; │ │ │ │ + var className = feature.geometry.CLASS_NAME; │ │ │ │ + var type = className.substring(className.lastIndexOf(".") + 1); │ │ │ │ + type = type.toLowerCase(); │ │ │ │ + var builder = this.createXML[type]; │ │ │ │ + if (builder) { │ │ │ │ + nodes = builder.apply(this, [feature]); │ │ │ │ } │ │ │ │ + return nodes; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: transformFeature │ │ │ │ - * Transforms the feature. │ │ │ │ - * │ │ │ │ + * Method: createXML │ │ │ │ + * Takes a feature, returns a list of nodes from size 0->n. │ │ │ │ + * Will include all pieces of the serialization that are required which │ │ │ │ + * have not already been created. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * mods - {Object} An object with optional scale, ratio, rotation and │ │ │ │ - * center properties. │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - transformFeature: function(mods) { │ │ │ │ - if (!this._setfeature) { │ │ │ │ - this.scale *= (mods.scale || 1); │ │ │ │ - this.ratio *= (mods.ratio || 1); │ │ │ │ - var oldRotation = this.rotation; │ │ │ │ - this.rotation = (this.rotation + (mods.rotation || 0)) % 360; │ │ │ │ + createXML: { │ │ │ │ + 'point': function(point) { │ │ │ │ + var id = null; │ │ │ │ + var geometry = point.geometry ? point.geometry : point; │ │ │ │ │ │ │ │ - if (this.events.triggerEvent("beforetransform", mods) !== false) { │ │ │ │ - var feature = this.feature; │ │ │ │ - var geom = feature.geometry; │ │ │ │ - var center = this.center; │ │ │ │ - geom.rotate(-oldRotation, center); │ │ │ │ - if (mods.scale || mods.ratio) { │ │ │ │ - geom.resize(mods.scale, center, mods.ratio); │ │ │ │ - } else if (mods.center) { │ │ │ │ - feature.move(mods.center.getBounds().getCenterLonLat()); │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry = geometry.clone(); │ │ │ │ + geometry.transform(this.internalProjection, │ │ │ │ + this.externalProjection); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var already_exists = false; // We don't return anything if the node │ │ │ │ + // has already been created │ │ │ │ + if (point.osm_id) { │ │ │ │ + id = point.osm_id; │ │ │ │ + if (this.created_nodes[id]) { │ │ │ │ + already_exists = true; │ │ │ │ } │ │ │ │ - geom.rotate(this.rotation, center); │ │ │ │ - this.layer.drawFeature(feature); │ │ │ │ - feature.toState(OpenLayers.State.UPDATE); │ │ │ │ - this.events.triggerEvent("transform", mods); │ │ │ │ + } else { │ │ │ │ + id = -this.osm_id; │ │ │ │ + this.osm_id++; │ │ │ │ + } │ │ │ │ + if (already_exists) { │ │ │ │ + node = this.created_nodes[id]; │ │ │ │ + } else { │ │ │ │ + var node = this.createElementNS(null, "node"); │ │ │ │ + } │ │ │ │ + this.created_nodes[id] = node; │ │ │ │ + node.setAttribute("id", id); │ │ │ │ + node.setAttribute("lon", geometry.x); │ │ │ │ + node.setAttribute("lat", geometry.y); │ │ │ │ + if (point.attributes) { │ │ │ │ + this.serializeTags(point, node); │ │ │ │ + } │ │ │ │ + this.setState(point, node); │ │ │ │ + return already_exists ? [] : [node]; │ │ │ │ + }, │ │ │ │ + linestring: function(feature) { │ │ │ │ + var id; │ │ │ │ + var nodes = []; │ │ │ │ + var geometry = feature.geometry; │ │ │ │ + if (feature.osm_id) { │ │ │ │ + id = feature.osm_id; │ │ │ │ + } else { │ │ │ │ + id = -this.osm_id; │ │ │ │ + this.osm_id++; │ │ │ │ + } │ │ │ │ + var way = this.createElementNS(null, "way"); │ │ │ │ + way.setAttribute("id", id); │ │ │ │ + for (var i = 0; i < geometry.components.length; i++) { │ │ │ │ + var node = this.createXML['point'].apply(this, [geometry.components[i]]); │ │ │ │ + if (node.length) { │ │ │ │ + node = node[0]; │ │ │ │ + var node_ref = node.getAttribute("id"); │ │ │ │ + nodes.push(node); │ │ │ │ + } else { │ │ │ │ + node_ref = geometry.components[i].osm_id; │ │ │ │ + node = this.created_nodes[node_ref]; │ │ │ │ + } │ │ │ │ + this.setState(feature, node); │ │ │ │ + var nd_dom = this.createElementNS(null, "nd"); │ │ │ │ + nd_dom.setAttribute("ref", node_ref); │ │ │ │ + way.appendChild(nd_dom); │ │ │ │ } │ │ │ │ + this.serializeTags(feature, way); │ │ │ │ + nodes.push(way); │ │ │ │ + │ │ │ │ + return nodes; │ │ │ │ + }, │ │ │ │ + polygon: function(feature) { │ │ │ │ + var attrs = OpenLayers.Util.extend({ │ │ │ │ + 'area': 'yes' │ │ │ │ + }, feature.attributes); │ │ │ │ + var feat = new OpenLayers.Feature.Vector(feature.geometry.components[0], attrs); │ │ │ │ + feat.osm_id = feature.osm_id; │ │ │ │ + return this.createXML['linestring'].apply(this, [feat]); │ │ │ │ } │ │ │ │ - this.layer.drawFeature(this.box, this.renderIntent); │ │ │ │ - this.drawHandles(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Take care of things that are not handled in superclass. │ │ │ │ + * Method: serializeTags │ │ │ │ + * Given a feature, serialize the attributes onto the given node. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * node - {DOMNode} │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - var geom; │ │ │ │ - for (var i = 0; i < 8; ++i) { │ │ │ │ - geom = this.box.geometry.components[i]; │ │ │ │ - geom._handle.destroy(); │ │ │ │ - geom._handle = null; │ │ │ │ - geom._rotationHandle && geom._rotationHandle.destroy(); │ │ │ │ - geom._rotationHandle = null; │ │ │ │ + serializeTags: function(feature, node) { │ │ │ │ + for (var key in feature.attributes) { │ │ │ │ + var tag = this.createElementNS(null, "tag"); │ │ │ │ + tag.setAttribute("k", key); │ │ │ │ + tag.setAttribute("v", feature.attributes[key]); │ │ │ │ + node.appendChild(tag); │ │ │ │ } │ │ │ │ - this.center = null; │ │ │ │ - this.feature = null; │ │ │ │ - this.handles = null; │ │ │ │ - this.rotationHandleSymbolizer = null; │ │ │ │ - this.rotationHandles = null; │ │ │ │ - this.box.destroy(); │ │ │ │ - this.box = null; │ │ │ │ - this.layer = null; │ │ │ │ - this.dragControl.destroy(); │ │ │ │ - this.dragControl = null; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.TransformFeature" │ │ │ │ + /** │ │ │ │ + * Method: setState │ │ │ │ + * OpenStreetMap has a convention that 'state' is stored for modification or deletion. │ │ │ │ + * This allows the file to be uploaded via JOSM or the bulk uploader tool. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * node - {DOMNode} │ │ │ │ + */ │ │ │ │ + setState: function(feature, node) { │ │ │ │ + if (feature.state) { │ │ │ │ + var state = null; │ │ │ │ + switch (feature.state) { │ │ │ │ + case OpenLayers.State.UPDATE: │ │ │ │ + state = "modify"; │ │ │ │ + case OpenLayers.State.DELETE: │ │ │ │ + state = "delete"; │ │ │ │ + } │ │ │ │ + if (state) { │ │ │ │ + node.setAttribute("action", state); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.OSM" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/DrawFeature.js │ │ │ │ + OpenLayers/Format/WMC.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Format/Context.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.DrawFeature │ │ │ │ - * The DrawFeature control draws point, line or polygon features on a vector │ │ │ │ - * layer when active. │ │ │ │ + * Class: OpenLayers.Format.WMC │ │ │ │ + * Read and write Web Map Context documents. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format.Context> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ +OpenLayers.Format.WMC = OpenLayers.Class(OpenLayers.Format.Context, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: layer │ │ │ │ - * {<OpenLayers.Layer.Vector>} │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ + * {String} Version number to assume if none found. Default is "1.1.0". │ │ │ │ */ │ │ │ │ - layer: null, │ │ │ │ + defaultVersion: "1.1.0", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: callbacks │ │ │ │ - * {Object} The functions that are sent to the handler for callback │ │ │ │ - */ │ │ │ │ - callbacks: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ + * Constructor: OpenLayers.Format.WMC │ │ │ │ + * Create a new parser for Web Map Context documents. │ │ │ │ * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * featureadded - Triggered when a feature is added │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: multi │ │ │ │ - * {Boolean} Cast features to multi-part geometries before passing to the │ │ │ │ - * layer. Default is false. │ │ │ │ + * Method: layerToContext │ │ │ │ + * Create a layer context object given a wms layer object. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layer - {<OpenLayers.Layer.WMS>} The layer. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} A layer context object. │ │ │ │ */ │ │ │ │ - multi: false, │ │ │ │ + layerToContext: function(layer) { │ │ │ │ + var parser = this.getParser(); │ │ │ │ + var layerContext = { │ │ │ │ + queryable: layer.queryable, │ │ │ │ + visibility: layer.visibility, │ │ │ │ + name: layer.params["LAYERS"], │ │ │ │ + title: layer.name, │ │ │ │ + "abstract": layer.metadata["abstract"], │ │ │ │ + dataURL: layer.metadata.dataURL, │ │ │ │ + metadataURL: layer.metadataURL, │ │ │ │ + server: { │ │ │ │ + version: layer.params["VERSION"], │ │ │ │ + url: layer.url │ │ │ │ + }, │ │ │ │ + maxExtent: layer.maxExtent, │ │ │ │ + transparent: layer.params["TRANSPARENT"], │ │ │ │ + numZoomLevels: layer.numZoomLevels, │ │ │ │ + units: layer.units, │ │ │ │ + isBaseLayer: layer.isBaseLayer, │ │ │ │ + opacity: layer.opacity == 1 ? undefined : layer.opacity, │ │ │ │ + displayInLayerSwitcher: layer.displayInLayerSwitcher, │ │ │ │ + singleTile: layer.singleTile, │ │ │ │ + tileSize: (layer.singleTile || !layer.tileSize) ? │ │ │ │ + undefined : { │ │ │ │ + width: layer.tileSize.w, │ │ │ │ + height: layer.tileSize.h │ │ │ │ + }, │ │ │ │ + minScale: (layer.options.resolutions || │ │ │ │ + layer.options.scales || │ │ │ │ + layer.options.maxResolution || │ │ │ │ + layer.options.minScale) ? │ │ │ │ + layer.minScale : undefined, │ │ │ │ + maxScale: (layer.options.resolutions || │ │ │ │ + layer.options.scales || │ │ │ │ + layer.options.minResolution || │ │ │ │ + layer.options.maxScale) ? │ │ │ │ + layer.maxScale : undefined, │ │ │ │ + formats: [], │ │ │ │ + styles: [], │ │ │ │ + srs: layer.srs, │ │ │ │ + dimensions: layer.dimensions │ │ │ │ + }; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: featureAdded │ │ │ │ - * {Function} Called after each feature is added │ │ │ │ - */ │ │ │ │ - featureAdded: function() {}, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: handlerOptions │ │ │ │ - * {Object} Used to set non-default properties on the control's handler │ │ │ │ - */ │ │ │ │ + if (layer.metadata.servertitle) { │ │ │ │ + layerContext.server.title = layer.metadata.servertitle; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.DrawFeature │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} │ │ │ │ - * handler - {<OpenLayers.Handler>} │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ - initialize: function(layer, handler, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.callbacks = OpenLayers.Util.extend({ │ │ │ │ - done: this.drawFeature, │ │ │ │ - modify: function(vertex, feature) { │ │ │ │ - this.layer.events.triggerEvent( │ │ │ │ - "sketchmodified", { │ │ │ │ - vertex: vertex, │ │ │ │ - feature: feature │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - create: function(vertex, feature) { │ │ │ │ - this.layer.events.triggerEvent( │ │ │ │ - "sketchstarted", { │ │ │ │ - vertex: vertex, │ │ │ │ - feature: feature │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - this.callbacks │ │ │ │ - ); │ │ │ │ - this.layer = layer; │ │ │ │ - this.handlerOptions = this.handlerOptions || {}; │ │ │ │ - this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( │ │ │ │ - this.handlerOptions.layerOptions, { │ │ │ │ - renderers: layer.renderers, │ │ │ │ - rendererOptions: layer.rendererOptions │ │ │ │ + if (layer.metadata.formats && layer.metadata.formats.length > 0) { │ │ │ │ + for (var i = 0, len = layer.metadata.formats.length; i < len; i++) { │ │ │ │ + var format = layer.metadata.formats[i]; │ │ │ │ + layerContext.formats.push({ │ │ │ │ + value: format.value, │ │ │ │ + current: (format.value == layer.params["FORMAT"]) │ │ │ │ + }); │ │ │ │ } │ │ │ │ - ); │ │ │ │ - if (!("multi" in this.handlerOptions)) { │ │ │ │ - this.handlerOptions.multi = this.multi; │ │ │ │ - } │ │ │ │ - var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary; │ │ │ │ - if (sketchStyle) { │ │ │ │ - this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( │ │ │ │ - this.handlerOptions.layerOptions, { │ │ │ │ - styleMap: new OpenLayers.StyleMap({ │ │ │ │ - "default": sketchStyle │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - ); │ │ │ │ + } else { │ │ │ │ + layerContext.formats.push({ │ │ │ │ + value: layer.params["FORMAT"], │ │ │ │ + current: true │ │ │ │ + }); │ │ │ │ } │ │ │ │ - this.handler = new handler(this, this.callbacks, this.handlerOptions); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawFeature │ │ │ │ - */ │ │ │ │ - drawFeature: function(geometry) { │ │ │ │ - var feature = new OpenLayers.Feature.Vector(geometry); │ │ │ │ - var proceed = this.layer.events.triggerEvent( │ │ │ │ - "sketchcomplete", { │ │ │ │ - feature: feature │ │ │ │ + if (layer.metadata.styles && layer.metadata.styles.length > 0) { │ │ │ │ + for (var i = 0, len = layer.metadata.styles.length; i < len; i++) { │ │ │ │ + var style = layer.metadata.styles[i]; │ │ │ │ + if ((style.href == layer.params["SLD"]) || │ │ │ │ + (style.body == layer.params["SLD_BODY"]) || │ │ │ │ + (style.name == layer.params["STYLES"])) { │ │ │ │ + style.current = true; │ │ │ │ + } else { │ │ │ │ + style.current = false; │ │ │ │ + } │ │ │ │ + layerContext.styles.push(style); │ │ │ │ } │ │ │ │ - ); │ │ │ │ - if (proceed !== false) { │ │ │ │ - feature.state = OpenLayers.State.INSERT; │ │ │ │ - this.layer.addFeatures([feature]); │ │ │ │ - this.featureAdded(feature); │ │ │ │ - this.events.triggerEvent("featureadded", { │ │ │ │ - feature: feature │ │ │ │ + } else { │ │ │ │ + layerContext.styles.push({ │ │ │ │ + href: layer.params["SLD"], │ │ │ │ + body: layer.params["SLD_BODY"], │ │ │ │ + name: layer.params["STYLES"] || parser.defaultStyleName, │ │ │ │ + title: parser.defaultStyleTitle, │ │ │ │ + current: true │ │ │ │ }); │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: insertXY │ │ │ │ - * Insert a point in the current sketch given x & y coordinates. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * x - {Number} The x-coordinate of the point. │ │ │ │ - * y - {Number} The y-coordinate of the point. │ │ │ │ - */ │ │ │ │ - insertXY: function(x, y) { │ │ │ │ - if (this.handler && this.handler.line) { │ │ │ │ - this.handler.insertXY(x, y); │ │ │ │ - } │ │ │ │ + return layerContext; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: insertDeltaXY │ │ │ │ - * Insert a point given offsets from the previously inserted point. │ │ │ │ + * Method: toContext │ │ │ │ + * Create a context object free from layer given a map or a │ │ │ │ + * context object. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * dx - {Number} The x-coordinate offset of the point. │ │ │ │ - * dy - {Number} The y-coordinate offset of the point. │ │ │ │ - */ │ │ │ │ - insertDeltaXY: function(dx, dy) { │ │ │ │ - if (this.handler && this.handler.line) { │ │ │ │ - this.handler.insertDeltaXY(dx, dy); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: insertDirectionLength │ │ │ │ - * Insert a point in the current sketch given a direction and a length. │ │ │ │ + * obj - {<OpenLayers.Map> | Object} The map or context. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * direction - {Number} Degrees clockwise from the positive x-axis. │ │ │ │ - * length - {Number} Distance from the previously drawn point. │ │ │ │ + * Returns: │ │ │ │ + * {Object} A context object. │ │ │ │ */ │ │ │ │ - insertDirectionLength: function(direction, length) { │ │ │ │ - if (this.handler && this.handler.line) { │ │ │ │ - this.handler.insertDirectionLength(direction, length); │ │ │ │ + toContext: function(obj) { │ │ │ │ + var context = {}; │ │ │ │ + var layers = obj.layers; │ │ │ │ + if (obj.CLASS_NAME == "OpenLayers.Map") { │ │ │ │ + var metadata = obj.metadata || {}; │ │ │ │ + context.size = obj.getSize(); │ │ │ │ + context.bounds = obj.getExtent(); │ │ │ │ + context.projection = obj.projection; │ │ │ │ + context.title = obj.title; │ │ │ │ + context.keywords = metadata.keywords; │ │ │ │ + context["abstract"] = metadata["abstract"]; │ │ │ │ + context.logo = metadata.logo; │ │ │ │ + context.descriptionURL = metadata.descriptionURL; │ │ │ │ + context.contactInformation = metadata.contactInformation; │ │ │ │ + context.maxExtent = obj.maxExtent; │ │ │ │ + } else { │ │ │ │ + // copy all obj properties except the "layers" property │ │ │ │ + OpenLayers.Util.applyDefaults(context, obj); │ │ │ │ + if (context.layers != undefined) { │ │ │ │ + delete(context.layers); │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: insertDeflectionLength │ │ │ │ - * Insert a point in the current sketch given a deflection and a length. │ │ │ │ - * The deflection should be degrees clockwise from the previously │ │ │ │ - * digitized segment. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * deflection - {Number} Degrees clockwise from the previous segment. │ │ │ │ - * length - {Number} Distance from the previously drawn point. │ │ │ │ - */ │ │ │ │ - insertDeflectionLength: function(deflection, length) { │ │ │ │ - if (this.handler && this.handler.line) { │ │ │ │ - this.handler.insertDeflectionLength(deflection, length); │ │ │ │ + if (context.layersContext == undefined) { │ │ │ │ + context.layersContext = []; │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: undo │ │ │ │ - * Remove the most recently added point in the current sketch geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} An edit was undone. │ │ │ │ - */ │ │ │ │ - undo: function() { │ │ │ │ - return this.handler.undo && this.handler.undo(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: redo │ │ │ │ - * Reinsert the most recently removed point resulting from an <undo> call. │ │ │ │ - * The undo stack is deleted whenever a point is added by other means. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} An edit was redone. │ │ │ │ - */ │ │ │ │ - redo: function() { │ │ │ │ - return this.handler.redo && this.handler.redo(); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: finishSketch │ │ │ │ - * Finishes the sketch without including the currently drawn point. │ │ │ │ - * This method can be called to terminate drawing programmatically │ │ │ │ - * instead of waiting for the user to end the sketch. │ │ │ │ - */ │ │ │ │ - finishSketch: function() { │ │ │ │ - this.handler.finishGeometry(); │ │ │ │ + // let's convert layers into layersContext object (if any) │ │ │ │ + if (layers != undefined && OpenLayers.Util.isArray(layers)) { │ │ │ │ + for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ + var layer = layers[i]; │ │ │ │ + if (layer instanceof OpenLayers.Layer.WMS) { │ │ │ │ + context.layersContext.push(this.layerToContext(layer)); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return context; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: cancel │ │ │ │ - * Cancel the current sketch. This removes the current sketch and keeps │ │ │ │ - * the drawing control active. │ │ │ │ - */ │ │ │ │ - cancel: function() { │ │ │ │ - this.handler.cancel(); │ │ │ │ - }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMC" │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.DrawFeature" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/ZoomOut.js │ │ │ │ + OpenLayers/Format/WFSCapabilities.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control/Button.js │ │ │ │ + * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.ZoomOut │ │ │ │ - * The ZoomOut control is a button to decrease the zoom level of a map. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Format.WFSCapabilities │ │ │ │ + * Read WFS Capabilities. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.ZoomOut = OpenLayers.Class(OpenLayers.Control.Button, { │ │ │ │ +OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: trigger │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ + * {String} Version number to assume if none found. Default is "1.1.0". │ │ │ │ */ │ │ │ │ - trigger: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.zoomOut(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + defaultVersion: "1.1.0", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WFSCapabilities │ │ │ │ + * Create a new parser for WFS capabilities. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read capabilities data from a string, and return a list of layers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} List of named layers. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFSCapabilities" │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ZoomOut" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/UTFGrid.js │ │ │ │ + OpenLayers/Format/QueryStringFilter.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Handler/Hover.js │ │ │ │ - * @requires OpenLayers/Handler/Click.js │ │ │ │ + * @requires OpenLayers/Console.js │ │ │ │ + * @requires OpenLayers/Format.js │ │ │ │ + * @requires OpenLayers/Filter/Spatial.js │ │ │ │ + * @requires OpenLayers/Filter/Comparison.js │ │ │ │ + * @requires OpenLayers/Filter/Logical.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.UTFGrid │ │ │ │ - * │ │ │ │ - * This Control provides behavior associated with UTFGrid Layers. │ │ │ │ - * These 'hit grids' provide underlying feature attributes without │ │ │ │ - * calling the server (again). This control allows Mousemove, Hovering │ │ │ │ - * and Click events to trigger callbacks that use the attributes in │ │ │ │ - * whatever way you need. │ │ │ │ - * │ │ │ │ - * The most common example may be a UTFGrid layer containing feature │ │ │ │ - * attributes that are displayed in a div as you mouseover. │ │ │ │ - * │ │ │ │ - * Example Code: │ │ │ │ - * │ │ │ │ - * (start code) │ │ │ │ - * var world_utfgrid = new OpenLayers.Layer.UTFGrid( │ │ │ │ - * 'UTFGrid Layer', │ │ │ │ - * "http://tiles/world_utfgrid/${z}/${x}/${y}.json" │ │ │ │ - * ); │ │ │ │ - * map.addLayer(world_utfgrid); │ │ │ │ - * │ │ │ │ - * var control = new OpenLayers.Control.UTFGrid({ │ │ │ │ - * layers: [world_utfgrid], │ │ │ │ - * handlerMode: 'move', │ │ │ │ - * callback: function(infoLookup) { │ │ │ │ - * // do something with returned data │ │ │ │ - * │ │ │ │ - * } │ │ │ │ - * }) │ │ │ │ - * (end code) │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Format.QueryStringFilter │ │ │ │ + * Parser for reading a query string and creating a simple filter. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.UTFGrid = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * true. │ │ │ │ - */ │ │ │ │ - autoActivate: true, │ │ │ │ +OpenLayers.Format.QueryStringFilter = (function() { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: Layers │ │ │ │ - * List of layers to consider. Must be Layer.UTFGrids │ │ │ │ - * `null` is the default indicating all UTFGrid Layers are queried. │ │ │ │ - * {Array} <OpenLayers.Layer.UTFGrid> │ │ │ │ - */ │ │ │ │ - layers: null, │ │ │ │ - │ │ │ │ - /* Property: defaultHandlerOptions │ │ │ │ - * The default opts passed to the handler constructors │ │ │ │ - */ │ │ │ │ - defaultHandlerOptions: { │ │ │ │ - 'delay': 300, │ │ │ │ - 'pixelTolerance': 4, │ │ │ │ - 'stopMove': false, │ │ │ │ - 'single': true, │ │ │ │ - 'double': false, │ │ │ │ - 'stopSingle': false, │ │ │ │ - 'stopDouble': false │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /* APIProperty: handlerMode │ │ │ │ - * Defaults to 'click'. Can be 'hover' or 'move'. │ │ │ │ + * Map the OpenLayers.Filter.Comparison types to the operation strings of │ │ │ │ + * the protocol. │ │ │ │ */ │ │ │ │ - handlerMode: 'click', │ │ │ │ + var cmpToStr = {}; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = "eq"; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = "ne"; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = "lt"; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = "lte"; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = "gt"; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = "gte"; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.LIKE] = "ilike"; │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setHandler │ │ │ │ - * sets this.handlerMode and calls resetHandler() │ │ │ │ + * Function: regex2value │ │ │ │ + * Convert the value from a regular expression string to a LIKE/ILIKE │ │ │ │ + * string known to the web service. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * hm - {String} Handler Mode string; 'click', 'hover' or 'move'. │ │ │ │ - */ │ │ │ │ - setHandler: function(hm) { │ │ │ │ - this.handlerMode = hm; │ │ │ │ - this.resetHandler(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: resetHandler │ │ │ │ - * Deactivates the old hanlder and creates a new │ │ │ │ - * <OpenLayers.Handler> based on the mode specified in │ │ │ │ - * this.handlerMode │ │ │ │ + * value - {String} The regex string. │ │ │ │ * │ │ │ │ + * Returns: │ │ │ │ + * {String} The converted string. │ │ │ │ */ │ │ │ │ - resetHandler: function() { │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.deactivate(); │ │ │ │ - this.handler.destroy(); │ │ │ │ - this.handler = null; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.handlerMode == 'hover') { │ │ │ │ - // Handle this event on hover │ │ │ │ - this.handler = new OpenLayers.Handler.Hover( │ │ │ │ - this, { │ │ │ │ - 'pause': this.handleEvent, │ │ │ │ - 'move': this.reset │ │ │ │ - }, │ │ │ │ - this.handlerOptions │ │ │ │ - ); │ │ │ │ - } else if (this.handlerMode == 'click') { │ │ │ │ - // Handle this event on click │ │ │ │ - this.handler = new OpenLayers.Handler.Click( │ │ │ │ - this, { │ │ │ │ - 'click': this.handleEvent │ │ │ │ - }, this.handlerOptions │ │ │ │ - ); │ │ │ │ - } else if (this.handlerMode == 'move') { │ │ │ │ - this.handler = new OpenLayers.Handler.Hover( │ │ │ │ - this, │ │ │ │ - // Handle this event while hovering OR moving │ │ │ │ - { │ │ │ │ - 'pause': this.handleEvent, │ │ │ │ - 'move': this.handleEvent │ │ │ │ - }, │ │ │ │ - this.handlerOptions │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (this.handler) { │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + function regex2value(value) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: <OpenLayers.Control.UTFGrid> │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - options.handlerOptions = options.handlerOptions || this.defaultHandlerOptions; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.resetHandler(); │ │ │ │ - }, │ │ │ │ + // highly sensitive!! Do not change this without running the │ │ │ │ + // Protocol/HTTP.html unit tests │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: handleEvent │ │ │ │ - * Internal method called when specified event is triggered. │ │ │ │ - * │ │ │ │ - * This method does several things: │ │ │ │ - * │ │ │ │ - * Gets the lonLat of the event. │ │ │ │ - * │ │ │ │ - * Loops through the appropriate hit grid layers and gathers the attributes. │ │ │ │ - * │ │ │ │ - * Passes the attributes to the callback │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ - */ │ │ │ │ - handleEvent: function(evt) { │ │ │ │ - if (evt == null) { │ │ │ │ - this.reset(); │ │ │ │ - return; │ │ │ │ - } │ │ │ │ + // convert % to \% │ │ │ │ + value = value.replace(/%/g, "\\%"); │ │ │ │ │ │ │ │ - var lonLat = this.map.getLonLatFromPixel(evt.xy); │ │ │ │ - if (!lonLat) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ + // convert \\. to \\_ (\\.* occurences converted later) │ │ │ │ + value = value.replace(/\\\\\.(\*)?/g, function($0, $1) { │ │ │ │ + return $1 ? $0 : "\\\\_"; │ │ │ │ + }); │ │ │ │ │ │ │ │ - var layers = this.findLayers(); │ │ │ │ - if (layers.length > 0) { │ │ │ │ - var infoLookup = {}; │ │ │ │ - var layer, idx; │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - layer = layers[i]; │ │ │ │ - idx = OpenLayers.Util.indexOf(this.map.layers, layer); │ │ │ │ - infoLookup[idx] = layer.getFeatureInfo(lonLat); │ │ │ │ - } │ │ │ │ - this.callback(infoLookup, lonLat, evt.xy); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + // convert \\.* to \\% │ │ │ │ + value = value.replace(/\\\\\.\*/g, "\\\\%"); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: callback │ │ │ │ - * Function to be called when a mouse event corresponds with a location that │ │ │ │ - * includes data in one of the configured UTFGrid layers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * infoLookup - {Object} Keys of this object are layer indexes and can be │ │ │ │ - * used to resolve a layer in the map.layers array. The structure of │ │ │ │ - * the property values depend on the data included in the underlying │ │ │ │ - * UTFGrid and may be any valid JSON type. │ │ │ │ - */ │ │ │ │ - callback: function(infoLookup) { │ │ │ │ - // to be provided in the constructor │ │ │ │ - }, │ │ │ │ + // convert . to _ (\. and .* occurences converted later) │ │ │ │ + value = value.replace(/(\\)?\.(\*)?/g, function($0, $1, $2) { │ │ │ │ + return $1 || $2 ? $0 : "_"; │ │ │ │ + }); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: reset │ │ │ │ - * Calls the callback with null. │ │ │ │ - */ │ │ │ │ - reset: function(evt) { │ │ │ │ - this.callback(null); │ │ │ │ - }, │ │ │ │ + // convert .* to % (\.* occurnces converted later) │ │ │ │ + value = value.replace(/(\\)?\.\*/g, function($0, $1) { │ │ │ │ + return $1 ? $0 : "%"; │ │ │ │ + }); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: findLayers │ │ │ │ - * Internal method to get the layers, independent of whether we are │ │ │ │ - * inspecting the map or using a client-provided array │ │ │ │ - * │ │ │ │ - * The default value of this.layers is null; this causes the │ │ │ │ - * findLayers method to return ALL UTFGrid layers encountered. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * None │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} Layers to handle on each event │ │ │ │ - */ │ │ │ │ - findLayers: function() { │ │ │ │ - var candidates = this.layers || this.map.layers; │ │ │ │ - var layers = []; │ │ │ │ - var layer; │ │ │ │ - for (var i = candidates.length - 1; i >= 0; --i) { │ │ │ │ - layer = candidates[i]; │ │ │ │ - if (layer instanceof OpenLayers.Layer.UTFGrid) { │ │ │ │ - layers.push(layer); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return layers; │ │ │ │ - }, │ │ │ │ + // convert \. to . │ │ │ │ + value = value.replace(/\\\./g, "."); │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.UTFGrid" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/EditingToolbar.js │ │ │ │ - ====================================================================== */ │ │ │ │ + // replace \* with * (watching out for \\*) │ │ │ │ + value = value.replace(/(\\)?\\\*/g, function($0, $1) { │ │ │ │ + return $1 ? $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. */ │ │ │ │ + return value; │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control/Panel.js │ │ │ │ - * @requires OpenLayers/Control/Navigation.js │ │ │ │ - * @requires OpenLayers/Control/DrawFeature.js │ │ │ │ - * @requires OpenLayers/Handler/Point.js │ │ │ │ - * @requires OpenLayers/Handler/Path.js │ │ │ │ - * @requires OpenLayers/Handler/Polygon.js │ │ │ │ - */ │ │ │ │ + return OpenLayers.Class(OpenLayers.Format, { │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.EditingToolbar │ │ │ │ - * The EditingToolbar is a panel of 4 controls to draw polygons, lines, │ │ │ │ - * points, or to navigate the map by panning. By default it appears in the │ │ │ │ - * upper right corner of the map. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control.Panel> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.EditingToolbar = OpenLayers.Class( │ │ │ │ - OpenLayers.Control.Panel, { │ │ │ │ + /** │ │ │ │ + * Property: wildcarded. │ │ │ │ + * {Boolean} If true percent signs are added around values │ │ │ │ + * read from LIKE filters, for example if the protocol │ │ │ │ + * read method is passed a LIKE filter whose property │ │ │ │ + * is "foo" and whose value is "bar" the string │ │ │ │ + * "foo__ilike=%bar%" will be sent in the query string; │ │ │ │ + * defaults to false. │ │ │ │ + */ │ │ │ │ + wildcarded: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: citeCompliant │ │ │ │ - * {Boolean} If set to true, coordinates of features drawn in a map extent │ │ │ │ - * crossing the date line won't exceed the world bounds. Default is false. │ │ │ │ + * APIProperty: srsInBBOX │ │ │ │ + * {Boolean} Include the SRS identifier in BBOX query string parameter. │ │ │ │ + * Default is false. If true and the layer has a projection object set, │ │ │ │ + * any BBOX filter will be serialized with a fifth item identifying the │ │ │ │ + * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913 │ │ │ │ */ │ │ │ │ - citeCompliant: false, │ │ │ │ + srsInBBOX: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.EditingToolbar │ │ │ │ - * Create an editing toolbar for a given layer. │ │ │ │ + * APIMethod: write │ │ │ │ + * Serialize an <OpenLayers.Filter> objects using the "simple" filter syntax for │ │ │ │ + * query string parameters. This function must be called as a method of │ │ │ │ + * a protocol instance. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} │ │ │ │ - * options - {Object} │ │ │ │ + * filter - {<OpenLayers.Filter>} filter to convert. │ │ │ │ + * params - {Object} The parameters object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} The resulting parameters object. │ │ │ │ */ │ │ │ │ - initialize: function(layer, options) { │ │ │ │ - OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); │ │ │ │ - │ │ │ │ - this.addControls( │ │ │ │ - [new OpenLayers.Control.Navigation()] │ │ │ │ - ); │ │ │ │ - var controls = [ │ │ │ │ - new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, { │ │ │ │ - displayClass: 'olControlDrawFeaturePoint', │ │ │ │ - handlerOptions: { │ │ │ │ - citeCompliant: this.citeCompliant │ │ │ │ + write: function(filter, params) { │ │ │ │ + params = params || {}; │ │ │ │ + var className = filter.CLASS_NAME; │ │ │ │ + var filterType = className.substring(className.lastIndexOf(".") + 1); │ │ │ │ + switch (filterType) { │ │ │ │ + case "Spatial": │ │ │ │ + switch (filter.type) { │ │ │ │ + case OpenLayers.Filter.Spatial.BBOX: │ │ │ │ + params.bbox = filter.value.toArray(); │ │ │ │ + if (this.srsInBBOX && filter.projection) { │ │ │ │ + params.bbox.push(filter.projection.getCode()); │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case OpenLayers.Filter.Spatial.DWITHIN: │ │ │ │ + params.tolerance = filter.distance; │ │ │ │ + // no break here │ │ │ │ + case OpenLayers.Filter.Spatial.WITHIN: │ │ │ │ + params.lon = filter.value.x; │ │ │ │ + params.lat = filter.value.y; │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + OpenLayers.Console.warn( │ │ │ │ + "Unknown spatial filter type " + filter.type); │ │ │ │ } │ │ │ │ - }), │ │ │ │ - new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, { │ │ │ │ - displayClass: 'olControlDrawFeaturePath', │ │ │ │ - handlerOptions: { │ │ │ │ - citeCompliant: this.citeCompliant │ │ │ │ + break; │ │ │ │ + case "Comparison": │ │ │ │ + var op = cmpToStr[filter.type]; │ │ │ │ + if (op !== undefined) { │ │ │ │ + var value = filter.value; │ │ │ │ + if (filter.type == OpenLayers.Filter.Comparison.LIKE) { │ │ │ │ + value = regex2value(value); │ │ │ │ + if (this.wildcarded) { │ │ │ │ + value = "%" + value + "%"; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + params[filter.property + "__" + op] = value; │ │ │ │ + params.queryable = params.queryable || []; │ │ │ │ + params.queryable.push(filter.property); │ │ │ │ + } else { │ │ │ │ + OpenLayers.Console.warn( │ │ │ │ + "Unknown comparison filter type " + filter.type); │ │ │ │ } │ │ │ │ - }), │ │ │ │ - new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, { │ │ │ │ - displayClass: 'olControlDrawFeaturePolygon', │ │ │ │ - handlerOptions: { │ │ │ │ - citeCompliant: this.citeCompliant │ │ │ │ + break; │ │ │ │ + case "Logical": │ │ │ │ + if (filter.type === OpenLayers.Filter.Logical.AND) { │ │ │ │ + for (var i = 0, len = filter.filters.length; i < len; i++) { │ │ │ │ + params = this.write(filter.filters[i], params); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + OpenLayers.Console.warn( │ │ │ │ + "Unsupported logical filter type " + filter.type); │ │ │ │ } │ │ │ │ - }) │ │ │ │ - ]; │ │ │ │ - this.addControls(controls); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ - * calls the default draw, and then activates mouse defaults. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - draw: function() { │ │ │ │ - var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments); │ │ │ │ - if (this.defaultControl === null) { │ │ │ │ - this.defaultControl = this.controls[0]; │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + OpenLayers.Console.warn("Unknown filter type " + filterType); │ │ │ │ } │ │ │ │ - return div; │ │ │ │ + return params; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.EditingToolbar" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.QueryStringFilter" │ │ │ │ + │ │ │ │ }); │ │ │ │ + │ │ │ │ + │ │ │ │ +})(); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/Zoom.js │ │ │ │ + OpenLayers/Format/WMSGetFeatureInfo.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Events/buttonclick.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.Zoom │ │ │ │ - * The Zoom control is a pair of +/- links for zooming in and out. │ │ │ │ + * Class: OpenLayers.Format.WMSGetFeatureInfo │ │ │ │ + * Class to read GetFeatureInfo responses from Web Mapping Services │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ +OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: zoomInText │ │ │ │ - * {String} │ │ │ │ - * Text for zoom-in link. Default is "+". │ │ │ │ + * APIProperty: layerIdentifier │ │ │ │ + * {String} All xml nodes containing this search criteria will populate an │ │ │ │ + * internal array of layer nodes. │ │ │ │ */ │ │ │ │ - zoomInText: "+", │ │ │ │ + layerIdentifier: '_layer', │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: zoomInId │ │ │ │ - * {String} │ │ │ │ - * Instead of having the control create a zoom in link, you can provide │ │ │ │ - * the identifier for an anchor element already added to the document. │ │ │ │ - * By default, an element with id "olZoomInLink" will be searched for │ │ │ │ - * and used if it exists. │ │ │ │ + * APIProperty: featureIdentifier │ │ │ │ + * {String} All xml nodes containing this search criteria will populate an │ │ │ │ + * internal array of feature nodes for each layer node found. │ │ │ │ */ │ │ │ │ - zoomInId: "olZoomInLink", │ │ │ │ + featureIdentifier: '_feature', │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: zoomOutText │ │ │ │ - * {String} │ │ │ │ - * Text for zoom-out link. Default is "\u2212". │ │ │ │ + * Property: regExes │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ */ │ │ │ │ - zoomOutText: "\u2212", │ │ │ │ + regExes: { │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: zoomOutId │ │ │ │ - * {String} │ │ │ │ - * Instead of having the control create a zoom out link, you can provide │ │ │ │ - * the identifier for an anchor element already added to the document. │ │ │ │ - * By default, an element with id "olZoomOutLink" will be searched for │ │ │ │ - * and used if it exists. │ │ │ │ + * Property: gmlFormat │ │ │ │ + * {<OpenLayers.Format.GML>} internal GML format for parsing geometries │ │ │ │ + * in msGMLOutput │ │ │ │ */ │ │ │ │ - zoomOutId: "olZoomOutLink", │ │ │ │ + gmlFormat: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ + * Constructor: OpenLayers.Format.WMSGetFeatureInfo │ │ │ │ + * Create a new parser for WMS GetFeatureInfo responses │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A reference to the DOMElement containing the zoom links. │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - var div = OpenLayers.Control.prototype.draw.apply(this), │ │ │ │ - links = this.getOrCreateLinks(div), │ │ │ │ - zoomIn = links.zoomIn, │ │ │ │ - zoomOut = links.zoomOut, │ │ │ │ - eventsInstance = this.map.events; │ │ │ │ - │ │ │ │ - if (zoomOut.parentNode !== div) { │ │ │ │ - eventsInstance = this.events; │ │ │ │ - eventsInstance.attachToElement(zoomOut.parentNode); │ │ │ │ - } │ │ │ │ - eventsInstance.register("buttonclick", this, this.onZoomClick); │ │ │ │ - │ │ │ │ - this.zoomInLink = zoomIn; │ │ │ │ - this.zoomOutLink = zoomOut; │ │ │ │ - return div; │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getOrCreateLinks │ │ │ │ - * │ │ │ │ + * APIMethod: read │ │ │ │ + * Read WMS GetFeatureInfo data from a string, and return an array of features │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * el - {DOMElement} │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ * │ │ │ │ - * Return: │ │ │ │ - * {Object} Object with zoomIn and zoomOut properties referencing links. │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} An array of features. │ │ │ │ */ │ │ │ │ - getOrCreateLinks: function(el) { │ │ │ │ - var zoomIn = document.getElementById(this.zoomInId), │ │ │ │ - zoomOut = document.getElementById(this.zoomOutId); │ │ │ │ - if (!zoomIn) { │ │ │ │ - zoomIn = document.createElement("a"); │ │ │ │ - zoomIn.href = "#zoomIn"; │ │ │ │ - zoomIn.appendChild(document.createTextNode(this.zoomInText)); │ │ │ │ - zoomIn.className = "olControlZoomIn"; │ │ │ │ - el.appendChild(zoomIn); │ │ │ │ + read: function(data) { │ │ │ │ + var result; │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ } │ │ │ │ - OpenLayers.Element.addClass(zoomIn, "olButton"); │ │ │ │ - if (!zoomOut) { │ │ │ │ - zoomOut = document.createElement("a"); │ │ │ │ - zoomOut.href = "#zoomOut"; │ │ │ │ - zoomOut.appendChild(document.createTextNode(this.zoomOutText)); │ │ │ │ - zoomOut.className = "olControlZoomOut"; │ │ │ │ - el.appendChild(zoomOut); │ │ │ │ + var root = data.documentElement; │ │ │ │ + if (root) { │ │ │ │ + var scope = this; │ │ │ │ + var read = this["read_" + root.nodeName]; │ │ │ │ + if (read) { │ │ │ │ + result = read.call(this, root); │ │ │ │ + } else { │ │ │ │ + // fall-back to GML since this is a common output format for WMS │ │ │ │ + // GetFeatureInfo responses │ │ │ │ + result = new OpenLayers.Format.GML((this.options ? this.options : {})).read(data); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + result = data; │ │ │ │ } │ │ │ │ - OpenLayers.Element.addClass(zoomOut, "olButton"); │ │ │ │ - return { │ │ │ │ - zoomIn: zoomIn, │ │ │ │ - zoomOut: zoomOut │ │ │ │ - }; │ │ │ │ + return result; │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: onZoomClick │ │ │ │ - * Called when zoomin/out link is clicked. │ │ │ │ + * Method: read_msGMLOutput │ │ │ │ + * Parse msGMLOutput nodes. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {DOMElement} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} │ │ │ │ */ │ │ │ │ - onZoomClick: function(evt) { │ │ │ │ - var button = evt.buttonElement; │ │ │ │ - if (button === this.zoomInLink) { │ │ │ │ - this.map.zoomIn(); │ │ │ │ - } else if (button === this.zoomOutLink) { │ │ │ │ - this.map.zoomOut(); │ │ │ │ + read_msGMLOutput: function(data) { │ │ │ │ + var response = []; │ │ │ │ + var layerNodes = this.getSiblingNodesByTagCriteria(data, │ │ │ │ + this.layerIdentifier); │ │ │ │ + if (layerNodes) { │ │ │ │ + for (var i = 0, len = layerNodes.length; i < len; ++i) { │ │ │ │ + var node = layerNodes[i]; │ │ │ │ + var layerName = node.nodeName; │ │ │ │ + if (node.prefix) { │ │ │ │ + layerName = layerName.split(':')[1]; │ │ │ │ + } │ │ │ │ + var layerName = layerName.replace(this.layerIdentifier, ''); │ │ │ │ + var featureNodes = this.getSiblingNodesByTagCriteria(node, │ │ │ │ + this.featureIdentifier); │ │ │ │ + if (featureNodes) { │ │ │ │ + for (var j = 0; j < featureNodes.length; j++) { │ │ │ │ + var featureNode = featureNodes[j]; │ │ │ │ + var geomInfo = this.parseGeometry(featureNode); │ │ │ │ + var attributes = this.parseAttributes(featureNode); │ │ │ │ + var feature = new OpenLayers.Feature.Vector(geomInfo.geometry, │ │ │ │ + attributes, null); │ │ │ │ + feature.bounds = geomInfo.bounds; │ │ │ │ + feature.type = layerName; │ │ │ │ + response.push(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + return response; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * Clean up. │ │ │ │ + /** │ │ │ │ + * Method: read_FeatureInfoResponse │ │ │ │ + * Parse FeatureInfoResponse nodes. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {DOMElement} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.unregister("buttonclick", this, this.onZoomClick); │ │ │ │ - } │ │ │ │ - delete this.zoomInLink; │ │ │ │ - delete this.zoomOutLink; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Zoom" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Handler/Keyboard.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Handler.js │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.handler.Keyboard │ │ │ │ - * A handler for keyboard events. Create a new instance with the │ │ │ │ - * <OpenLayers.Handler.Keyboard> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Handler> │ │ │ │ - */ │ │ │ │ -OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - │ │ │ │ - /* http://www.quirksmode.org/js/keys.html explains key x-browser │ │ │ │ - key handling quirks in pretty nice detail */ │ │ │ │ + read_FeatureInfoResponse: function(data) { │ │ │ │ + var response = []; │ │ │ │ + var featureNodes = this.getElementsByTagNameNS(data, '*', │ │ │ │ + 'FIELDS'); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: KEY_EVENTS │ │ │ │ - * keydown, keypress, keyup │ │ │ │ - */ │ │ │ │ - KEY_EVENTS: ["keydown", "keyup"], │ │ │ │ + for (var i = 0, len = featureNodes.length; i < len; i++) { │ │ │ │ + var featureNode = featureNodes[i]; │ │ │ │ + var geom = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: eventListener │ │ │ │ - * {Function} │ │ │ │ - */ │ │ │ │ - eventListener: null, │ │ │ │ + // attributes can be actual attributes on the FIELDS tag, │ │ │ │ + // or FIELD children │ │ │ │ + var attributes = {}; │ │ │ │ + var j; │ │ │ │ + var jlen = featureNode.attributes.length; │ │ │ │ + if (jlen > 0) { │ │ │ │ + for (j = 0; j < jlen; j++) { │ │ │ │ + var attribute = featureNode.attributes[j]; │ │ │ │ + attributes[attribute.nodeName] = attribute.nodeValue; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + var nodes = featureNode.childNodes; │ │ │ │ + for (j = 0, jlen = nodes.length; j < jlen; ++j) { │ │ │ │ + var node = nodes[j]; │ │ │ │ + if (node.nodeType != 3) { │ │ │ │ + attributes[node.getAttribute("name")] = │ │ │ │ + node.getAttribute("value"); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: observeElement │ │ │ │ - * {DOMElement|String} The DOM element on which we listen for │ │ │ │ - * key events. Default to the document. │ │ │ │ - */ │ │ │ │ - observeElement: null, │ │ │ │ + response.push( │ │ │ │ + new OpenLayers.Feature.Vector(geom, attributes, null) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return response; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Handler.Keyboard │ │ │ │ - * Returns a new keyboard handler. │ │ │ │ + * Method: getSiblingNodesByTagCriteria │ │ │ │ + * Recursively searches passed xml node and all it's descendant levels for │ │ │ │ + * nodes whose tagName contains the passed search string. This returns an │ │ │ │ + * array of all sibling nodes which match the criteria from the highest │ │ │ │ + * hierarchial level from which a match is found. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} The control that is making use of │ │ │ │ - * this handler. If a handler is being used without a control, the │ │ │ │ - * handlers setMap method must be overridden to deal properly with │ │ │ │ - * the map. │ │ │ │ - * callbacks - {Object} An object containing a single function to be │ │ │ │ - * called when the drag operation is finished. The callback should │ │ │ │ - * expect to recieve a single argument, the pixel location of the event. │ │ │ │ - * Callbacks for 'keydown', 'keypress', and 'keyup' are supported. │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * handler. │ │ │ │ + * node - {DOMElement} An xml node │ │ │ │ + * criteria - {String} Search string which will match some part of a tagName │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * Array({DOMElement}) An array of sibling xml nodes │ │ │ │ */ │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - // cache the bound event listener method so it can be unobserved later │ │ │ │ - this.eventListener = OpenLayers.Function.bindAsEventListener( │ │ │ │ - this.handleKeyEvent, this │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ + getSiblingNodesByTagCriteria: function(node, criteria) { │ │ │ │ + var nodes = []; │ │ │ │ + var children, tagName, n, matchNodes, child; │ │ │ │ + if (node && node.hasChildNodes()) { │ │ │ │ + children = node.childNodes; │ │ │ │ + n = children.length; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - this.eventListener = null; │ │ │ │ - OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + for (var k = 0; k < n; k++) { │ │ │ │ + child = children[k]; │ │ │ │ + while (child && child.nodeType != 1) { │ │ │ │ + child = child.nextSibling; │ │ │ │ + k++; │ │ │ │ + } │ │ │ │ + tagName = (child ? child.nodeName : ''); │ │ │ │ + if (tagName.length > 0 && tagName.indexOf(criteria) > -1) { │ │ │ │ + nodes.push(child); │ │ │ │ + } else { │ │ │ │ + matchNodes = this.getSiblingNodesByTagCriteria( │ │ │ │ + child, criteria); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: activate │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.observeElement = this.observeElement || document; │ │ │ │ - for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) { │ │ │ │ - OpenLayers.Event.observe( │ │ │ │ - this.observeElement, this.KEY_EVENTS[i], this.eventListener); │ │ │ │ + if (matchNodes.length > 0) { │ │ │ │ + (nodes.length == 0) ? │ │ │ │ + nodes = matchNodes: nodes.push(matchNodes); │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ + │ │ │ │ } │ │ │ │ + return nodes; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: deactivate │ │ │ │ + * Method: parseAttributes │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {<DOMElement>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An attributes object. │ │ │ │ + * │ │ │ │ + * Notes: │ │ │ │ + * Assumes that attributes are direct child xml nodes of the passed node │ │ │ │ + * and contain only a single text node. │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) { │ │ │ │ - OpenLayers.Event.stopObserving( │ │ │ │ - this.observeElement, this.KEY_EVENTS[i], this.eventListener); │ │ │ │ + parseAttributes: function(node) { │ │ │ │ + var attributes = {}; │ │ │ │ + if (node.nodeType == 1) { │ │ │ │ + var children = node.childNodes; │ │ │ │ + var n = children.length; │ │ │ │ + for (var i = 0; i < n; ++i) { │ │ │ │ + var child = children[i]; │ │ │ │ + if (child.nodeType == 1) { │ │ │ │ + var grandchildren = child.childNodes; │ │ │ │ + var name = (child.prefix) ? │ │ │ │ + child.nodeName.split(":")[1] : child.nodeName; │ │ │ │ + if (grandchildren.length == 0) { │ │ │ │ + attributes[name] = null; │ │ │ │ + } else if (grandchildren.length == 1) { │ │ │ │ + var grandchild = grandchildren[0]; │ │ │ │ + if (grandchild.nodeType == 3 || │ │ │ │ + grandchild.nodeType == 4) { │ │ │ │ + var value = grandchild.nodeValue.replace( │ │ │ │ + this.regExes.trimSpace, ""); │ │ │ │ + attributes[name] = value; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - deactivated = true; │ │ │ │ } │ │ │ │ - return deactivated; │ │ │ │ + return attributes; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleKeyEvent │ │ │ │ + * Method: parseGeometry │ │ │ │ + * Parse the geometry and the feature bounds out of the node using │ │ │ │ + * Format.GML │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {<DOMElement>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An object containing the geometry and the feature bounds │ │ │ │ */ │ │ │ │ - handleKeyEvent: function(evt) { │ │ │ │ - if (this.checkModifiers(evt)) { │ │ │ │ - this.callback(evt.type, [evt]); │ │ │ │ + parseGeometry: function(node) { │ │ │ │ + // we need to use the old Format.GML parser since we do not know the │ │ │ │ + // geometry name │ │ │ │ + if (!this.gmlFormat) { │ │ │ │ + this.gmlFormat = new OpenLayers.Format.GML(); │ │ │ │ + } │ │ │ │ + var feature = this.gmlFormat.parseFeature(node); │ │ │ │ + var geometry, bounds = null; │ │ │ │ + if (feature) { │ │ │ │ + geometry = feature.geometry && feature.geometry.clone(); │ │ │ │ + bounds = feature.bounds && feature.bounds.clone(); │ │ │ │ + feature.destroy(); │ │ │ │ } │ │ │ │ + return { │ │ │ │ + geometry: geometry, │ │ │ │ + bounds: bounds │ │ │ │ + }; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Keyboard" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSGetFeatureInfo" │ │ │ │ + │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/ModifyFeature.js │ │ │ │ + OpenLayers/Format/SOSGetObservation.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Handler/Drag.js │ │ │ │ - * @requires OpenLayers/Handler/Keyboard.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Format/SOSGetFeatureOfInterest.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.ModifyFeature │ │ │ │ - * Control to modify features. When activated, a click renders the vertices │ │ │ │ - * of a feature - these vertices can then be dragged. By default, the │ │ │ │ - * delete key will delete the vertex under the mouse. New features are │ │ │ │ - * added by dragging "virtual vertices" between vertices. Create a new │ │ │ │ - * control with the <OpenLayers.Control.ModifyFeature> constructor. │ │ │ │ + * Class: OpenLayers.Format.SOSGetObservation │ │ │ │ + * Read and write SOS GetObersation (to get the actual values from a sensor) │ │ │ │ + * version 1.0.0 │ │ │ │ * │ │ │ │ - * Inherits From: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: documentDrag │ │ │ │ - * {Boolean} If set to true, dragging vertices will continue even if the │ │ │ │ - * mouse cursor leaves the map viewport. Default is false. │ │ │ │ - */ │ │ │ │ - documentDrag: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: geometryTypes │ │ │ │ - * {Array(String)} To restrict modification to a limited set of geometry │ │ │ │ - * types, send a list of strings corresponding to the geometry class │ │ │ │ - * names. │ │ │ │ - */ │ │ │ │ - geometryTypes: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: clickout │ │ │ │ - * {Boolean} Unselect features when clicking outside any feature. │ │ │ │ - * Default is true. │ │ │ │ - */ │ │ │ │ - clickout: true, │ │ │ │ +OpenLayers.Format.SOSGetObservation = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: toggle │ │ │ │ - * {Boolean} Unselect a selected feature on click. │ │ │ │ - * Default is true. │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ */ │ │ │ │ - toggle: true, │ │ │ │ + namespaces: { │ │ │ │ + ows: "http://www.opengis.net/ows", │ │ │ │ + gml: "http://www.opengis.net/gml", │ │ │ │ + sos: "http://www.opengis.net/sos/1.0", │ │ │ │ + ogc: "http://www.opengis.net/ogc", │ │ │ │ + om: "http://www.opengis.net/om/1.0", │ │ │ │ + sa: "http://www.opengis.net/sampling/1.0", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: standalone │ │ │ │ - * {Boolean} Set to true to create a control without SelectFeature │ │ │ │ - * capabilities. Default is false. If standalone is true, to modify │ │ │ │ - * a feature, call the <selectFeature> method with the target feature. │ │ │ │ - * Note that you must call the <unselectFeature> method to finish │ │ │ │ - * feature modification in standalone mode (before starting to modify │ │ │ │ - * another feature). │ │ │ │ + * Property: regExes │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ */ │ │ │ │ - standalone: false, │ │ │ │ + regExes: { │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: layer │ │ │ │ - * {<OpenLayers.Layer.Vector>} │ │ │ │ + * Constant: VERSION │ │ │ │ + * {String} 1.0.0 │ │ │ │ */ │ │ │ │ - layer: null, │ │ │ │ + VERSION: "1.0.0", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: feature │ │ │ │ - * {<OpenLayers.Feature.Vector>} Feature currently available for modification. │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} Schema location │ │ │ │ */ │ │ │ │ - feature: null, │ │ │ │ + schemaLocation: "http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosGetObservation.xsd", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: vertex │ │ │ │ - * {<OpenLayers.Feature.Vector>} Vertex currently being modified. │ │ │ │ + * Property: defaultPrefix │ │ │ │ */ │ │ │ │ - vertex: null, │ │ │ │ + defaultPrefix: "sos", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: vertices │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} Verticies currently available │ │ │ │ - * for dragging. │ │ │ │ + * Constructor: OpenLayers.Format.SOSGetObservation │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - vertices: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: virtualVertices │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} Virtual vertices in the middle │ │ │ │ - * of each edge. │ │ │ │ + * Method: read │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An object containing the measurements │ │ │ │ */ │ │ │ │ - virtualVertices: null, │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ + } │ │ │ │ + var info = { │ │ │ │ + measurements: [], │ │ │ │ + observations: [] │ │ │ │ + }; │ │ │ │ + this.readNode(data, info); │ │ │ │ + return info; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: handlers │ │ │ │ - * {Object} │ │ │ │ + * Method: write │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} An SOS GetObservation request XML string. │ │ │ │ */ │ │ │ │ - handlers: null, │ │ │ │ + write: function(options) { │ │ │ │ + var node = this.writeNode("sos:GetObservation", options); │ │ │ │ + node.setAttribute("xmlns:om", this.namespaces.om); │ │ │ │ + node.setAttribute("xmlns:ogc", this.namespaces.ogc); │ │ │ │ + this.setAttributeNS( │ │ │ │ + node, this.namespaces.xsi, │ │ │ │ + "xsi:schemaLocation", this.schemaLocation │ │ │ │ + ); │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: deleteCodes │ │ │ │ - * {Array(Integer)} Keycodes for deleting verticies. Set to null to disable │ │ │ │ - * vertex deltion by keypress. If non-null, keypresses with codes │ │ │ │ - * in this array will delete vertices under the mouse. Default │ │ │ │ - * is 46 and 68, the 'delete' and lowercase 'd' keys. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - deleteCodes: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: virtualStyle │ │ │ │ - * {Object} A symbolizer to be used for virtual vertices. │ │ │ │ - */ │ │ │ │ - virtualStyle: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: vertexRenderIntent │ │ │ │ - * {String} The renderIntent to use for vertices. If no <virtualStyle> is │ │ │ │ - * provided, this renderIntent will also be used for virtual vertices, with │ │ │ │ - * a fillOpacity and strokeOpacity of 0.3. Default is null, which means │ │ │ │ - * that the layer's default style will be used for vertices. │ │ │ │ - */ │ │ │ │ - vertexRenderIntent: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: mode │ │ │ │ - * {Integer} Bitfields specifying the modification mode. Defaults to │ │ │ │ - * OpenLayers.Control.ModifyFeature.RESHAPE. To set the mode to a │ │ │ │ - * combination of options, use the | operator. For example, to allow │ │ │ │ - * the control to both resize and rotate features, use the following │ │ │ │ - * syntax │ │ │ │ - * (code) │ │ │ │ - * control.mode = OpenLayers.Control.ModifyFeature.RESIZE | │ │ │ │ - * OpenLayers.Control.ModifyFeature.ROTATE; │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ - mode: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: createVertices │ │ │ │ - * {Boolean} Create new vertices by dragging the virtual vertices │ │ │ │ - * in the middle of each edge. Default is true. │ │ │ │ - */ │ │ │ │ - createVertices: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: modified │ │ │ │ - * {Boolean} The currently selected feature has been modified. │ │ │ │ - */ │ │ │ │ - modified: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: radiusHandle │ │ │ │ - * {<OpenLayers.Feature.Vector>} A handle for rotating/resizing a feature. │ │ │ │ - */ │ │ │ │ - radiusHandle: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: dragHandle │ │ │ │ - * {<OpenLayers.Feature.Vector>} A handle for dragging a feature. │ │ │ │ - */ │ │ │ │ - dragHandle: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: onModificationStart │ │ │ │ - * {Function} *Deprecated*. Register for "beforefeaturemodified" instead. │ │ │ │ - * The "beforefeaturemodified" event is triggered on the layer before │ │ │ │ - * any modification begins. │ │ │ │ - * │ │ │ │ - * Optional function to be called when a feature is selected │ │ │ │ - * to be modified. The function should expect to be called with a │ │ │ │ - * feature. This could be used for example to allow to lock the │ │ │ │ - * feature on server-side. │ │ │ │ - */ │ │ │ │ - onModificationStart: function() {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: onModification │ │ │ │ - * {Function} *Deprecated*. Register for "featuremodified" instead. │ │ │ │ - * The "featuremodified" event is triggered on the layer with each │ │ │ │ - * feature modification. │ │ │ │ - * │ │ │ │ - * Optional function to be called when a feature has been │ │ │ │ - * modified. The function should expect to be called with a feature. │ │ │ │ - */ │ │ │ │ - onModification: function() {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: onModificationEnd │ │ │ │ - * {Function} *Deprecated*. Register for "afterfeaturemodified" instead. │ │ │ │ - * The "afterfeaturemodified" event is triggered on the layer after │ │ │ │ - * a feature has been modified. │ │ │ │ - * │ │ │ │ - * Optional function to be called when a feature is finished │ │ │ │ - * being modified. The function should expect to be called with a │ │ │ │ - * feature. │ │ │ │ - */ │ │ │ │ - onModificationEnd: function() {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.ModifyFeature │ │ │ │ - * Create a new modify feature control. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that │ │ │ │ - * will be modified. │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * control. │ │ │ │ - */ │ │ │ │ - initialize: function(layer, options) { │ │ │ │ - options = options || {}; │ │ │ │ - this.layer = layer; │ │ │ │ - this.vertices = []; │ │ │ │ - this.virtualVertices = []; │ │ │ │ - this.virtualStyle = OpenLayers.Util.extend({}, │ │ │ │ - this.layer.style || │ │ │ │ - this.layer.styleMap.createSymbolizer(null, options.vertexRenderIntent) │ │ │ │ - ); │ │ │ │ - this.virtualStyle.fillOpacity = 0.3; │ │ │ │ - this.virtualStyle.strokeOpacity = 0.3; │ │ │ │ - this.deleteCodes = [46, 68]; │ │ │ │ - this.mode = OpenLayers.Control.ModifyFeature.RESHAPE; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - if (!(OpenLayers.Util.isArray(this.deleteCodes))) { │ │ │ │ - this.deleteCodes = [this.deleteCodes]; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // configure the drag handler │ │ │ │ - var dragCallbacks = { │ │ │ │ - down: function(pixel) { │ │ │ │ - this.vertex = null; │ │ │ │ - var feature = this.layer.getFeatureFromEvent( │ │ │ │ - this.handlers.drag.evt); │ │ │ │ - if (feature) { │ │ │ │ - this.dragStart(feature); │ │ │ │ - } else if (this.clickout) { │ │ │ │ - this._unselect = this.feature; │ │ │ │ - } │ │ │ │ + readers: { │ │ │ │ + "om": { │ │ │ │ + "ObservationCollection": function(node, obj) { │ │ │ │ + obj.id = this.getAttributeNS(node, this.namespaces.gml, "id"); │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ }, │ │ │ │ - move: function(pixel) { │ │ │ │ - delete this._unselect; │ │ │ │ - if (this.vertex) { │ │ │ │ - this.dragVertex(this.vertex, pixel); │ │ │ │ - } │ │ │ │ + "member": function(node, observationCollection) { │ │ │ │ + this.readChildNodes(node, observationCollection); │ │ │ │ }, │ │ │ │ - up: function() { │ │ │ │ - this.handlers.drag.stopDown = false; │ │ │ │ - if (this._unselect) { │ │ │ │ - this.unselectFeature(this._unselect); │ │ │ │ - delete this._unselect; │ │ │ │ + "Measurement": function(node, observationCollection) { │ │ │ │ + var measurement = {}; │ │ │ │ + observationCollection.measurements.push(measurement); │ │ │ │ + this.readChildNodes(node, measurement); │ │ │ │ + }, │ │ │ │ + "Observation": function(node, observationCollection) { │ │ │ │ + var observation = {}; │ │ │ │ + observationCollection.observations.push(observation); │ │ │ │ + this.readChildNodes(node, observation); │ │ │ │ + }, │ │ │ │ + "samplingTime": function(node, measurement) { │ │ │ │ + var samplingTime = {}; │ │ │ │ + measurement.samplingTime = samplingTime; │ │ │ │ + this.readChildNodes(node, samplingTime); │ │ │ │ + }, │ │ │ │ + "observedProperty": function(node, measurement) { │ │ │ │ + measurement.observedProperty = │ │ │ │ + this.getAttributeNS(node, this.namespaces.xlink, "href"); │ │ │ │ + this.readChildNodes(node, measurement); │ │ │ │ + }, │ │ │ │ + "procedure": function(node, measurement) { │ │ │ │ + measurement.procedure = │ │ │ │ + this.getAttributeNS(node, this.namespaces.xlink, "href"); │ │ │ │ + this.readChildNodes(node, measurement); │ │ │ │ + }, │ │ │ │ + "featureOfInterest": function(node, observation) { │ │ │ │ + var foi = { │ │ │ │ + features: [] │ │ │ │ + }; │ │ │ │ + observation.fois = []; │ │ │ │ + observation.fois.push(foi); │ │ │ │ + this.readChildNodes(node, foi); │ │ │ │ + // postprocessing to get actual features │ │ │ │ + var features = []; │ │ │ │ + for (var i = 0, len = foi.features.length; i < len; i++) { │ │ │ │ + var feature = foi.features[i]; │ │ │ │ + features.push(new OpenLayers.Feature.Vector( │ │ │ │ + feature.components[0], feature.attributes)); │ │ │ │ } │ │ │ │ + foi.features = features; │ │ │ │ }, │ │ │ │ - done: function(pixel) { │ │ │ │ - if (this.vertex) { │ │ │ │ - this.dragComplete(this.vertex); │ │ │ │ + "result": function(node, measurement) { │ │ │ │ + var result = {}; │ │ │ │ + measurement.result = result; │ │ │ │ + if (this.getChildValue(node) !== '') { │ │ │ │ + result.value = this.getChildValue(node); │ │ │ │ + result.uom = node.getAttribute("uom"); │ │ │ │ + } else { │ │ │ │ + this.readChildNodes(node, result); │ │ │ │ } │ │ │ │ } │ │ │ │ - }; │ │ │ │ - var dragOptions = { │ │ │ │ - documentDrag: this.documentDrag, │ │ │ │ - stopDown: false │ │ │ │ - }; │ │ │ │ - │ │ │ │ - // configure the keyboard handler │ │ │ │ - var keyboardOptions = { │ │ │ │ - keydown: this.handleKeypress │ │ │ │ - }; │ │ │ │ - this.handlers = { │ │ │ │ - keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions), │ │ │ │ - drag: new OpenLayers.Handler.Drag(this, dragCallbacks, dragOptions) │ │ │ │ - }; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Take care of things that are not handled in superclass. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.un({ │ │ │ │ - "removelayer": this.handleMapEvents, │ │ │ │ - "changelayer": this.handleMapEvents, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - this.layer = null; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, []); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Activate the control. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Successfully activated the control. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - this.moveLayerToTop(); │ │ │ │ - this.map.events.on({ │ │ │ │ - "removelayer": this.handleMapEvents, │ │ │ │ - "changelayer": this.handleMapEvents, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - return (this.handlers.keyboard.activate() && │ │ │ │ - this.handlers.drag.activate() && │ │ │ │ - OpenLayers.Control.prototype.activate.apply(this, arguments)); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the control. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Successfully deactivated the control. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - // the return from the controls is unimportant in this case │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.moveLayerBack(); │ │ │ │ - this.map.events.un({ │ │ │ │ - "removelayer": this.handleMapEvents, │ │ │ │ - "changelayer": this.handleMapEvents, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.layer.removeFeatures(this.vertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.layer.removeFeatures(this.virtualVertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.vertices = []; │ │ │ │ - this.handlers.drag.deactivate(); │ │ │ │ - this.handlers.keyboard.deactivate(); │ │ │ │ - var feature = this.feature; │ │ │ │ - if (feature && feature.geometry && feature.layer) { │ │ │ │ - this.unselectFeature(feature); │ │ │ │ - } │ │ │ │ - deactivated = true; │ │ │ │ - } │ │ │ │ - return deactivated; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: beforeSelectFeature │ │ │ │ - * Called before a feature is selected. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The feature about to be selected. │ │ │ │ - */ │ │ │ │ - beforeSelectFeature: function(feature) { │ │ │ │ - return this.layer.events.triggerEvent( │ │ │ │ - "beforefeaturemodified", { │ │ │ │ - feature: feature │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: selectFeature │ │ │ │ - * Select a feature for modification in standalone mode. In non-standalone │ │ │ │ - * mode, this method is called when a feature is selected by clicking. │ │ │ │ - * Register a listener to the beforefeaturemodified event and return false │ │ │ │ - * to prevent feature modification. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} the selected feature. │ │ │ │ - */ │ │ │ │ - selectFeature: function(feature) { │ │ │ │ - if (this.feature === feature || │ │ │ │ - (this.geometryTypes && OpenLayers.Util.indexOf(this.geometryTypes, │ │ │ │ - feature.geometry.CLASS_NAME) == -1)) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (this.beforeSelectFeature(feature) !== false) { │ │ │ │ - if (this.feature) { │ │ │ │ - this.unselectFeature(this.feature); │ │ │ │ - } │ │ │ │ - this.feature = feature; │ │ │ │ - this.layer.selectedFeatures.push(feature); │ │ │ │ - this.layer.drawFeature(feature, 'select'); │ │ │ │ - this.modified = false; │ │ │ │ - this.resetVertices(); │ │ │ │ - this.onModificationStart(this.feature); │ │ │ │ - } │ │ │ │ - // keep track of geometry modifications │ │ │ │ - var modified = feature.modified; │ │ │ │ - if (feature.geometry && !(modified && modified.geometry)) { │ │ │ │ - this._originalGeometry = feature.geometry.clone(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: unselectFeature │ │ │ │ - * Called when the select feature control unselects a feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The unselected feature. │ │ │ │ - */ │ │ │ │ - unselectFeature: function(feature) { │ │ │ │ - this.layer.removeFeatures(this.vertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.vertices = []; │ │ │ │ - this.layer.destroyFeatures(this.virtualVertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.virtualVertices = []; │ │ │ │ - if (this.dragHandle) { │ │ │ │ - this.layer.destroyFeatures([this.dragHandle], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - delete this.dragHandle; │ │ │ │ - } │ │ │ │ - if (this.radiusHandle) { │ │ │ │ - this.layer.destroyFeatures([this.radiusHandle], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - delete this.radiusHandle; │ │ │ │ - } │ │ │ │ - this.layer.drawFeature(this.feature, 'default'); │ │ │ │ - this.feature = null; │ │ │ │ - OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature); │ │ │ │ - this.onModificationEnd(feature); │ │ │ │ - this.layer.events.triggerEvent("afterfeaturemodified", { │ │ │ │ - feature: feature, │ │ │ │ - modified: this.modified │ │ │ │ - }); │ │ │ │ - this.modified = false; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: dragStart │ │ │ │ - * Called by the drag handler before a feature is dragged. This method is │ │ │ │ - * used to differentiate between points and vertices │ │ │ │ - * of higher order geometries. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be │ │ │ │ - * dragged. │ │ │ │ - */ │ │ │ │ - dragStart: function(feature) { │ │ │ │ - var isPoint = feature.geometry.CLASS_NAME == │ │ │ │ - 'OpenLayers.Geometry.Point'; │ │ │ │ - if (!this.standalone && │ │ │ │ - ((!feature._sketch && isPoint) || !feature._sketch)) { │ │ │ │ - if (this.toggle && this.feature === feature) { │ │ │ │ - // mark feature for unselection │ │ │ │ - this._unselect = feature; │ │ │ │ + }, │ │ │ │ + "sa": OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.sa, │ │ │ │ + "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ + "TimeInstant": function(node, samplingTime) { │ │ │ │ + var timeInstant = {}; │ │ │ │ + samplingTime.timeInstant = timeInstant; │ │ │ │ + this.readChildNodes(node, timeInstant); │ │ │ │ + }, │ │ │ │ + "timePosition": function(node, timeInstant) { │ │ │ │ + timeInstant.timePosition = this.getChildValue(node); │ │ │ │ } │ │ │ │ - this.selectFeature(feature); │ │ │ │ - } │ │ │ │ - if (feature._sketch || isPoint) { │ │ │ │ - // feature is a drag or virtual handle or point │ │ │ │ - this.vertex = feature; │ │ │ │ - this.handlers.drag.stopDown = true; │ │ │ │ - } │ │ │ │ + }, OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.gml) │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: dragVertex │ │ │ │ - * Called by the drag handler with each drag move of a vertex. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged. │ │ │ │ - * pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - dragVertex: function(vertex, pixel) { │ │ │ │ - var pos = this.map.getLonLatFromViewPortPx(pixel); │ │ │ │ - var geom = vertex.geometry; │ │ │ │ - geom.move(pos.lon - geom.x, pos.lat - geom.y); │ │ │ │ - this.modified = true; │ │ │ │ - /** │ │ │ │ - * Five cases: │ │ │ │ - * 1) dragging a simple point │ │ │ │ - * 2) dragging a virtual vertex │ │ │ │ - * 3) dragging a drag handle │ │ │ │ - * 4) dragging a real vertex │ │ │ │ - * 5) dragging a radius handle │ │ │ │ - */ │ │ │ │ - if (this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - // dragging a simple point │ │ │ │ - this.layer.events.triggerEvent("vertexmodified", { │ │ │ │ - vertex: vertex.geometry, │ │ │ │ - feature: this.feature, │ │ │ │ - pixel: pixel │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - if (vertex._index) { │ │ │ │ - // dragging a virtual vertex │ │ │ │ - vertex.geometry.parent.addComponent(vertex.geometry, │ │ │ │ - vertex._index); │ │ │ │ - // move from virtual to real vertex │ │ │ │ - delete vertex._index; │ │ │ │ - OpenLayers.Util.removeItem(this.virtualVertices, vertex); │ │ │ │ - this.vertices.push(vertex); │ │ │ │ - } else if (vertex == this.dragHandle) { │ │ │ │ - // dragging a drag handle │ │ │ │ - this.layer.removeFeatures(this.vertices, { │ │ │ │ - silent: true │ │ │ │ + writers: { │ │ │ │ + "sos": { │ │ │ │ + "GetObservation": function(options) { │ │ │ │ + var node = this.createElementNSPlus("GetObservation", { │ │ │ │ + attributes: { │ │ │ │ + version: this.VERSION, │ │ │ │ + service: 'SOS' │ │ │ │ + } │ │ │ │ }); │ │ │ │ - this.vertices = []; │ │ │ │ - if (this.radiusHandle) { │ │ │ │ - this.layer.destroyFeatures([this.radiusHandle], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.radiusHandle = null; │ │ │ │ + this.writeNode("offering", options, node); │ │ │ │ + if (options.eventTime) { │ │ │ │ + this.writeNode("eventTime", options, node); │ │ │ │ } │ │ │ │ - } else if (vertex !== this.radiusHandle) { │ │ │ │ - // dragging a real vertex │ │ │ │ - this.layer.events.triggerEvent("vertexmodified", { │ │ │ │ - vertex: vertex.geometry, │ │ │ │ - feature: this.feature, │ │ │ │ - pixel: pixel │ │ │ │ + for (var procedure in options.procedures) { │ │ │ │ + this.writeNode("procedure", options.procedures[procedure], node); │ │ │ │ + } │ │ │ │ + for (var observedProperty in options.observedProperties) { │ │ │ │ + this.writeNode("observedProperty", options.observedProperties[observedProperty], node); │ │ │ │ + } │ │ │ │ + if (options.foi) { │ │ │ │ + this.writeNode("featureOfInterest", options.foi, node); │ │ │ │ + } │ │ │ │ + this.writeNode("responseFormat", options, node); │ │ │ │ + if (options.resultModel) { │ │ │ │ + this.writeNode("resultModel", options, node); │ │ │ │ + } │ │ │ │ + if (options.responseMode) { │ │ │ │ + this.writeNode("responseMode", options, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "featureOfInterest": function(foi) { │ │ │ │ + var node = this.createElementNSPlus("featureOfInterest"); │ │ │ │ + this.writeNode("ObjectID", foi.objectId, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "ObjectID": function(options) { │ │ │ │ + return this.createElementNSPlus("ObjectID", { │ │ │ │ + value: options │ │ │ │ }); │ │ │ │ - } │ │ │ │ - // dragging a radius handle - no special treatment │ │ │ │ - if (this.virtualVertices.length > 0) { │ │ │ │ - this.layer.destroyFeatures(this.virtualVertices, { │ │ │ │ - silent: true │ │ │ │ + }, │ │ │ │ + "responseFormat": function(options) { │ │ │ │ + return this.createElementNSPlus("responseFormat", { │ │ │ │ + value: options.responseFormat │ │ │ │ }); │ │ │ │ - this.virtualVertices = []; │ │ │ │ - } │ │ │ │ - this.layer.drawFeature(this.feature, this.standalone ? undefined : │ │ │ │ - 'select'); │ │ │ │ - } │ │ │ │ - // keep the vertex on top so it gets the mouseout after dragging │ │ │ │ - // this should be removed in favor of an option to draw under or │ │ │ │ - // maintain node z-index │ │ │ │ - this.layer.drawFeature(vertex); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: dragComplete │ │ │ │ - * Called by the drag handler when the feature dragging is complete. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged. │ │ │ │ - */ │ │ │ │ - dragComplete: function(vertex) { │ │ │ │ - this.resetVertices(); │ │ │ │ - this.setFeatureState(); │ │ │ │ - this.onModification(this.feature); │ │ │ │ - this.layer.events.triggerEvent("featuremodified", { │ │ │ │ - feature: this.feature │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setFeatureState │ │ │ │ - * Called when the feature is modified. If the current state is not │ │ │ │ - * INSERT or DELETE, the state is set to UPDATE. │ │ │ │ - */ │ │ │ │ - setFeatureState: function() { │ │ │ │ - if (this.feature.state != OpenLayers.State.INSERT && │ │ │ │ - this.feature.state != OpenLayers.State.DELETE) { │ │ │ │ - this.feature.state = OpenLayers.State.UPDATE; │ │ │ │ - if (this.modified && this._originalGeometry) { │ │ │ │ - var feature = this.feature; │ │ │ │ - feature.modified = OpenLayers.Util.extend(feature.modified, { │ │ │ │ - geometry: this._originalGeometry │ │ │ │ + }, │ │ │ │ + "procedure": function(procedure) { │ │ │ │ + return this.createElementNSPlus("procedure", { │ │ │ │ + value: procedure │ │ │ │ }); │ │ │ │ - delete this._originalGeometry; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: resetVertices │ │ │ │ - */ │ │ │ │ - resetVertices: function() { │ │ │ │ - if (this.vertices.length > 0) { │ │ │ │ - this.layer.removeFeatures(this.vertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.vertices = []; │ │ │ │ - } │ │ │ │ - if (this.virtualVertices.length > 0) { │ │ │ │ - this.layer.removeFeatures(this.virtualVertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.virtualVertices = []; │ │ │ │ - } │ │ │ │ - if (this.dragHandle) { │ │ │ │ - this.layer.destroyFeatures([this.dragHandle], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.dragHandle = null; │ │ │ │ - } │ │ │ │ - if (this.radiusHandle) { │ │ │ │ - this.layer.destroyFeatures([this.radiusHandle], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.radiusHandle = null; │ │ │ │ - } │ │ │ │ - if (this.feature && │ │ │ │ - this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") { │ │ │ │ - if ((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) { │ │ │ │ - this.collectDragHandle(); │ │ │ │ - } │ │ │ │ - if ((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE | │ │ │ │ - OpenLayers.Control.ModifyFeature.RESIZE))) { │ │ │ │ - this.collectRadiusHandle(); │ │ │ │ - } │ │ │ │ - if (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE) { │ │ │ │ - // Don't collect vertices when we're resizing │ │ │ │ - if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)) { │ │ │ │ - this.collectVertices(); │ │ │ │ + }, │ │ │ │ + "offering": function(options) { │ │ │ │ + return this.createElementNSPlus("offering", { │ │ │ │ + value: options.offering │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "observedProperty": function(observedProperty) { │ │ │ │ + return this.createElementNSPlus("observedProperty", { │ │ │ │ + value: observedProperty │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "eventTime": function(options) { │ │ │ │ + var node = this.createElementNSPlus("eventTime"); │ │ │ │ + if (options.eventTime === 'latest') { │ │ │ │ + this.writeNode("ogc:TM_Equals", options, node); │ │ │ │ } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "resultModel": function(options) { │ │ │ │ + return this.createElementNSPlus("resultModel", { │ │ │ │ + value: options.resultModel │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "responseMode": function(options) { │ │ │ │ + return this.createElementNSPlus("responseMode", { │ │ │ │ + value: options.responseMode │ │ │ │ + }); │ │ │ │ } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: handleKeypress │ │ │ │ - * Called by the feature handler on keypress. This is used to delete │ │ │ │ - * vertices. If the <deleteCode> property is set, vertices will │ │ │ │ - * be deleted when a feature is selected for modification and │ │ │ │ - * the mouse is over a vertex. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} Keypress event. │ │ │ │ - */ │ │ │ │ - handleKeypress: function(evt) { │ │ │ │ - var code = evt.keyCode; │ │ │ │ - │ │ │ │ - // check for delete key │ │ │ │ - if (this.feature && │ │ │ │ - OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) { │ │ │ │ - var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt); │ │ │ │ - if (vertex && │ │ │ │ - OpenLayers.Util.indexOf(this.vertices, vertex) != -1 && │ │ │ │ - !this.handlers.drag.dragging && vertex.geometry.parent) { │ │ │ │ - // remove the vertex │ │ │ │ - vertex.geometry.parent.removeComponent(vertex.geometry); │ │ │ │ - this.layer.events.triggerEvent("vertexremoved", { │ │ │ │ - vertex: vertex.geometry, │ │ │ │ - feature: this.feature, │ │ │ │ - pixel: evt.xy │ │ │ │ + }, │ │ │ │ + "ogc": { │ │ │ │ + "TM_Equals": function(options) { │ │ │ │ + var node = this.createElementNSPlus("ogc:TM_Equals"); │ │ │ │ + this.writeNode("ogc:PropertyName", { │ │ │ │ + property: "urn:ogc:data:time:iso8601" │ │ │ │ + }, node); │ │ │ │ + if (options.eventTime === 'latest') { │ │ │ │ + this.writeNode("gml:TimeInstant", { │ │ │ │ + value: 'latest' │ │ │ │ + }, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "PropertyName": function(options) { │ │ │ │ + return this.createElementNSPlus("ogc:PropertyName", { │ │ │ │ + value: options.property │ │ │ │ }); │ │ │ │ - this.layer.drawFeature(this.feature, this.standalone ? │ │ │ │ - undefined : 'select'); │ │ │ │ - this.modified = true; │ │ │ │ - this.resetVertices(); │ │ │ │ - this.setFeatureState(); │ │ │ │ - this.onModification(this.feature); │ │ │ │ - this.layer.events.triggerEvent("featuremodified", { │ │ │ │ - feature: this.feature │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "gml": { │ │ │ │ + "TimeInstant": function(options) { │ │ │ │ + var node = this.createElementNSPlus("gml:TimeInstant"); │ │ │ │ + this.writeNode("gml:timePosition", options, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "timePosition": function(options) { │ │ │ │ + var node = this.createElementNSPlus("gml:timePosition", { │ │ │ │ + value: options.value │ │ │ │ }); │ │ │ │ + return node; │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: collectVertices │ │ │ │ - * Collect the vertices from the modifiable feature's geometry and push │ │ │ │ - * them on to the control's vertices array. │ │ │ │ - */ │ │ │ │ - collectVertices: function() { │ │ │ │ - this.vertices = []; │ │ │ │ - this.virtualVertices = []; │ │ │ │ - var control = this; │ │ │ │ + CLASS_NAME: "OpenLayers.Format.SOSGetObservation" │ │ │ │ │ │ │ │ - function collectComponentVertices(geometry) { │ │ │ │ - var i, vertex, component, len; │ │ │ │ - if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - vertex = new OpenLayers.Feature.Vector(geometry); │ │ │ │ - vertex._sketch = true; │ │ │ │ - vertex.renderIntent = control.vertexRenderIntent; │ │ │ │ - control.vertices.push(vertex); │ │ │ │ - } else { │ │ │ │ - var numVert = geometry.components.length; │ │ │ │ - if (geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { │ │ │ │ - numVert -= 1; │ │ │ │ - } │ │ │ │ - for (i = 0; i < numVert; ++i) { │ │ │ │ - component = geometry.components[i]; │ │ │ │ - if (component.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - vertex = new OpenLayers.Feature.Vector(component); │ │ │ │ - vertex._sketch = true; │ │ │ │ - vertex.renderIntent = control.vertexRenderIntent; │ │ │ │ - control.vertices.push(vertex); │ │ │ │ - } else { │ │ │ │ - collectComponentVertices(component); │ │ │ │ - } │ │ │ │ - } │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WMSCapabilities.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - // add virtual vertices in the middle of each edge │ │ │ │ - if (control.createVertices && geometry.CLASS_NAME != "OpenLayers.Geometry.MultiPoint") { │ │ │ │ - for (i = 0, len = geometry.components.length; i < len - 1; ++i) { │ │ │ │ - var prevVertex = geometry.components[i]; │ │ │ │ - var nextVertex = geometry.components[i + 1]; │ │ │ │ - if (prevVertex.CLASS_NAME == "OpenLayers.Geometry.Point" && │ │ │ │ - nextVertex.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - var x = (prevVertex.x + nextVertex.x) / 2; │ │ │ │ - var y = (prevVertex.y + nextVertex.y) / 2; │ │ │ │ - var point = new OpenLayers.Feature.Vector( │ │ │ │ - new OpenLayers.Geometry.Point(x, y), │ │ │ │ - null, control.virtualStyle │ │ │ │ - ); │ │ │ │ - // set the virtual parent and intended index │ │ │ │ - point.geometry.parent = geometry; │ │ │ │ - point._index = i + 1; │ │ │ │ - point._sketch = true; │ │ │ │ - control.virtualVertices.push(point); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - collectComponentVertices.call(this, this.feature.geometry); │ │ │ │ - this.layer.addFeatures(this.virtualVertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.layer.addFeatures(this.vertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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: collectDragHandle │ │ │ │ - * Collect the drag handle for the selected geometry. │ │ │ │ - */ │ │ │ │ - collectDragHandle: function() { │ │ │ │ - var geometry = this.feature.geometry; │ │ │ │ - var center = geometry.getBounds().getCenterLonLat(); │ │ │ │ - var originGeometry = new OpenLayers.Geometry.Point( │ │ │ │ - center.lon, center.lat │ │ │ │ - ); │ │ │ │ - var origin = new OpenLayers.Feature.Vector(originGeometry); │ │ │ │ - originGeometry.move = function(x, y) { │ │ │ │ - OpenLayers.Geometry.Point.prototype.move.call(this, x, y); │ │ │ │ - geometry.move(x, y); │ │ │ │ - }; │ │ │ │ - origin._sketch = true; │ │ │ │ - this.dragHandle = origin; │ │ │ │ - this.dragHandle.renderIntent = this.vertexRenderIntent; │ │ │ │ - this.layer.addFeatures([this.dragHandle], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WMSCapabilities │ │ │ │ + * Read WMS Capabilities. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WMSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: collectRadiusHandle │ │ │ │ - * Collect the radius handle for the selected geometry. │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ + * {String} Version number to assume if none found. Default is "1.1.1". │ │ │ │ */ │ │ │ │ - collectRadiusHandle: function() { │ │ │ │ - var geometry = this.feature.geometry; │ │ │ │ - var bounds = geometry.getBounds(); │ │ │ │ - var center = bounds.getCenterLonLat(); │ │ │ │ - var originGeometry = new OpenLayers.Geometry.Point( │ │ │ │ - center.lon, center.lat │ │ │ │ - ); │ │ │ │ - var radiusGeometry = new OpenLayers.Geometry.Point( │ │ │ │ - bounds.right, bounds.bottom │ │ │ │ - ); │ │ │ │ - var radius = new OpenLayers.Feature.Vector(radiusGeometry); │ │ │ │ - var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE); │ │ │ │ - var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE); │ │ │ │ - var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE); │ │ │ │ - │ │ │ │ - radiusGeometry.move = function(x, y) { │ │ │ │ - OpenLayers.Geometry.Point.prototype.move.call(this, x, y); │ │ │ │ - var dx1 = this.x - originGeometry.x; │ │ │ │ - var dy1 = this.y - originGeometry.y; │ │ │ │ - var dx0 = dx1 - x; │ │ │ │ - var dy0 = dy1 - y; │ │ │ │ - if (rotate) { │ │ │ │ - var a0 = Math.atan2(dy0, dx0); │ │ │ │ - var a1 = Math.atan2(dy1, dx1); │ │ │ │ - var angle = a1 - a0; │ │ │ │ - angle *= 180 / Math.PI; │ │ │ │ - geometry.rotate(angle, originGeometry); │ │ │ │ - } │ │ │ │ - if (resize) { │ │ │ │ - var scale, ratio; │ │ │ │ - // 'resize' together with 'reshape' implies that the aspect │ │ │ │ - // ratio of the geometry will not be preserved whilst resizing │ │ │ │ - if (reshape) { │ │ │ │ - scale = dy1 / dy0; │ │ │ │ - ratio = (dx1 / dx0) / scale; │ │ │ │ - } else { │ │ │ │ - var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0)); │ │ │ │ - var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1)); │ │ │ │ - scale = l1 / l0; │ │ │ │ - } │ │ │ │ - geometry.resize(scale, originGeometry, ratio); │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - radius._sketch = true; │ │ │ │ - this.radiusHandle = radius; │ │ │ │ - this.radiusHandle.renderIntent = this.vertexRenderIntent; │ │ │ │ - this.layer.addFeatures([this.radiusHandle], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ + defaultVersion: "1.1.1", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the control and all handlers. │ │ │ │ + * APIProperty: profile │ │ │ │ + * {String} If provided, use a custom profile. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} The control's map. │ │ │ │ + * Currently supported profiles: │ │ │ │ + * - WMSC - parses vendor specific capabilities for WMS-C. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - this.handlers.drag.setMap(map); │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + profile: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleMapEvents │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Format.WMSCapabilities │ │ │ │ + * Create a new parser for WMS capabilities. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - handleMapEvents: function(evt) { │ │ │ │ - if (evt.type == "removelayer" || evt.property == "order") { │ │ │ │ - this.moveLayerToTop(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveLayerToTop │ │ │ │ - * Moves the layer for this handler to the top, so mouse events can reach │ │ │ │ - * it. │ │ │ │ + * APIMethod: read │ │ │ │ + * Read capabilities data from a string, and return a list of layers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} List of named layers. │ │ │ │ */ │ │ │ │ - moveLayerToTop: function() { │ │ │ │ - var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1, │ │ │ │ - this.layer.getZIndex()) + 1; │ │ │ │ - this.layer.setZIndex(index); │ │ │ │ - │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: moveLayerBack │ │ │ │ - * Moves the layer back to the position determined by the map's layers │ │ │ │ - * array. │ │ │ │ - */ │ │ │ │ - moveLayerBack: function() { │ │ │ │ - var index = this.layer.getZIndex() - 1; │ │ │ │ - if (index >= this.map.Z_INDEX_BASE['Feature']) { │ │ │ │ - this.layer.setZIndex(index); │ │ │ │ - } else { │ │ │ │ - this.map.setLayerZIndex(this.layer, │ │ │ │ - this.map.getLayerIndex(this.layer)); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSCapabilities" │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ModifyFeature" │ │ │ │ }); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: RESHAPE │ │ │ │ - * {Integer} Constant used to make the control work in reshape mode │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.ModifyFeature.RESHAPE = 1; │ │ │ │ -/** │ │ │ │ - * Constant: RESIZE │ │ │ │ - * {Integer} Constant used to make the control work in resize mode │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.ModifyFeature.RESIZE = 2; │ │ │ │ -/** │ │ │ │ - * Constant: ROTATE │ │ │ │ - * {Integer} Constant used to make the control work in rotate mode │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.ModifyFeature.ROTATE = 4; │ │ │ │ -/** │ │ │ │ - * Constant: DRAG │ │ │ │ - * {Integer} Constant used to make the control work in drag mode │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.ModifyFeature.DRAG = 8; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/Pan.js │ │ │ │ + OpenLayers/Format/WFSDescribeFeatureType.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control/Button.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Format/OGCExceptionReport.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.Pan │ │ │ │ - * The Pan control is a single button to pan the map in one direction. For │ │ │ │ - * a more complete control see <OpenLayers.Control.PanPanel>. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Format.WFSDescribeFeatureType │ │ │ │ + * Read WFS DescribeFeatureType response │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.Pan = OpenLayers.Class(OpenLayers.Control.Button, { │ │ │ │ +OpenLayers.Format.WFSDescribeFeatureType = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: slideFactor │ │ │ │ - * {Integer} Number of pixels by which we'll pan the map in any direction │ │ │ │ - * on clicking the arrow buttons, defaults to 50. If you want to pan │ │ │ │ - * by some ratio of the map dimensions, use <slideRatio> instead. │ │ │ │ - */ │ │ │ │ - slideFactor: 50, │ │ │ │ + /** │ │ │ │ + * Property: regExes │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ + */ │ │ │ │ + regExes: { │ │ │ │ + trimSpace: (/^\s*|\s*$/g) │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: slideRatio │ │ │ │ - * {Number} The fraction of map width/height by which we'll pan the map │ │ │ │ - * on clicking the arrow buttons. Default is null. If set, will │ │ │ │ - * override <slideFactor>. E.g. if slideRatio is .5, then Pan Up will │ │ │ │ - * pan up half the map height. │ │ │ │ - */ │ │ │ │ - slideRatio: null, │ │ │ │ + /** │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + */ │ │ │ │ + namespaces: { │ │ │ │ + xsd: "http://www.w3.org/2001/XMLSchema" │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: direction │ │ │ │ - * {String} in {'North', 'South', 'East', 'West'} │ │ │ │ - */ │ │ │ │ - direction: null, │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WFSDescribeFeatureType │ │ │ │ + * Create a new parser for WFS DescribeFeatureType responses. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.Pan │ │ │ │ - * Control which handles the panning (in any of the cardinal directions) │ │ │ │ - * of the map by a set px distance. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * direction - {String} The direction this button should pan. │ │ │ │ - * options - {Object} An optional object whose properties will be used │ │ │ │ - * to extend the control. │ │ │ │ - */ │ │ │ │ - initialize: function(direction, options) { │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "xsd": { │ │ │ │ + "schema": function(node, obj) { │ │ │ │ + var complexTypes = []; │ │ │ │ + var customTypes = {}; │ │ │ │ + var schema = { │ │ │ │ + complexTypes: complexTypes, │ │ │ │ + customTypes: customTypes │ │ │ │ + }; │ │ │ │ + var i, len; │ │ │ │ │ │ │ │ - this.direction = direction; │ │ │ │ - this.CLASS_NAME += this.direction; │ │ │ │ + this.readChildNodes(node, schema); │ │ │ │ │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - }, │ │ │ │ + var attributes = node.attributes; │ │ │ │ + var attr, name; │ │ │ │ + for (i = 0, len = attributes.length; i < len; ++i) { │ │ │ │ + attr = attributes[i]; │ │ │ │ + name = attr.name; │ │ │ │ + if (name.indexOf("xmlns") === 0) { │ │ │ │ + this.setNamespace(name.split(":")[1] || "", attr.value); │ │ │ │ + } else { │ │ │ │ + obj[name] = attr.value; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + obj.featureTypes = complexTypes; │ │ │ │ + obj.targetPrefix = this.namespaceAlias[obj.targetNamespace]; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: trigger │ │ │ │ - */ │ │ │ │ - trigger: function() { │ │ │ │ - if (this.map) { │ │ │ │ - var getSlideFactor = OpenLayers.Function.bind(function(dim) { │ │ │ │ - return this.slideRatio ? │ │ │ │ - this.map.getSize()[dim] * this.slideRatio : │ │ │ │ - this.slideFactor; │ │ │ │ - }, this); │ │ │ │ + // map complexTypes to names of customTypes │ │ │ │ + var complexType, customType; │ │ │ │ + for (i = 0, len = complexTypes.length; i < len; ++i) { │ │ │ │ + complexType = complexTypes[i]; │ │ │ │ + customType = customTypes[complexType.typeName]; │ │ │ │ + if (customTypes[complexType.typeName]) { │ │ │ │ + complexType.typeName = customType.name; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "complexType": function(node, obj) { │ │ │ │ + var complexType = { │ │ │ │ + // this is a temporary typeName, it will be overwritten by │ │ │ │ + // the schema reader with the metadata found in the │ │ │ │ + // customTypes hash │ │ │ │ + "typeName": node.getAttribute("name") │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, complexType); │ │ │ │ + obj.complexTypes.push(complexType); │ │ │ │ + }, │ │ │ │ + "complexContent": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "extension": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "sequence": function(node, obj) { │ │ │ │ + var sequence = { │ │ │ │ + elements: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, sequence); │ │ │ │ + obj.properties = sequence.elements; │ │ │ │ + }, │ │ │ │ + "element": function(node, obj) { │ │ │ │ + var type; │ │ │ │ + if (obj.elements) { │ │ │ │ + var element = {}; │ │ │ │ + var attributes = node.attributes; │ │ │ │ + var attr; │ │ │ │ + for (var i = 0, len = attributes.length; i < len; ++i) { │ │ │ │ + attr = attributes[i]; │ │ │ │ + element[attr.name] = attr.value; │ │ │ │ + } │ │ │ │ │ │ │ │ - switch (this.direction) { │ │ │ │ - case OpenLayers.Control.Pan.NORTH: │ │ │ │ - this.map.pan(0, -getSlideFactor("h")); │ │ │ │ - break; │ │ │ │ - case OpenLayers.Control.Pan.SOUTH: │ │ │ │ - this.map.pan(0, getSlideFactor("h")); │ │ │ │ - break; │ │ │ │ - case OpenLayers.Control.Pan.WEST: │ │ │ │ - this.map.pan(-getSlideFactor("w"), 0); │ │ │ │ - break; │ │ │ │ - case OpenLayers.Control.Pan.EAST: │ │ │ │ - this.map.pan(getSlideFactor("w"), 0); │ │ │ │ - break; │ │ │ │ + type = element.type; │ │ │ │ + if (!type) { │ │ │ │ + type = {}; │ │ │ │ + this.readChildNodes(node, type); │ │ │ │ + element.restriction = type; │ │ │ │ + element.type = type.base; │ │ │ │ + } │ │ │ │ + var fullType = type.base || type; │ │ │ │ + element.localType = fullType.split(":").pop(); │ │ │ │ + obj.elements.push(element); │ │ │ │ + this.readChildNodes(node, element); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (obj.complexTypes) { │ │ │ │ + type = node.getAttribute("type"); │ │ │ │ + var localType = type.split(":").pop(); │ │ │ │ + obj.customTypes[localType] = { │ │ │ │ + "name": node.getAttribute("name"), │ │ │ │ + "type": type │ │ │ │ + }; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "annotation": function(node, obj) { │ │ │ │ + obj.annotation = {}; │ │ │ │ + this.readChildNodes(node, obj.annotation); │ │ │ │ + }, │ │ │ │ + "appinfo": function(node, obj) { │ │ │ │ + if (!obj.appinfo) { │ │ │ │ + obj.appinfo = []; │ │ │ │ + } │ │ │ │ + obj.appinfo.push(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "documentation": function(node, obj) { │ │ │ │ + if (!obj.documentation) { │ │ │ │ + obj.documentation = []; │ │ │ │ + } │ │ │ │ + var value = this.getChildValue(node); │ │ │ │ + obj.documentation.push({ │ │ │ │ + lang: node.getAttribute("xml:lang"), │ │ │ │ + textContent: value.replace(this.regExes.trimSpace, "") │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "simpleType": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "restriction": function(node, obj) { │ │ │ │ + obj.base = node.getAttribute("base"); │ │ │ │ + this.readRestriction(node, obj); │ │ │ │ + } │ │ │ │ } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Pan" │ │ │ │ -}); │ │ │ │ + /** │ │ │ │ + * Method: readRestriction │ │ │ │ + * Reads restriction defined in the child nodes of a restriction element │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} the node to parse │ │ │ │ + * obj - {Object} the object that receives the read result │ │ │ │ + */ │ │ │ │ + readRestriction: function(node, obj) { │ │ │ │ + var children = node.childNodes; │ │ │ │ + var child, nodeName, value; │ │ │ │ + for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ + child = children[i]; │ │ │ │ + if (child.nodeType == 1) { │ │ │ │ + nodeName = child.nodeName.split(":").pop(); │ │ │ │ + value = child.getAttribute("value"); │ │ │ │ + if (!obj[nodeName]) { │ │ │ │ + obj[nodeName] = value; │ │ │ │ + } else { │ │ │ │ + if (typeof obj[nodeName] == "string") { │ │ │ │ + obj[nodeName] = [obj[nodeName]]; │ │ │ │ + } │ │ │ │ + obj[nodeName].push(value); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ -OpenLayers.Control.Pan.NORTH = "North"; │ │ │ │ -OpenLayers.Control.Pan.SOUTH = "South"; │ │ │ │ -OpenLayers.Control.Pan.EAST = "East"; │ │ │ │ -OpenLayers.Control.Pan.WEST = "West"; │ │ │ │ + /** │ │ │ │ + * Method: read │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {DOMElement|String} A WFS DescribeFeatureType document. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An object representing the WFS DescribeFeatureType response. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ + } │ │ │ │ + var schema = {}; │ │ │ │ + if (data.nodeName.split(":").pop() === 'ExceptionReport') { │ │ │ │ + // an exception must have occurred, so parse it │ │ │ │ + var parser = new OpenLayers.Format.OGCExceptionReport(); │ │ │ │ + schema.error = parser.read(data); │ │ │ │ + } else { │ │ │ │ + this.readNode(data, schema); │ │ │ │ + } │ │ │ │ + return schema; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFSDescribeFeatureType" │ │ │ │ + │ │ │ │ + }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/PanPanel.js │ │ │ │ + OpenLayers/Format/WCSCapabilities.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control/Panel.js │ │ │ │ - * @requires OpenLayers/Control/Pan.js │ │ │ │ + * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.PanPanel │ │ │ │ - * The PanPanel is visible control for panning the map North, South, East or │ │ │ │ - * West in small steps. By default it is drawn in the top left corner of the │ │ │ │ - * map. │ │ │ │ - * │ │ │ │ - * Note: │ │ │ │ - * If you wish to use this class with the default images and you want │ │ │ │ - * it to look nice in ie6, you should add the following, conditionally │ │ │ │ - * added css stylesheet to your HTML file: │ │ │ │ + * Class: OpenLayers.Format.WCSCapabilities │ │ │ │ + * Read WCS Capabilities. │ │ │ │ * │ │ │ │ - * (code) │ │ │ │ - * <!--[if lte IE 6]> │ │ │ │ - * <link rel="stylesheet" href="../theme/default/ie6-style.css" type="text/css" /> │ │ │ │ - * <![endif]--> │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control.Panel> │ │ │ │ + * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.PanPanel = OpenLayers.Class(OpenLayers.Control.Panel, { │ │ │ │ +OpenLayers.Format.WCSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: slideFactor │ │ │ │ - * {Integer} Number of pixels by which we'll pan the map in any direction │ │ │ │ - * on clicking the arrow buttons, defaults to 50. If you want to pan │ │ │ │ - * by some ratio of the map dimensions, use <slideRatio> instead. │ │ │ │ + /** │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ + * {String} Version number to assume if none found. Default is "1.1.0". │ │ │ │ */ │ │ │ │ - slideFactor: 50, │ │ │ │ + defaultVersion: "1.1.0", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: slideRatio │ │ │ │ - * {Number} The fraction of map width/height by which we'll pan the map │ │ │ │ - * on clicking the arrow buttons. Default is null. If set, will │ │ │ │ - * override <slideFactor>. E.g. if slideRatio is .5, then Pan Up will │ │ │ │ - * pan up half the map height. │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WCSCapabilities │ │ │ │ + * Create a new parser for WCS capabilities. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - slideRatio: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.PanPanel │ │ │ │ - * Add the four directional pan buttons. │ │ │ │ + * APIMethod: read │ │ │ │ + * Read capabilities data from a string, and return a list of coverages. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be used │ │ │ │ - * to extend the control. │ │ │ │ + * Returns: │ │ │ │ + * {Array} List of named coverages. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); │ │ │ │ - var options = { │ │ │ │ - slideFactor: this.slideFactor, │ │ │ │ - slideRatio: this.slideRatio │ │ │ │ - }; │ │ │ │ - this.addControls([ │ │ │ │ - new OpenLayers.Control.Pan(OpenLayers.Control.Pan.NORTH, options), │ │ │ │ - new OpenLayers.Control.Pan(OpenLayers.Control.Pan.SOUTH, options), │ │ │ │ - new OpenLayers.Control.Pan(OpenLayers.Control.Pan.EAST, options), │ │ │ │ - new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST, options) │ │ │ │ - ]); │ │ │ │ - }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.PanPanel" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WCSCapabilities" │ │ │ │ + │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/GetFeature.js │ │ │ │ + OpenLayers/Format/GeoRSS.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Handler/Click.js │ │ │ │ - * @requires OpenLayers/Handler/Box.js │ │ │ │ - * @requires OpenLayers/Handler/Hover.js │ │ │ │ - * @requires OpenLayers/Filter/Spatial.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Geometry/Point.js │ │ │ │ + * @requires OpenLayers/Geometry/LineString.js │ │ │ │ + * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.GetFeature │ │ │ │ - * Gets vector features for locations underneath the mouse cursor. Can be │ │ │ │ - * configured to act on click, hover or dragged boxes. Uses an │ │ │ │ - * <OpenLayers.Protocol> that supports spatial filters to retrieve │ │ │ │ - * features from a server and fires events that notify applications of the │ │ │ │ - * selected features. │ │ │ │ + * Class: OpenLayers.Format.GeoRSS │ │ │ │ + * Read/write GeoRSS parser. Create a new instance with the │ │ │ │ + * <OpenLayers.Format.GeoRSS> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.GetFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: protocol │ │ │ │ - * {<OpenLayers.Protocol>} Required. The protocol used for fetching │ │ │ │ - * features. │ │ │ │ - */ │ │ │ │ - protocol: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: multipleKey │ │ │ │ - * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets │ │ │ │ - * the <multiple> property to true. Default is null. │ │ │ │ - */ │ │ │ │ - multipleKey: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: toggleKey │ │ │ │ - * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets │ │ │ │ - * the <toggle> property to true. Default is null. │ │ │ │ - */ │ │ │ │ - toggleKey: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: modifiers │ │ │ │ - * {Object} The event modifiers to use, according to the current event │ │ │ │ - * being handled by this control's handlers │ │ │ │ - */ │ │ │ │ - modifiers: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: multiple │ │ │ │ - * {Boolean} Allow selection of multiple geometries. Default is false. │ │ │ │ - */ │ │ │ │ - multiple: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: click │ │ │ │ - * {Boolean} Use a click handler for selecting/unselecting features. If │ │ │ │ - * both <click> and <box> are set to true, the click handler takes │ │ │ │ - * precedence over the box handler if a box with zero extent was │ │ │ │ - * selected. Default is true. │ │ │ │ - */ │ │ │ │ - click: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: single │ │ │ │ - * {Boolean} Tells whether select by click should select a single │ │ │ │ - * feature. If set to false, all matching features are selected. │ │ │ │ - * If set to true, only the best matching feature is selected. │ │ │ │ - * This option has an effect only of the <click> option is set │ │ │ │ - * to true. Default is true. │ │ │ │ - */ │ │ │ │ - single: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: clickout │ │ │ │ - * {Boolean} Unselect features when clicking outside any feature. │ │ │ │ - * Applies only if <click> is true. Default is true. │ │ │ │ - */ │ │ │ │ - clickout: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: toggle │ │ │ │ - * {Boolean} Unselect a selected feature on click. Applies only if │ │ │ │ - * <click> is true. Default is false. │ │ │ │ - */ │ │ │ │ - toggle: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: clickTolerance │ │ │ │ - * {Integer} Tolerance for the filter query in pixels. This has the │ │ │ │ - * same effect as the tolerance parameter on WMS GetFeatureInfo │ │ │ │ - * requests. Will be ignored for box selections. Applies only if │ │ │ │ - * <click> or <hover> is true. Default is 5. Note that this not │ │ │ │ - * only affects requests on click, but also on hover. │ │ │ │ - */ │ │ │ │ - clickTolerance: 5, │ │ │ │ +OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: hover │ │ │ │ - * {Boolean} Send feature requests on mouse moves. Default is false. │ │ │ │ + * APIProperty: rssns │ │ │ │ + * {String} RSS namespace to use. Defaults to │ │ │ │ + * "http://backend.userland.com/rss2" │ │ │ │ */ │ │ │ │ - hover: false, │ │ │ │ + rssns: "http://backend.userland.com/rss2", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: box │ │ │ │ - * {Boolean} Allow feature selection by drawing a box. If set to │ │ │ │ - * true set <click> to false to disable the click handler and │ │ │ │ - * rely on the box handler only, even for "zero extent" boxes. │ │ │ │ - * See the description of the <click> option for additional │ │ │ │ - * information. Default is false. │ │ │ │ + * APIProperty: featurens │ │ │ │ + * {String} Feature Attributes namespace. Defaults to │ │ │ │ + * "http://mapserver.gis.umn.edu/mapserver" │ │ │ │ */ │ │ │ │ - box: false, │ │ │ │ + featureNS: "http://mapserver.gis.umn.edu/mapserver", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: maxFeatures │ │ │ │ - * {Integer} Maximum number of features to return from a query in single mode │ │ │ │ - * if supported by the <protocol>. This set of features is then used to │ │ │ │ - * determine the best match client-side. Default is 10. │ │ │ │ + * APIProperty: georssns │ │ │ │ + * {String} GeoRSS namespace to use. Defaults to │ │ │ │ + * "http://www.georss.org/georss" │ │ │ │ */ │ │ │ │ - maxFeatures: 10, │ │ │ │ + georssns: "http://www.georss.org/georss", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: features │ │ │ │ - * {Object} Hash of {<OpenLayers.Feature.Vector>}, keyed by fid, holding │ │ │ │ - * the currently selected features │ │ │ │ + * APIProperty: geons │ │ │ │ + * {String} W3C Geo namespace to use. Defaults to │ │ │ │ + * "http://www.w3.org/2003/01/geo/wgs84_pos#" │ │ │ │ */ │ │ │ │ - features: null, │ │ │ │ + geons: "http://www.w3.org/2003/01/geo/wgs84_pos#", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Proeprty: hoverFeature │ │ │ │ - * {<OpenLayers.Feature.Vector>} The feature currently selected by the │ │ │ │ - * hover handler │ │ │ │ + * APIProperty: featureTitle │ │ │ │ + * {String} Default title for features. Defaults to "Untitled" │ │ │ │ */ │ │ │ │ - hoverFeature: null, │ │ │ │ + featureTitle: "Untitled", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: handlerOptions │ │ │ │ - * {Object} Additional options for the handlers used by this control. This │ │ │ │ - * is a hash with the keys "click", "box" and "hover". │ │ │ │ + * APIProperty: featureDescription │ │ │ │ + * {String} Default description for features. Defaults to "No Description" │ │ │ │ */ │ │ │ │ + featureDescription: "No Description", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: handlers │ │ │ │ - * {Object} Object with references to multiple <OpenLayers.Handler> │ │ │ │ - * instances. │ │ │ │ + * Property: gmlParse │ │ │ │ + * {Object} GML Format object for parsing features │ │ │ │ + * Non-API and only created if necessary │ │ │ │ */ │ │ │ │ - handlers: null, │ │ │ │ + gmlParser: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: hoverResponse │ │ │ │ - * {<OpenLayers.Protocol.Response>} The response object associated with │ │ │ │ - * the currently running hover request (if any). │ │ │ │ + * APIProperty: xy │ │ │ │ + * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x) │ │ │ │ + * For GeoRSS the default is (y,x), therefore: false │ │ │ │ */ │ │ │ │ - hoverResponse: null, │ │ │ │ + xy: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: filterType │ │ │ │ - * {<String>} The type of filter to use when sending off a request. │ │ │ │ - * Possible values: │ │ │ │ - * OpenLayers.Filter.Spatial.<BBOX|INTERSECTS|WITHIN|CONTAINS> │ │ │ │ - * Defaults to: OpenLayers.Filter.Spatial.BBOX │ │ │ │ - */ │ │ │ │ - filterType: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ + * Constructor: OpenLayers.Format.GeoRSS │ │ │ │ + * Create a new parser for GeoRSS. │ │ │ │ * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * beforefeatureselected - Triggered when <click> is true before a │ │ │ │ - * feature is selected. The event object has a feature property with │ │ │ │ - * the feature about to select │ │ │ │ - * featureselected - Triggered when <click> is true and a feature is │ │ │ │ - * selected. The event object has a feature property with the │ │ │ │ - * selected feature │ │ │ │ - * beforefeaturesselected - Triggered when <click> is true before a │ │ │ │ - * set of features is selected. The event object is an array of │ │ │ │ - * feature properties with the features about to be selected. │ │ │ │ - * Return false after receiving this event to discontinue processing │ │ │ │ - * of all featureselected events and the featuresselected event. │ │ │ │ - * featuresselected - Triggered when <click> is true and a set of │ │ │ │ - * features is selected. The event object is an array of feature │ │ │ │ - * properties of the selected features │ │ │ │ - * featureunselected - Triggered when <click> is true and a feature is │ │ │ │ - * unselected. The event object has a feature property with the │ │ │ │ - * unselected feature │ │ │ │ - * clickout - Triggered when when <click> is true and no feature was │ │ │ │ - * selected. │ │ │ │ - * hoverfeature - Triggered when <hover> is true and the mouse has │ │ │ │ - * stopped over a feature │ │ │ │ - * outfeature - Triggered when <hover> is true and the mouse moves │ │ │ │ - * moved away from a hover-selected feature │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.GetFeature │ │ │ │ - * Create a new control for fetching remote features. │ │ │ │ + * Method: createGeometryFromItem │ │ │ │ + * Return a geometry from a GeoRSS Item. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} A configuration object which at least has to contain │ │ │ │ - * a <protocol> property (if not, it has to be set before a request is │ │ │ │ - * made) │ │ │ │ + * item - {DOMElement} A GeoRSS item node. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} A geometry representing the node. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - options.handlerOptions = options.handlerOptions || {}; │ │ │ │ + createGeometryFromItem: function(item) { │ │ │ │ + var point = this.getElementsByTagNameNS(item, this.georssns, "point"); │ │ │ │ + var lat = this.getElementsByTagNameNS(item, this.geons, 'lat'); │ │ │ │ + var lon = this.getElementsByTagNameNS(item, this.geons, 'long'); │ │ │ │ │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + var line = this.getElementsByTagNameNS(item, │ │ │ │ + this.georssns, │ │ │ │ + "line"); │ │ │ │ + var polygon = this.getElementsByTagNameNS(item, │ │ │ │ + this.georssns, │ │ │ │ + "polygon"); │ │ │ │ + var where = this.getElementsByTagNameNS(item, │ │ │ │ + this.georssns, │ │ │ │ + "where"); │ │ │ │ + var box = this.getElementsByTagNameNS(item, │ │ │ │ + this.georssns, │ │ │ │ + "box"); │ │ │ │ │ │ │ │ - this.features = {}; │ │ │ │ + if (point.length > 0 || (lat.length > 0 && lon.length > 0)) { │ │ │ │ + var location; │ │ │ │ + if (point.length > 0) { │ │ │ │ + location = OpenLayers.String.trim( │ │ │ │ + point[0].firstChild.nodeValue).split(/\s+/); │ │ │ │ + if (location.length != 2) { │ │ │ │ + location = OpenLayers.String.trim( │ │ │ │ + point[0].firstChild.nodeValue).split(/\s*,\s*/); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + location = [parseFloat(lat[0].firstChild.nodeValue), │ │ │ │ + parseFloat(lon[0].firstChild.nodeValue) │ │ │ │ + ]; │ │ │ │ + } │ │ │ │ │ │ │ │ - this.handlers = {}; │ │ │ │ + var geometry = new OpenLayers.Geometry.Point(location[1], location[0]); │ │ │ │ │ │ │ │ - if (this.click) { │ │ │ │ - this.handlers.click = new OpenLayers.Handler.Click(this, { │ │ │ │ - click: this.selectClick │ │ │ │ - }, this.handlerOptions.click || {}); │ │ │ │ + } else if (line.length > 0) { │ │ │ │ + var coords = OpenLayers.String.trim(this.getChildValue(line[0])).split(/\s+/); │ │ │ │ + var components = []; │ │ │ │ + var point; │ │ │ │ + for (var i = 0, len = coords.length; i < len; i += 2) { │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[i + 1], coords[i]); │ │ │ │ + components.push(point); │ │ │ │ + } │ │ │ │ + geometry = new OpenLayers.Geometry.LineString(components); │ │ │ │ + } else if (polygon.length > 0) { │ │ │ │ + var coords = OpenLayers.String.trim(this.getChildValue(polygon[0])).split(/\s+/); │ │ │ │ + var components = []; │ │ │ │ + var point; │ │ │ │ + for (var i = 0, len = coords.length; i < len; i += 2) { │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[i + 1], coords[i]); │ │ │ │ + components.push(point); │ │ │ │ + } │ │ │ │ + geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]); │ │ │ │ + } else if (where.length > 0) { │ │ │ │ + if (!this.gmlParser) { │ │ │ │ + this.gmlParser = new OpenLayers.Format.GML({ │ │ │ │ + 'xy': this.xy │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + var feature = this.gmlParser.parseFeature(where[0]); │ │ │ │ + geometry = feature.geometry; │ │ │ │ + } else if (box.length > 0) { │ │ │ │ + var coords = OpenLayers.String.trim(box[0].firstChild.nodeValue).split(/\s+/); │ │ │ │ + var components = []; │ │ │ │ + var point; │ │ │ │ + if (coords.length > 3) { │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[1], coords[0]); │ │ │ │ + components.push(point); │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[1], coords[2]); │ │ │ │ + components.push(point); │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[3], coords[2]); │ │ │ │ + components.push(point); │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[3], coords[0]); │ │ │ │ + components.push(point); │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[1], coords[0]); │ │ │ │ + components.push(point); │ │ │ │ + } │ │ │ │ + geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]); │ │ │ │ } │ │ │ │ │ │ │ │ - if (this.box) { │ │ │ │ - this.handlers.box = new OpenLayers.Handler.Box( │ │ │ │ - this, { │ │ │ │ - done: this.selectBox │ │ │ │ - }, │ │ │ │ - OpenLayers.Util.extend(this.handlerOptions.box, { │ │ │ │ - boxDivClassName: "olHandlerBoxSelectFeature" │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ + if (geometry && this.internalProjection && this.externalProjection) { │ │ │ │ + geometry.transform(this.externalProjection, │ │ │ │ + this.internalProjection); │ │ │ │ } │ │ │ │ │ │ │ │ - if (this.hover) { │ │ │ │ - this.handlers.hover = new OpenLayers.Handler.Hover( │ │ │ │ - this, { │ │ │ │ - 'move': this.cancelHover, │ │ │ │ - 'pause': this.selectHover │ │ │ │ - }, │ │ │ │ - OpenLayers.Util.extend(this.handlerOptions.hover, { │ │ │ │ - 'delay': 250, │ │ │ │ - 'pixelTolerance': 2 │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ + return geometry; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: activate │ │ │ │ - * Activates the control. │ │ │ │ - * │ │ │ │ + * Method: createFeatureFromItem │ │ │ │ + * Return a feature from a GeoRSS Item. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * item - {DOMElement} A GeoRSS item node. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The control was effectively activated. │ │ │ │ + * {<OpenLayers.Feature.Vector>} A feature representing the item. │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - if (!this.active) { │ │ │ │ - for (var i in this.handlers) { │ │ │ │ - this.handlers[i].activate(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return OpenLayers.Control.prototype.activate.apply( │ │ │ │ - this, arguments │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ + createFeatureFromItem: function(item) { │ │ │ │ + var geometry = this.createGeometryFromItem(item); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Deactivates the control. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The control was effectively deactivated. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - for (var i in this.handlers) { │ │ │ │ - this.handlers[i].deactivate(); │ │ │ │ + /* Provide defaults for title and description */ │ │ │ │ + var title = this._getChildValue(item, "*", "title", this.featureTitle); │ │ │ │ + │ │ │ │ + /* First try RSS descriptions, then Atom summaries */ │ │ │ │ + var description = this._getChildValue( │ │ │ │ + item, "*", "description", │ │ │ │ + this._getChildValue(item, "*", "content", │ │ │ │ + this._getChildValue(item, "*", "summary", this.featureDescription))); │ │ │ │ + │ │ │ │ + /* If no link URL is found in the first child node, try the │ │ │ │ + href attribute */ │ │ │ │ + var link = this._getChildValue(item, "*", "link"); │ │ │ │ + if (!link) { │ │ │ │ + try { │ │ │ │ + link = this.getElementsByTagNameNS(item, "*", "link")[0].getAttribute("href"); │ │ │ │ + } catch (e) { │ │ │ │ + link = null; │ │ │ │ } │ │ │ │ } │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply( │ │ │ │ - this, arguments │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: selectClick │ │ │ │ - * Called on click │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ - */ │ │ │ │ - selectClick: function(evt) { │ │ │ │ - var bounds = this.pixelToBounds(evt.xy); │ │ │ │ + var id = this._getChildValue(item, "*", "id", null); │ │ │ │ │ │ │ │ - this.setModifiers(evt); │ │ │ │ - this.request(bounds, { │ │ │ │ - single: this.single │ │ │ │ - }); │ │ │ │ + var data = { │ │ │ │ + "title": title, │ │ │ │ + "description": description, │ │ │ │ + "link": link │ │ │ │ + }; │ │ │ │ + var feature = new OpenLayers.Feature.Vector(geometry, data); │ │ │ │ + feature.fid = id; │ │ │ │ + return feature; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: selectBox │ │ │ │ - * Callback from the handlers.box set up when <box> selection is on │ │ │ │ + * Method: _getChildValue │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * position - {<OpenLayers.Bounds>|Object} An OpenLayers.Bounds or │ │ │ │ - * an object with a 'left', 'bottom', 'right' and 'top' properties. │ │ │ │ + * node - {DOMElement} │ │ │ │ + * nsuri - {String} Child node namespace uri ("*" for any). │ │ │ │ + * name - {String} Child node name. │ │ │ │ + * def - {String} Optional string default to return if no child found. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The value of the first child with the given tag name. Returns │ │ │ │ + * default value or empty string if none found. │ │ │ │ */ │ │ │ │ - selectBox: function(position) { │ │ │ │ - var bounds; │ │ │ │ - if (position instanceof OpenLayers.Bounds) { │ │ │ │ - var minXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.left, │ │ │ │ - y: position.bottom │ │ │ │ - }); │ │ │ │ - var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.right, │ │ │ │ - y: position.top │ │ │ │ - }); │ │ │ │ - bounds = new OpenLayers.Bounds( │ │ │ │ - minXY.lon, minXY.lat, maxXY.lon, maxXY.lat │ │ │ │ - ); │ │ │ │ - │ │ │ │ + _getChildValue: function(node, nsuri, name, def) { │ │ │ │ + var value; │ │ │ │ + var eles = this.getElementsByTagNameNS(node, nsuri, name); │ │ │ │ + if (eles && eles[0] && eles[0].firstChild && │ │ │ │ + eles[0].firstChild.nodeValue) { │ │ │ │ + value = this.getChildValue(eles[0]); │ │ │ │ } else { │ │ │ │ - if (this.click) { │ │ │ │ - // box without extent - let the click handler take care of it │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - bounds = this.pixelToBounds(position); │ │ │ │ + value = (def == undefined) ? "" : def; │ │ │ │ } │ │ │ │ - this.setModifiers(this.handlers.box.dragHandler.evt); │ │ │ │ - this.request(bounds); │ │ │ │ + return value; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: selectHover │ │ │ │ - * Callback from the handlers.hover set up when <hover> selection is on │ │ │ │ + * APIMethod: read │ │ │ │ + * Return a list of features from a GeoRSS doc │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} event object with an xy property │ │ │ │ + * doc - {Element} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ */ │ │ │ │ - selectHover: function(evt) { │ │ │ │ - var bounds = this.pixelToBounds(evt.xy); │ │ │ │ - this.request(bounds, { │ │ │ │ - single: true, │ │ │ │ - hover: true │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ + read: function(doc) { │ │ │ │ + if (typeof doc == "string") { │ │ │ │ + doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: cancelHover │ │ │ │ - * Callback from the handlers.hover set up when <hover> selection is on │ │ │ │ - */ │ │ │ │ - cancelHover: function() { │ │ │ │ - if (this.hoverResponse) { │ │ │ │ - this.protocol.abort(this.hoverResponse); │ │ │ │ - this.hoverResponse = null; │ │ │ │ + /* Try RSS items first, then Atom entries */ │ │ │ │ + var itemlist = null; │ │ │ │ + itemlist = this.getElementsByTagNameNS(doc, '*', 'item'); │ │ │ │ + if (itemlist.length == 0) { │ │ │ │ + itemlist = this.getElementsByTagNameNS(doc, '*', 'entry'); │ │ │ │ + } │ │ │ │ │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ + var numItems = itemlist.length; │ │ │ │ + var features = new Array(numItems); │ │ │ │ + for (var i = 0; i < numItems; i++) { │ │ │ │ + features[i] = this.createFeatureFromItem(itemlist[i]); │ │ │ │ } │ │ │ │ + return features; │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: request │ │ │ │ - * Sends a GetFeature request to the WFS │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} bounds for the request's BBOX filter │ │ │ │ - * options - {Object} additional options for this method. │ │ │ │ + * APIMethod: write │ │ │ │ + * Accept Feature Collection, and return a string. │ │ │ │ * │ │ │ │ - * Supported options include: │ │ │ │ - * single - {Boolean} A single feature should be returned. │ │ │ │ - * Note that this will be ignored if the protocol does not │ │ │ │ - * return the geometries of the features. │ │ │ │ - * hover - {Boolean} Do the request for the hover handler. │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string. │ │ │ │ */ │ │ │ │ - request: function(bounds, options) { │ │ │ │ - options = options || {}; │ │ │ │ - var filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: this.filterType, │ │ │ │ - value: bounds │ │ │ │ - }); │ │ │ │ - │ │ │ │ - // Set the cursor to "wait" to tell the user we're working. │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ - │ │ │ │ - var response = this.protocol.read({ │ │ │ │ - maxFeatures: options.single == true ? this.maxFeatures : undefined, │ │ │ │ - filter: filter, │ │ │ │ - callback: function(result) { │ │ │ │ - if (result.success()) { │ │ │ │ - if (result.features.length) { │ │ │ │ - if (options.single == true) { │ │ │ │ - this.selectBestFeature(result.features, │ │ │ │ - bounds.getCenterLonLat(), options); │ │ │ │ - } else { │ │ │ │ - this.select(result.features); │ │ │ │ - } │ │ │ │ - } else if (options.hover) { │ │ │ │ - this.hoverSelect(); │ │ │ │ - } else { │ │ │ │ - this.events.triggerEvent("clickout"); │ │ │ │ - if (this.clickout) { │ │ │ │ - this.unselectAll(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - // Reset the cursor. │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ - }, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - if (options.hover == true) { │ │ │ │ - this.hoverResponse = response; │ │ │ │ + write: function(features) { │ │ │ │ + var georss; │ │ │ │ + if (OpenLayers.Util.isArray(features)) { │ │ │ │ + georss = this.createElementNS(this.rssns, "rss"); │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ + georss.appendChild(this.createFeatureXML(features[i])); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + georss = this.createFeatureXML(features); │ │ │ │ } │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [georss]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: selectBestFeature │ │ │ │ - * Selects the feature from an array of features that is the best match │ │ │ │ - * for the click position. │ │ │ │ + * Method: createFeatureXML │ │ │ │ + * Accept an <OpenLayers.Feature.Vector>, and build a geometry for it. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ - * clickPosition - {<OpenLayers.LonLat>} │ │ │ │ - * options - {Object} additional options for this method │ │ │ │ - * │ │ │ │ - * Supported options include: │ │ │ │ - * hover - {Boolean} Do the selection for the hover handler. │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - selectBestFeature: function(features, clickPosition, options) { │ │ │ │ - options = options || {}; │ │ │ │ - if (features.length) { │ │ │ │ - var point = new OpenLayers.Geometry.Point(clickPosition.lon, │ │ │ │ - clickPosition.lat); │ │ │ │ - var feature, resultFeature, dist; │ │ │ │ - var minDist = Number.MAX_VALUE; │ │ │ │ - for (var i = 0; i < features.length; ++i) { │ │ │ │ - feature = features[i]; │ │ │ │ - if (feature.geometry) { │ │ │ │ - dist = point.distanceTo(feature.geometry, { │ │ │ │ - edge: false │ │ │ │ - }); │ │ │ │ - if (dist < minDist) { │ │ │ │ - minDist = dist; │ │ │ │ - resultFeature = feature; │ │ │ │ - if (minDist == 0) { │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + createFeatureXML: function(feature) { │ │ │ │ + var geometryNode = this.buildGeometryNode(feature.geometry); │ │ │ │ + var featureNode = this.createElementNS(this.rssns, "item"); │ │ │ │ + var titleNode = this.createElementNS(this.rssns, "title"); │ │ │ │ + titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : "")); │ │ │ │ + var descNode = this.createElementNS(this.rssns, "description"); │ │ │ │ + descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : "")); │ │ │ │ + featureNode.appendChild(titleNode); │ │ │ │ + featureNode.appendChild(descNode); │ │ │ │ + if (feature.attributes.link) { │ │ │ │ + var linkNode = this.createElementNS(this.rssns, "link"); │ │ │ │ + linkNode.appendChild(this.createTextNode(feature.attributes.link)); │ │ │ │ + featureNode.appendChild(linkNode); │ │ │ │ + } │ │ │ │ + for (var attr in feature.attributes) { │ │ │ │ + if (attr == "link" || attr == "title" || attr == "description") { │ │ │ │ + continue; │ │ │ │ } │ │ │ │ - │ │ │ │ - if (options.hover == true) { │ │ │ │ - this.hoverSelect(resultFeature); │ │ │ │ - } else { │ │ │ │ - this.select(resultFeature || features); │ │ │ │ + var attrText = this.createTextNode(feature.attributes[attr]); │ │ │ │ + var nodename = attr; │ │ │ │ + if (attr.search(":") != -1) { │ │ │ │ + nodename = attr.split(":")[1]; │ │ │ │ } │ │ │ │ + var attrContainer = this.createElementNS(this.featureNS, "feature:" + nodename); │ │ │ │ + attrContainer.appendChild(attrText); │ │ │ │ + featureNode.appendChild(attrContainer); │ │ │ │ } │ │ │ │ + featureNode.appendChild(geometryNode); │ │ │ │ + return featureNode; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setModifiers │ │ │ │ - * Sets the multiple and toggle modifiers according to the current event │ │ │ │ + /** │ │ │ │ + * Method: buildGeometryNode │ │ │ │ + * builds a GeoRSS node with a given geometry │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A gml node. │ │ │ │ */ │ │ │ │ - setModifiers: function(evt) { │ │ │ │ - this.modifiers = { │ │ │ │ - multiple: this.multiple || (this.multipleKey && evt[this.multipleKey]), │ │ │ │ - toggle: this.toggle || (this.toggleKey && evt[this.toggleKey]) │ │ │ │ - }; │ │ │ │ + buildGeometryNode: function(geometry) { │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry = geometry.clone(); │ │ │ │ + geometry.transform(this.internalProjection, │ │ │ │ + this.externalProjection); │ │ │ │ + } │ │ │ │ + var node; │ │ │ │ + // match Polygon │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") { │ │ │ │ + node = this.createElementNS(this.georssns, 'georss:polygon'); │ │ │ │ + │ │ │ │ + node.appendChild(this.buildCoordinatesNode(geometry.components[0])); │ │ │ │ + } │ │ │ │ + // match LineString │ │ │ │ + else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") { │ │ │ │ + node = this.createElementNS(this.georssns, 'georss:line'); │ │ │ │ + │ │ │ │ + node.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ + } │ │ │ │ + // match Point │ │ │ │ + else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ + node = this.createElementNS(this.georssns, 'georss:point'); │ │ │ │ + node.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ + } else { │ │ │ │ + throw "Couldn't parse " + geometry.CLASS_NAME; │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: select │ │ │ │ - * Add feature to the hash of selected features and trigger the │ │ │ │ - * featureselected and featuresselected events. │ │ │ │ + /** │ │ │ │ + * Method: buildCoordinatesNode │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * features - {<OpenLayers.Feature.Vector>} or an array of features │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ */ │ │ │ │ - select: function(features) { │ │ │ │ - if (!this.modifiers.multiple && !this.modifiers.toggle) { │ │ │ │ - this.unselectAll(); │ │ │ │ - } │ │ │ │ - if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ - features = [features]; │ │ │ │ - } │ │ │ │ + buildCoordinatesNode: function(geometry) { │ │ │ │ + var points = null; │ │ │ │ │ │ │ │ - var cont = this.events.triggerEvent("beforefeaturesselected", { │ │ │ │ - features: features │ │ │ │ - }); │ │ │ │ - if (cont !== false) { │ │ │ │ - var selectedFeatures = []; │ │ │ │ - var feature; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - feature = features[i]; │ │ │ │ - if (this.features[feature.fid || feature.id]) { │ │ │ │ - if (this.modifiers.toggle) { │ │ │ │ - this.unselect(this.features[feature.fid || feature.id]); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - cont = this.events.triggerEvent("beforefeatureselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (cont !== false) { │ │ │ │ - this.features[feature.fid || feature.id] = feature; │ │ │ │ - selectedFeatures.push(feature); │ │ │ │ + if (geometry.components) { │ │ │ │ + points = geometry.components; │ │ │ │ + } │ │ │ │ │ │ │ │ - this.events.triggerEvent("featureselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + var path; │ │ │ │ + if (points) { │ │ │ │ + var numPoints = points.length; │ │ │ │ + var parts = new Array(numPoints); │ │ │ │ + for (var i = 0; i < numPoints; i++) { │ │ │ │ + parts[i] = points[i].y + " " + points[i].x; │ │ │ │ } │ │ │ │ - this.events.triggerEvent("featuresselected", { │ │ │ │ - features: selectedFeatures │ │ │ │ - }); │ │ │ │ + path = parts.join(" "); │ │ │ │ + } else { │ │ │ │ + path = geometry.y + " " + geometry.x; │ │ │ │ } │ │ │ │ + return this.createTextNode(path); │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Format.GeoRSS" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/XLS.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.XLS │ │ │ │ + * Read/Write XLS (OpenLS). Create a new instance with the <OpenLayers.Format.XLS> │ │ │ │ + * constructor. Currently only implemented for Location Utility Services, more │ │ │ │ + * specifically only for Geocoding. No support for Reverse Geocoding as yet. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.XLS = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: hoverSelect │ │ │ │ - * Sets/unsets the <hoverFeature> │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} the feature to hover-select. │ │ │ │ - * If none is provided, the current <hoverFeature> will be nulled and │ │ │ │ - * the outfeature event will be triggered. │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ + * {String} Version number to assume if none found. Default is "1.1.0". │ │ │ │ */ │ │ │ │ - hoverSelect: function(feature) { │ │ │ │ - var fid = feature ? feature.fid || feature.id : null; │ │ │ │ - var hfid = this.hoverFeature ? │ │ │ │ - this.hoverFeature.fid || this.hoverFeature.id : null; │ │ │ │ + defaultVersion: "1.1.0", │ │ │ │ │ │ │ │ - if (hfid && hfid != fid) { │ │ │ │ - this.events.triggerEvent("outfeature", { │ │ │ │ - feature: this.hoverFeature │ │ │ │ - }); │ │ │ │ - this.hoverFeature = null; │ │ │ │ - } │ │ │ │ - if (fid && fid != hfid) { │ │ │ │ - this.events.triggerEvent("hoverfeature", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.hoverFeature = feature; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: stringifyOutput │ │ │ │ + * {Boolean} If true, write will return a string otherwise a DOMElement. │ │ │ │ + * Default is true. │ │ │ │ + */ │ │ │ │ + stringifyOutput: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: unselect │ │ │ │ - * Remove feature from the hash of selected features and trigger the │ │ │ │ - * featureunselected event. │ │ │ │ + * Constructor: OpenLayers.Format.XLS │ │ │ │ + * Create a new parser for XLS. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - unselect: function(feature) { │ │ │ │ - delete this.features[feature.fid || feature.id]; │ │ │ │ - this.events.triggerEvent("featureunselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: unselectAll │ │ │ │ - * Unselect all selected features. │ │ │ │ - */ │ │ │ │ - unselectAll: function() { │ │ │ │ - // we'll want an option to supress notification here │ │ │ │ - for (var fid in this.features) { │ │ │ │ - this.unselect(this.features[fid]); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the control. │ │ │ │ - * │ │ │ │ + * APIMethod: write │ │ │ │ + * Write out an XLS request. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * request - {Object} An object representing the LUS request. │ │ │ │ + * options - {Object} Optional configuration object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} An XLS document string. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - for (var i in this.handlers) { │ │ │ │ - this.handlers[i].setMap(map); │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: pixelToBounds │ │ │ │ - * Takes a pixel as argument and creates bounds after adding the │ │ │ │ - * <clickTolerance>. │ │ │ │ - * │ │ │ │ + * APIMethod: read │ │ │ │ + * Read an XLS doc and return an object representing the result. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * pixel - {<OpenLayers.Pixel>} │ │ │ │ + * data - {String | DOMElement} Data to read. │ │ │ │ + * options - {Object} Options for the reader. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An object representing the GeocodeResponse. │ │ │ │ */ │ │ │ │ - pixelToBounds: function(pixel) { │ │ │ │ - var llPx = pixel.add(-this.clickTolerance / 2, this.clickTolerance / 2); │ │ │ │ - var urPx = pixel.add(this.clickTolerance / 2, -this.clickTolerance / 2); │ │ │ │ - var ll = this.map.getLonLatFromPixel(llPx); │ │ │ │ - var ur = this.map.getLonLatFromPixel(urPx); │ │ │ │ - return new OpenLayers.Bounds(ll.lon, ll.lat, ur.lon, ur.lat); │ │ │ │ - }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.GetFeature" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.XLS" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/ZoomToMaxExtent.js │ │ │ │ + OpenLayers/Format/CQL.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control/Button.js │ │ │ │ + * @requires OpenLayers/Format/WKT.js │ │ │ │ + * @requires OpenLayers/Filter/Comparison.js │ │ │ │ + * @requires OpenLayers/Filter/Logical.js │ │ │ │ + * @requires OpenLayers/Filter/Spatial.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.ZoomToMaxExtent │ │ │ │ - * The ZoomToMaxExtent control is a button that zooms out to the maximum │ │ │ │ - * extent of the map. It is designed to be used with a │ │ │ │ - * <OpenLayers.Control.Panel>. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Format.CQL │ │ │ │ + * Read CQL strings to get <OpenLayers.Filter> objects. Write │ │ │ │ + * <OpenLayers.Filter> objects to get CQL strings. Create a new parser with │ │ │ │ + * the <OpenLayers.Format.CQL> constructor. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control.Button, { │ │ │ │ +OpenLayers.Format.CQL = (function() { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: trigger │ │ │ │ - * │ │ │ │ - * Called whenever this control is being rendered inside of a panel and a │ │ │ │ - * click occurs on this controls element. Actually zooms to the maximum │ │ │ │ - * extent of this controls map. │ │ │ │ - */ │ │ │ │ - trigger: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.zoomToMaxExtent(); │ │ │ │ + var tokens = [ │ │ │ │ + "PROPERTY", "COMPARISON", "VALUE", "LOGICAL" │ │ │ │ + ], │ │ │ │ + │ │ │ │ + patterns = { │ │ │ │ + PROPERTY: /^[_a-zA-Z]\w*/, │ │ │ │ + COMPARISON: /^(=|<>|<=|<|>=|>|LIKE)/i, │ │ │ │ + IS_NULL: /^IS NULL/i, │ │ │ │ + COMMA: /^,/, │ │ │ │ + LOGICAL: /^(AND|OR)/i, │ │ │ │ + VALUE: /^('([^']|'')*'|\d+(\.\d*)?|\.\d+)/, │ │ │ │ + LPAREN: /^\(/, │ │ │ │ + RPAREN: /^\)/, │ │ │ │ + SPATIAL: /^(BBOX|INTERSECTS|DWITHIN|WITHIN|CONTAINS)/i, │ │ │ │ + NOT: /^NOT/i, │ │ │ │ + BETWEEN: /^BETWEEN/i, │ │ │ │ + GEOMETRY: function(text) { │ │ │ │ + var type = /^(POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)/.exec(text); │ │ │ │ + if (type) { │ │ │ │ + var len = text.length; │ │ │ │ + var idx = text.indexOf("(", type[0].length); │ │ │ │ + if (idx > -1) { │ │ │ │ + var depth = 1; │ │ │ │ + while (idx < len && depth > 0) { │ │ │ │ + idx++; │ │ │ │ + switch (text.charAt(idx)) { │ │ │ │ + case '(': │ │ │ │ + depth++; │ │ │ │ + break; │ │ │ │ + case ')': │ │ │ │ + depth--; │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + // in default case, do nothing │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return [text.substr(0, idx + 1)]; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + END: /^$/ │ │ │ │ + }, │ │ │ │ + │ │ │ │ + follows = { │ │ │ │ + LPAREN: ['GEOMETRY', 'SPATIAL', 'PROPERTY', 'VALUE', 'LPAREN'], │ │ │ │ + RPAREN: ['NOT', 'LOGICAL', 'END', 'RPAREN'], │ │ │ │ + PROPERTY: ['COMPARISON', 'BETWEEN', 'COMMA', 'IS_NULL'], │ │ │ │ + BETWEEN: ['VALUE'], │ │ │ │ + IS_NULL: ['END'], │ │ │ │ + COMPARISON: ['VALUE'], │ │ │ │ + COMMA: ['GEOMETRY', 'VALUE', 'PROPERTY'], │ │ │ │ + VALUE: ['LOGICAL', 'COMMA', 'RPAREN', 'END'], │ │ │ │ + SPATIAL: ['LPAREN'], │ │ │ │ + LOGICAL: ['NOT', 'VALUE', 'SPATIAL', 'PROPERTY', 'LPAREN'], │ │ │ │ + NOT: ['PROPERTY', 'LPAREN'], │ │ │ │ + GEOMETRY: ['COMMA', 'RPAREN'] │ │ │ │ + }, │ │ │ │ + │ │ │ │ + operators = { │ │ │ │ + '=': OpenLayers.Filter.Comparison.EQUAL_TO, │ │ │ │ + '<>': OpenLayers.Filter.Comparison.NOT_EQUAL_TO, │ │ │ │ + '<': OpenLayers.Filter.Comparison.LESS_THAN, │ │ │ │ + '<=': OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO, │ │ │ │ + '>': OpenLayers.Filter.Comparison.GREATER_THAN, │ │ │ │ + '>=': OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO, │ │ │ │ + 'LIKE': OpenLayers.Filter.Comparison.LIKE, │ │ │ │ + 'BETWEEN': OpenLayers.Filter.Comparison.BETWEEN, │ │ │ │ + 'IS NULL': OpenLayers.Filter.Comparison.IS_NULL │ │ │ │ + }, │ │ │ │ + │ │ │ │ + operatorReverse = {}, │ │ │ │ + │ │ │ │ + logicals = { │ │ │ │ + 'AND': OpenLayers.Filter.Logical.AND, │ │ │ │ + 'OR': OpenLayers.Filter.Logical.OR │ │ │ │ + }, │ │ │ │ + │ │ │ │ + logicalReverse = {}, │ │ │ │ + │ │ │ │ + precedence = { │ │ │ │ + 'RPAREN': 3, │ │ │ │ + 'LOGICAL': 2, │ │ │ │ + 'COMPARISON': 1 │ │ │ │ + }; │ │ │ │ + │ │ │ │ + var i; │ │ │ │ + for (i in operators) { │ │ │ │ + if (operators.hasOwnProperty(i)) { │ │ │ │ + operatorReverse[operators[i]] = i; │ │ │ │ } │ │ │ │ - }, │ │ │ │ + } │ │ │ │ + │ │ │ │ + for (i in logicals) { │ │ │ │ + if (logicals.hasOwnProperty(i)) { │ │ │ │ + logicalReverse[logicals[i]] = i; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + function tryToken(text, pattern) { │ │ │ │ + if (pattern instanceof RegExp) { │ │ │ │ + return pattern.exec(text); │ │ │ │ + } else { │ │ │ │ + return pattern(text); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + function nextToken(text, tokens) { │ │ │ │ + var i, token, len = tokens.length; │ │ │ │ + for (i = 0; i < len; i++) { │ │ │ │ + token = tokens[i]; │ │ │ │ + var pat = patterns[token]; │ │ │ │ + var matches = tryToken(text, pat); │ │ │ │ + if (matches) { │ │ │ │ + var match = matches[0]; │ │ │ │ + var remainder = text.substr(match.length).replace(/^\s*/, ""); │ │ │ │ + return { │ │ │ │ + type: token, │ │ │ │ + text: match, │ │ │ │ + remainder: remainder │ │ │ │ + }; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + var msg = "ERROR: In parsing: [" + text + "], expected one of: "; │ │ │ │ + for (i = 0; i < len; i++) { │ │ │ │ + token = tokens[i]; │ │ │ │ + msg += "\n " + token + ": " + patterns[token]; │ │ │ │ + } │ │ │ │ + │ │ │ │ + throw new Error(msg); │ │ │ │ + } │ │ │ │ + │ │ │ │ + function tokenize(text) { │ │ │ │ + var results = []; │ │ │ │ + var token, expect = ["NOT", "GEOMETRY", "SPATIAL", "PROPERTY", "LPAREN"]; │ │ │ │ + │ │ │ │ + do { │ │ │ │ + token = nextToken(text, expect); │ │ │ │ + text = token.remainder; │ │ │ │ + expect = follows[token.type]; │ │ │ │ + if (token.type != "END" && !expect) { │ │ │ │ + throw new Error("No follows list for " + token.type); │ │ │ │ + } │ │ │ │ + results.push(token); │ │ │ │ + } while (token.type != "END"); │ │ │ │ + │ │ │ │ + return results; │ │ │ │ + } │ │ │ │ + │ │ │ │ + function buildAst(tokens) { │ │ │ │ + var operatorStack = [], │ │ │ │ + postfix = []; │ │ │ │ + │ │ │ │ + while (tokens.length) { │ │ │ │ + var tok = tokens.shift(); │ │ │ │ + switch (tok.type) { │ │ │ │ + case "PROPERTY": │ │ │ │ + case "GEOMETRY": │ │ │ │ + case "VALUE": │ │ │ │ + postfix.push(tok); │ │ │ │ + break; │ │ │ │ + case "COMPARISON": │ │ │ │ + case "BETWEEN": │ │ │ │ + case "IS_NULL": │ │ │ │ + case "LOGICAL": │ │ │ │ + var p = precedence[tok.type]; │ │ │ │ + │ │ │ │ + while (operatorStack.length > 0 && │ │ │ │ + (precedence[operatorStack[operatorStack.length - 1].type] <= p) │ │ │ │ + ) { │ │ │ │ + postfix.push(operatorStack.pop()); │ │ │ │ + } │ │ │ │ + │ │ │ │ + operatorStack.push(tok); │ │ │ │ + break; │ │ │ │ + case "SPATIAL": │ │ │ │ + case "NOT": │ │ │ │ + case "LPAREN": │ │ │ │ + operatorStack.push(tok); │ │ │ │ + break; │ │ │ │ + case "RPAREN": │ │ │ │ + while (operatorStack.length > 0 && │ │ │ │ + (operatorStack[operatorStack.length - 1].type != "LPAREN") │ │ │ │ + ) { │ │ │ │ + postfix.push(operatorStack.pop()); │ │ │ │ + } │ │ │ │ + operatorStack.pop(); // toss out the LPAREN │ │ │ │ + │ │ │ │ + if (operatorStack.length > 0 && │ │ │ │ + operatorStack[operatorStack.length - 1].type == "SPATIAL") { │ │ │ │ + postfix.push(operatorStack.pop()); │ │ │ │ + } │ │ │ │ + case "COMMA": │ │ │ │ + case "END": │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + throw new Error("Unknown token type " + tok.type); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + while (operatorStack.length > 0) { │ │ │ │ + postfix.push(operatorStack.pop()); │ │ │ │ + } │ │ │ │ + │ │ │ │ + function buildTree() { │ │ │ │ + var tok = postfix.pop(); │ │ │ │ + switch (tok.type) { │ │ │ │ + case "LOGICAL": │ │ │ │ + var rhs = buildTree(), │ │ │ │ + lhs = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Logical({ │ │ │ │ + filters: [lhs, rhs], │ │ │ │ + type: logicals[tok.text.toUpperCase()] │ │ │ │ + }); │ │ │ │ + case "NOT": │ │ │ │ + var operand = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Logical({ │ │ │ │ + filters: [operand], │ │ │ │ + type: OpenLayers.Filter.Logical.NOT │ │ │ │ + }); │ │ │ │ + case "BETWEEN": │ │ │ │ + var min, max, property; │ │ │ │ + postfix.pop(); // unneeded AND token here │ │ │ │ + max = buildTree(); │ │ │ │ + min = buildTree(); │ │ │ │ + property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Comparison({ │ │ │ │ + property: property, │ │ │ │ + lowerBoundary: min, │ │ │ │ + upperBoundary: max, │ │ │ │ + type: OpenLayers.Filter.Comparison.BETWEEN │ │ │ │ + }); │ │ │ │ + case "COMPARISON": │ │ │ │ + var value = buildTree(), │ │ │ │ + property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Comparison({ │ │ │ │ + property: property, │ │ │ │ + value: value, │ │ │ │ + type: operators[tok.text.toUpperCase()] │ │ │ │ + }); │ │ │ │ + case "IS_NULL": │ │ │ │ + var property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Comparison({ │ │ │ │ + property: property, │ │ │ │ + type: operators[tok.text.toUpperCase()] │ │ │ │ + }); │ │ │ │ + case "VALUE": │ │ │ │ + var match = tok.text.match(/^'(.*)'$/); │ │ │ │ + if (match) { │ │ │ │ + return match[1].replace(/''/g, "'"); │ │ │ │ + } else { │ │ │ │ + return Number(tok.text); │ │ │ │ + } │ │ │ │ + case "SPATIAL": │ │ │ │ + switch (tok.text.toUpperCase()) { │ │ │ │ + case "BBOX": │ │ │ │ + var maxy = buildTree(), │ │ │ │ + maxx = buildTree(), │ │ │ │ + miny = buildTree(), │ │ │ │ + minx = buildTree(), │ │ │ │ + prop = buildTree(); │ │ │ │ + │ │ │ │ + return new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ + property: prop, │ │ │ │ + value: OpenLayers.Bounds.fromArray( │ │ │ │ + [minx, miny, maxx, maxy] │ │ │ │ + ) │ │ │ │ + }); │ │ │ │ + case "INTERSECTS": │ │ │ │ + var value = buildTree(), │ │ │ │ + property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ + property: property, │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + case "WITHIN": │ │ │ │ + var value = buildTree(), │ │ │ │ + property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.WITHIN, │ │ │ │ + property: property, │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + case "CONTAINS": │ │ │ │ + var value = buildTree(), │ │ │ │ + property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.CONTAINS, │ │ │ │ + property: property, │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + case "DWITHIN": │ │ │ │ + var distance = buildTree(), │ │ │ │ + value = buildTree(), │ │ │ │ + property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.DWITHIN, │ │ │ │ + value: value, │ │ │ │ + property: property, │ │ │ │ + distance: Number(distance) │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + case "GEOMETRY": │ │ │ │ + return OpenLayers.Geometry.fromWKT(tok.text); │ │ │ │ + default: │ │ │ │ + return tok.text; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + var result = buildTree(); │ │ │ │ + if (postfix.length > 0) { │ │ │ │ + var msg = "Remaining tokens after building AST: \n"; │ │ │ │ + for (var i = postfix.length - 1; i >= 0; i--) { │ │ │ │ + msg += postfix[i].type + ": " + postfix[i].text + "\n"; │ │ │ │ + } │ │ │ │ + throw new Error(msg); │ │ │ │ + } │ │ │ │ + │ │ │ │ + return result; │ │ │ │ + } │ │ │ │ + │ │ │ │ + return OpenLayers.Class(OpenLayers.Format, { │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Generate a filter from a CQL string. │ │ │ │ + │ │ │ │ + * Parameters: │ │ │ │ + * text - {String} The CQL text. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Filter>} A filter based on the CQL text. │ │ │ │ + */ │ │ │ │ + read: function(text) { │ │ │ │ + var result = buildAst(tokenize(text)); │ │ │ │ + if (this.keepData) { │ │ │ │ + this.data = result; │ │ │ │ + } │ │ │ │ + return result; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: write │ │ │ │ + * Convert a filter into a CQL string. │ │ │ │ + │ │ │ │ + * Parameters: │ │ │ │ + * filter - {<OpenLayers.Filter>} The filter. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A CQL string based on the filter. │ │ │ │ + */ │ │ │ │ + write: function(filter) { │ │ │ │ + if (filter instanceof OpenLayers.Geometry) { │ │ │ │ + return filter.toString(); │ │ │ │ + } │ │ │ │ + switch (filter.CLASS_NAME) { │ │ │ │ + case "OpenLayers.Filter.Spatial": │ │ │ │ + switch (filter.type) { │ │ │ │ + case OpenLayers.Filter.Spatial.BBOX: │ │ │ │ + return "BBOX(" + │ │ │ │ + filter.property + "," + │ │ │ │ + filter.value.toBBOX() + │ │ │ │ + ")"; │ │ │ │ + case OpenLayers.Filter.Spatial.DWITHIN: │ │ │ │ + return "DWITHIN(" + │ │ │ │ + filter.property + ", " + │ │ │ │ + this.write(filter.value) + ", " + │ │ │ │ + filter.distance + ")"; │ │ │ │ + case OpenLayers.Filter.Spatial.WITHIN: │ │ │ │ + return "WITHIN(" + │ │ │ │ + filter.property + ", " + │ │ │ │ + this.write(filter.value) + ")"; │ │ │ │ + case OpenLayers.Filter.Spatial.INTERSECTS: │ │ │ │ + return "INTERSECTS(" + │ │ │ │ + filter.property + ", " + │ │ │ │ + this.write(filter.value) + ")"; │ │ │ │ + case OpenLayers.Filter.Spatial.CONTAINS: │ │ │ │ + return "CONTAINS(" + │ │ │ │ + filter.property + ", " + │ │ │ │ + this.write(filter.value) + ")"; │ │ │ │ + default: │ │ │ │ + throw new Error("Unknown spatial filter type: " + filter.type); │ │ │ │ + } │ │ │ │ + case "OpenLayers.Filter.Logical": │ │ │ │ + if (filter.type == OpenLayers.Filter.Logical.NOT) { │ │ │ │ + // TODO: deal with precedence of logical operators to │ │ │ │ + // avoid extra parentheses (not urgent) │ │ │ │ + return "NOT (" + this.write(filter.filters[0]) + ")"; │ │ │ │ + } else { │ │ │ │ + var res = "("; │ │ │ │ + var first = true; │ │ │ │ + for (var i = 0; i < filter.filters.length; i++) { │ │ │ │ + if (first) { │ │ │ │ + first = false; │ │ │ │ + } else { │ │ │ │ + res += ") " + logicalReverse[filter.type] + " ("; │ │ │ │ + } │ │ │ │ + res += this.write(filter.filters[i]); │ │ │ │ + } │ │ │ │ + return res + ")"; │ │ │ │ + } │ │ │ │ + case "OpenLayers.Filter.Comparison": │ │ │ │ + if (filter.type == OpenLayers.Filter.Comparison.BETWEEN) { │ │ │ │ + return filter.property + " BETWEEN " + │ │ │ │ + this.write(filter.lowerBoundary) + " AND " + │ │ │ │ + this.write(filter.upperBoundary); │ │ │ │ + } else { │ │ │ │ + return (filter.value !== null) ? filter.property + │ │ │ │ + " " + operatorReverse[filter.type] + " " + │ │ │ │ + this.write(filter.value) : filter.property + │ │ │ │ + " " + operatorReverse[filter.type]; │ │ │ │ + } │ │ │ │ + case undefined: │ │ │ │ + if (typeof filter === "string") { │ │ │ │ + return "'" + filter.replace(/'/g, "''") + "'"; │ │ │ │ + } else if (typeof filter === "number") { │ │ │ │ + return String(filter); │ │ │ │ + } │ │ │ │ + default: │ │ │ │ + throw new Error("Can't encode: " + filter.CLASS_NAME + " " + filter); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.CQL" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +})(); │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ZoomToMaxExtent" │ │ │ │ -}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/CacheRead.js │ │ │ │ + OpenLayers/Format/OWSContext.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Format/Context.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.CacheRead │ │ │ │ - * A control for using image tiles cached with <OpenLayers.Control.CacheWrite> │ │ │ │ - * from the browser's local storage. │ │ │ │ + * Class: OpenLayers.Format.OWSContext │ │ │ │ + * Read and write OWS Context documents. OWS Context documents are a │ │ │ │ + * preliminary OGC (Open Geospatial Consortium) standard for storing the │ │ │ │ + * state of a web mapping application. In a way it is the successor to │ │ │ │ + * Web Map Context (WMC), since it is more generic and more types of layers │ │ │ │ + * can be stored. Also, nesting of layers is supported since version 0.3.1. │ │ │ │ + * For more information see: http://www.ogcnetwork.net/context │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format.Context> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.CacheRead = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: fetchEvent │ │ │ │ - * {String} The layer event to listen to for replacing remote resource tile │ │ │ │ - * URLs with cached data URIs. Supported values are "tileerror" (try │ │ │ │ - * remote first, fall back to cached) and "tileloadstart" (try cache │ │ │ │ - * first, fall back to remote). Default is "tileloadstart". │ │ │ │ - * │ │ │ │ - * Note that "tileerror" will not work for CORS enabled images (see │ │ │ │ - * https://developer.mozilla.org/en/CORS_Enabled_Image), i.e. layers │ │ │ │ - * configured with a <OpenLayers.Tile.Image.crossOriginKeyword> in │ │ │ │ - * <OpenLayers.Layer.Grid.tileOptions>. │ │ │ │ - */ │ │ │ │ - fetchEvent: "tileloadstart", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: layers │ │ │ │ - * {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, only these │ │ │ │ - * layers will receive tiles from the cache. │ │ │ │ - */ │ │ │ │ - layers: null, │ │ │ │ +OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * true. │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ + * {String} Version number to assume if none found. Default is "0.3.1". │ │ │ │ */ │ │ │ │ - autoActivate: true, │ │ │ │ + defaultVersion: "0.3.1", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.CacheRead │ │ │ │ + * Constructor: OpenLayers.Format.OWSContext │ │ │ │ + * Create a new parser for OWS Context documents. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Object with API properties for this control │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the control. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - var i, layers = this.layers || map.layers; │ │ │ │ - for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ - this.addLayer({ │ │ │ │ - layer: layers[i] │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - if (!this.layers) { │ │ │ │ - map.events.on({ │ │ │ │ - addlayer: this.addLayer, │ │ │ │ - removeLayer: this.removeLayer, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: addLayer │ │ │ │ - * Adds a layer to the control. Once added, tiles requested for this layer │ │ │ │ - * will be cached. │ │ │ │ + * Method: getVersion │ │ │ │ + * Returns the version to use. Subclasses can override this function │ │ │ │ + * if a different version detection is needed. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} Object with a layer property referencing an │ │ │ │ - * <OpenLayers.Layer> instance │ │ │ │ - */ │ │ │ │ - addLayer: function(evt) { │ │ │ │ - evt.layer.events.register(this.fetchEvent, this, this.fetch); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: removeLayer │ │ │ │ - * Removes a layer from the control. Once removed, tiles requested for this │ │ │ │ - * layer will no longer be cached. │ │ │ │ + * root - {DOMElement} │ │ │ │ + * options - {Object} Optional configuration object. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} Object with a layer property referencing an │ │ │ │ - * <OpenLayers.Layer> instance │ │ │ │ + * Returns: │ │ │ │ + * {String} The version to use. │ │ │ │ */ │ │ │ │ - removeLayer: function(evt) { │ │ │ │ - evt.layer.events.unregister(this.fetchEvent, this, this.fetch); │ │ │ │ + getVersion: function(root, options) { │ │ │ │ + var version = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply( │ │ │ │ + this, arguments); │ │ │ │ + // 0.3.1 is backwards compatible with 0.3.0 │ │ │ │ + if (version === "0.3.0") { │ │ │ │ + version = this.defaultVersion; │ │ │ │ + } │ │ │ │ + return version; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: fetch │ │ │ │ - * Listener to the <fetchEvent> event. Replaces a tile's url with a data │ │ │ │ - * URI from the cache. │ │ │ │ + * Method: toContext │ │ │ │ + * Create a context object free from layer given a map or a │ │ │ │ + * context object. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} Event object with a tile property. │ │ │ │ + * obj - {<OpenLayers.Map> | Object} The map or context. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} A context object. │ │ │ │ */ │ │ │ │ - fetch: function(evt) { │ │ │ │ - if (this.active && window.localStorage && │ │ │ │ - evt.tile instanceof OpenLayers.Tile.Image) { │ │ │ │ - var tile = evt.tile, │ │ │ │ - url = tile.url; │ │ │ │ - // deal with modified tile urls when both CacheWrite and CacheRead │ │ │ │ - // are active │ │ │ │ - if (!tile.layer.crossOriginKeyword && OpenLayers.ProxyHost && │ │ │ │ - url.indexOf(OpenLayers.ProxyHost) === 0) { │ │ │ │ - url = OpenLayers.Control.CacheWrite.urlMap[url]; │ │ │ │ - } │ │ │ │ - var dataURI = window.localStorage.getItem("olCache_" + url); │ │ │ │ - if (dataURI) { │ │ │ │ - tile.url = dataURI; │ │ │ │ - if (evt.type === "tileerror") { │ │ │ │ - tile.setImgSrc(dataURI); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + toContext: function(obj) { │ │ │ │ + var context = {}; │ │ │ │ + if (obj.CLASS_NAME == "OpenLayers.Map") { │ │ │ │ + context.bounds = obj.getExtent(); │ │ │ │ + context.maxExtent = obj.maxExtent; │ │ │ │ + context.projection = obj.projection; │ │ │ │ + context.size = obj.getSize(); │ │ │ │ + context.layers = obj.layers; │ │ │ │ } │ │ │ │ + return context; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * The destroy method is used to perform any clean up before the control │ │ │ │ - * is dereferenced. Typically this is where event listeners are removed │ │ │ │ - * to prevent memory leaks. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.layers || this.map) { │ │ │ │ - var i, layers = this.layers || this.map.layers; │ │ │ │ - for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ - this.removeLayer({ │ │ │ │ - layer: layers[i] │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.un({ │ │ │ │ - addlayer: this.addLayer, │ │ │ │ - removeLayer: this.removeLayer, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.OWSContext" │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.CacheRead" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/LayerSwitcher.js │ │ │ │ + OpenLayers/Format/GPX.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Lang.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ - * @requires OpenLayers/Events/buttonclick.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Geometry/Point.js │ │ │ │ + * @requires OpenLayers/Geometry/LineString.js │ │ │ │ + * @requires OpenLayers/Projection.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.LayerSwitcher │ │ │ │ - * The LayerSwitcher control displays a table of contents for the map. This │ │ │ │ - * allows the user interface to switch between BaseLasyers and to show or hide │ │ │ │ - * Overlays. By default the switcher is shown minimized on the right edge of │ │ │ │ - * the map, the user may expand it by clicking on the handle. │ │ │ │ - * │ │ │ │ - * To create the LayerSwitcher outside of the map, pass the Id of a html div │ │ │ │ - * as the first argument to the constructor. │ │ │ │ + * Class: OpenLayers.Format.GPX │ │ │ │ + * Read/write GPX parser. Create a new instance with the │ │ │ │ + * <OpenLayers.Format.GPX> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: layerStates │ │ │ │ - * {Array(Object)} Basically a copy of the "state" of the map's layers │ │ │ │ - * the last time the control was drawn. We have this in order to avoid │ │ │ │ - * unnecessarily redrawing the control. │ │ │ │ - */ │ │ │ │ - layerStates: null, │ │ │ │ - │ │ │ │ - // DOM Elements │ │ │ │ +OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: layersDiv │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - layersDiv: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: baseLayersDiv │ │ │ │ - * {DOMElement} │ │ │ │ + /** │ │ │ │ + * APIProperty: defaultDesc │ │ │ │ + * {String} Default description for the waypoints/tracks in the case │ │ │ │ + * where the feature has no "description" attribute. │ │ │ │ + * Default is "No description available". │ │ │ │ */ │ │ │ │ - baseLayersDiv: null, │ │ │ │ + defaultDesc: "No description available", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: baseLayers │ │ │ │ - * {Array(Object)} │ │ │ │ + * APIProperty: extractWaypoints │ │ │ │ + * {Boolean} Extract waypoints from GPX. (default: true) │ │ │ │ */ │ │ │ │ - baseLayers: null, │ │ │ │ - │ │ │ │ + extractWaypoints: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: dataLbl │ │ │ │ - * {DOMElement} │ │ │ │ + * APIProperty: extractTracks │ │ │ │ + * {Boolean} Extract tracks from GPX. (default: true) │ │ │ │ */ │ │ │ │ - dataLbl: null, │ │ │ │ + extractTracks: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: dataLayersDiv │ │ │ │ - * {DOMElement} │ │ │ │ + * APIProperty: extractRoutes │ │ │ │ + * {Boolean} Extract routes from GPX. (default: true) │ │ │ │ */ │ │ │ │ - dataLayersDiv: null, │ │ │ │ + extractRoutes: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: dataLayers │ │ │ │ - * {Array(Object)} │ │ │ │ + * APIProperty: extractAttributes │ │ │ │ + * {Boolean} Extract feature attributes from GPX. (default: true) │ │ │ │ + * NOTE: Attributes as part of extensions to the GPX standard may not │ │ │ │ + * be extracted. │ │ │ │ */ │ │ │ │ - dataLayers: null, │ │ │ │ - │ │ │ │ + extractAttributes: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: minimizeDiv │ │ │ │ - * {DOMElement} │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ */ │ │ │ │ - minimizeDiv: null, │ │ │ │ + namespaces: { │ │ │ │ + gpx: "http://www.topografix.com/GPX/1/1", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: maximizeDiv │ │ │ │ - * {DOMElement} │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} Schema location. Defaults to │ │ │ │ + * "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" │ │ │ │ */ │ │ │ │ - maximizeDiv: null, │ │ │ │ + schemaLocation: "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: ascending │ │ │ │ - * {Boolean} │ │ │ │ + * APIProperty: creator │ │ │ │ + * {String} The creator attribute to be added to the written GPX files. │ │ │ │ + * Defaults to "OpenLayers" │ │ │ │ */ │ │ │ │ - ascending: true, │ │ │ │ + creator: "OpenLayers", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.LayerSwitcher │ │ │ │ + * Constructor: OpenLayers.Format.GPX │ │ │ │ + * Create a new parser for GPX. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ - this.layerStates = []; │ │ │ │ + // GPX coordinates are always in longlat WGS84 │ │ │ │ + this.externalProjection = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ + * APIMethod: read │ │ │ │ + * Return a list of features from a GPX doc │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * doc - {Element} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * Array({<OpenLayers.Feature.Vector>}) │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ + read: function(doc) { │ │ │ │ + if (typeof doc == "string") { │ │ │ │ + doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); │ │ │ │ + } │ │ │ │ + var features = []; │ │ │ │ │ │ │ │ - //clear out layers info and unregister their events │ │ │ │ - this.clearLayersArray("base"); │ │ │ │ - this.clearLayersArray("data"); │ │ │ │ + if (this.extractTracks) { │ │ │ │ + var tracks = doc.getElementsByTagName("trk"); │ │ │ │ + for (var i = 0, len = tracks.length; i < len; i++) { │ │ │ │ + // Attributes are only in trk nodes, not trkseg nodes │ │ │ │ + var attrs = {}; │ │ │ │ + if (this.extractAttributes) { │ │ │ │ + attrs = this.parseAttributes(tracks[i]); │ │ │ │ + } │ │ │ │ │ │ │ │ - this.map.events.un({ │ │ │ │ - buttonclick: this.onButtonClick, │ │ │ │ - addlayer: this.redraw, │ │ │ │ - changelayer: this.redraw, │ │ │ │ - removelayer: this.redraw, │ │ │ │ - changebaselayer: this.redraw, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ + var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, "trkseg"); │ │ │ │ + for (var j = 0, seglen = segs.length; j < seglen; j++) { │ │ │ │ + // We don't yet support extraction of trkpt attributes │ │ │ │ + // All trksegs of a trk get that trk's attributes │ │ │ │ + var track = this.extractSegment(segs[j], "trkpt"); │ │ │ │ + features.push(new OpenLayers.Feature.Vector(track, attrs)); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + if (this.extractRoutes) { │ │ │ │ + var routes = doc.getElementsByTagName("rte"); │ │ │ │ + for (var k = 0, klen = routes.length; k < klen; k++) { │ │ │ │ + var attrs = {}; │ │ │ │ + if (this.extractAttributes) { │ │ │ │ + attrs = this.parseAttributes(routes[k]); │ │ │ │ + } │ │ │ │ + var route = this.extractSegment(routes[k], "rtept"); │ │ │ │ + features.push(new OpenLayers.Feature.Vector(route, attrs)); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * │ │ │ │ - * Properties: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ + if (this.extractWaypoints) { │ │ │ │ + var waypoints = doc.getElementsByTagName("wpt"); │ │ │ │ + for (var l = 0, len = waypoints.length; l < len; l++) { │ │ │ │ + var attrs = {}; │ │ │ │ + if (this.extractAttributes) { │ │ │ │ + attrs = this.parseAttributes(waypoints[l]); │ │ │ │ + } │ │ │ │ + var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute("lon"), waypoints[l].getAttribute("lat")); │ │ │ │ + features.push(new OpenLayers.Feature.Vector(wpt, attrs)); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - this.map.events.on({ │ │ │ │ - addlayer: this.redraw, │ │ │ │ - changelayer: this.redraw, │ │ │ │ - removelayer: this.redraw, │ │ │ │ - changebaselayer: this.redraw, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - if (this.outsideViewport) { │ │ │ │ - this.events.attachToElement(this.div); │ │ │ │ - this.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ - } else { │ │ │ │ - this.map.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + for (var g = 0, featLength = features.length; g < featLength; g++) { │ │ │ │ + features[g].geometry.transform(this.externalProjection, │ │ │ │ + this.internalProjection); │ │ │ │ + } │ │ │ │ } │ │ │ │ + │ │ │ │ + return features; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ + * Method: extractSegment │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * segment - {DOMElement} a trkseg or rte node to parse │ │ │ │ + * segmentType - {String} nodeName of waypoints that form the line │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} A reference to the DIV DOMElement containing the │ │ │ │ - * switcher tabs. │ │ │ │ + * {<OpenLayers.Geometry.LineString>} A linestring geometry │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this); │ │ │ │ - │ │ │ │ - // create layout divs │ │ │ │ - this.loadContents(); │ │ │ │ - │ │ │ │ - // set mode to minimize │ │ │ │ - if (!this.outsideViewport) { │ │ │ │ - this.minimizeControl(); │ │ │ │ + extractSegment: function(segment, segmentType) { │ │ │ │ + var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType); │ │ │ │ + var point_features = []; │ │ │ │ + for (var i = 0, len = points.length; i < len; i++) { │ │ │ │ + point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute("lon"), points[i].getAttribute("lat"))); │ │ │ │ } │ │ │ │ - │ │ │ │ - // populate div with current info │ │ │ │ - this.redraw(); │ │ │ │ - │ │ │ │ - return this.div; │ │ │ │ + return new OpenLayers.Geometry.LineString(point_features); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onButtonClick │ │ │ │ + * Method: parseAttributes │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * node - {<DOMElement>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An attributes object. │ │ │ │ */ │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - var button = evt.buttonElement; │ │ │ │ - if (button === this.minimizeDiv) { │ │ │ │ - this.minimizeControl(); │ │ │ │ - } else if (button === this.maximizeDiv) { │ │ │ │ - this.maximizeControl(); │ │ │ │ - } else if (button._layerSwitcher === this.id) { │ │ │ │ - if (button["for"]) { │ │ │ │ - button = document.getElementById(button["for"]); │ │ │ │ - } │ │ │ │ - if (!button.disabled) { │ │ │ │ - if (button.type == "radio") { │ │ │ │ - button.checked = true; │ │ │ │ - this.map.setBaseLayer(this.map.getLayer(button._layer)); │ │ │ │ - } else { │ │ │ │ - button.checked = !button.checked; │ │ │ │ - this.updateMap(); │ │ │ │ + parseAttributes: function(node) { │ │ │ │ + // node is either a wpt, trk or rte │ │ │ │ + // attributes are children of the form <attr>value</attr> │ │ │ │ + var attributes = {}; │ │ │ │ + var attrNode = node.firstChild, │ │ │ │ + value, name; │ │ │ │ + while (attrNode) { │ │ │ │ + if (attrNode.nodeType == 1 && attrNode.firstChild) { │ │ │ │ + value = attrNode.firstChild; │ │ │ │ + if (value.nodeType == 3 || value.nodeType == 4) { │ │ │ │ + name = (attrNode.prefix) ? │ │ │ │ + attrNode.nodeName.split(":")[1] : │ │ │ │ + attrNode.nodeName; │ │ │ │ + if (name != "trkseg" && name != "rtept") { │ │ │ │ + attributes[name] = value.nodeValue; │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ + attrNode = attrNode.nextSibling; │ │ │ │ } │ │ │ │ + return attributes; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clearLayersArray │ │ │ │ - * User specifies either "base" or "data". we then clear all the │ │ │ │ - * corresponding listeners, the div, and reinitialize a new array. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layersType - {String} │ │ │ │ + * APIMethod: write │ │ │ │ + * Accepts Feature Collection, and returns a string. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string. │ │ │ │ + * metadata - {Object} A key/value pairs object to build a metadata node to │ │ │ │ + * add to the gpx. Supported keys are 'name', 'desc', 'author'. │ │ │ │ */ │ │ │ │ - clearLayersArray: function(layersType) { │ │ │ │ - this[layersType + "LayersDiv"].innerHTML = ""; │ │ │ │ - this[layersType + "Layers"] = []; │ │ │ │ - }, │ │ │ │ + write: function(features, metadata) { │ │ │ │ + features = OpenLayers.Util.isArray(features) ? │ │ │ │ + features : [features]; │ │ │ │ + var gpx = this.createElementNS(this.namespaces.gpx, "gpx"); │ │ │ │ + gpx.setAttribute("version", "1.1"); │ │ │ │ + gpx.setAttribute("creator", this.creator); │ │ │ │ + this.setAttributes(gpx, { │ │ │ │ + "xsi:schemaLocation": this.schemaLocation │ │ │ │ + }); │ │ │ │ │ │ │ │ + if (metadata && typeof metadata == 'object') { │ │ │ │ + gpx.appendChild(this.buildMetadataNode(metadata)); │ │ │ │ + } │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ + gpx.appendChild(this.buildFeatureNode(features[i])); │ │ │ │ + } │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [gpx]); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: checkRedraw │ │ │ │ - * Checks if the layer state has changed since the last redraw() call. │ │ │ │ + * Method: buildMetadataNode │ │ │ │ + * Creates a "metadata" node. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The layer state changed since the last redraw() call. │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - checkRedraw: function() { │ │ │ │ - if (!this.layerStates.length || │ │ │ │ - (this.map.layers.length != this.layerStates.length)) { │ │ │ │ - return true; │ │ │ │ - } │ │ │ │ - │ │ │ │ - for (var i = 0, len = this.layerStates.length; i < len; i++) { │ │ │ │ - var layerState = this.layerStates[i]; │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - if ((layerState.name != layer.name) || │ │ │ │ - (layerState.inRange != layer.inRange) || │ │ │ │ - (layerState.id != layer.id) || │ │ │ │ - (layerState.visibility != layer.visibility)) { │ │ │ │ - return true; │ │ │ │ + buildMetadataNode: function(metadata) { │ │ │ │ + var types = ['name', 'desc', 'author'], │ │ │ │ + node = this.createElementNS(this.namespaces.gpx, 'metadata'); │ │ │ │ + for (var i = 0; i < types.length; i++) { │ │ │ │ + var type = types[i]; │ │ │ │ + if (metadata[type]) { │ │ │ │ + var n = this.createElementNS(this.namespaces.gpx, type); │ │ │ │ + n.appendChild(this.createTextNode(metadata[type])); │ │ │ │ + node.appendChild(n); │ │ │ │ } │ │ │ │ } │ │ │ │ - │ │ │ │ - return false; │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: redraw │ │ │ │ - * Goes through and takes the current state of the Map and rebuilds the │ │ │ │ - * control to display that state. Groups base layers into a │ │ │ │ - * radio-button group and lists each data layer with a checkbox. │ │ │ │ + * Method: buildFeatureNode │ │ │ │ + * Accepts an <OpenLayers.Feature.Vector>, and builds a node for it. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} A reference to the DIV DOMElement containing the control │ │ │ │ + * {DOMElement} - The created node, either a 'wpt' or a 'trk'. │ │ │ │ */ │ │ │ │ - redraw: function() { │ │ │ │ - //if the state hasn't changed since last redraw, no need │ │ │ │ - // to do anything. Just return the existing div. │ │ │ │ - if (!this.checkRedraw()) { │ │ │ │ - return this.div; │ │ │ │ - } │ │ │ │ - │ │ │ │ - //clear out previous layers │ │ │ │ - this.clearLayersArray("base"); │ │ │ │ - this.clearLayersArray("data"); │ │ │ │ - │ │ │ │ - var containsOverlays = false; │ │ │ │ - var containsBaseLayers = false; │ │ │ │ - │ │ │ │ - // Save state -- for checking layer if the map state changed. │ │ │ │ - // We save this before redrawing, because in the process of redrawing │ │ │ │ - // we will trigger more visibility changes, and we want to not redraw │ │ │ │ - // and enter an infinite loop. │ │ │ │ - var len = this.map.layers.length; │ │ │ │ - this.layerStates = new Array(len); │ │ │ │ - for (var i = 0; i < len; i++) { │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - this.layerStates[i] = { │ │ │ │ - 'name': layer.name, │ │ │ │ - 'visibility': layer.visibility, │ │ │ │ - 'inRange': layer.inRange, │ │ │ │ - 'id': layer.id │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var layers = this.map.layers.slice(); │ │ │ │ - if (!this.ascending) { │ │ │ │ - layers.reverse(); │ │ │ │ + buildFeatureNode: function(feature) { │ │ │ │ + var geometry = feature.geometry; │ │ │ │ + geometry = geometry.clone(); │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry.transform(this.internalProjection, │ │ │ │ + this.externalProjection); │ │ │ │ } │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - var layer = layers[i]; │ │ │ │ - var baseLayer = layer.isBaseLayer; │ │ │ │ - │ │ │ │ - if (layer.displayInLayerSwitcher) { │ │ │ │ - │ │ │ │ - if (baseLayer) { │ │ │ │ - containsBaseLayers = true; │ │ │ │ - } else { │ │ │ │ - containsOverlays = true; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // only check a baselayer if it is *the* baselayer, check data │ │ │ │ - // layers if they are visible │ │ │ │ - var checked = (baseLayer) ? (layer == this.map.baseLayer) : │ │ │ │ - layer.getVisibility(); │ │ │ │ - │ │ │ │ - // create input element │ │ │ │ - var inputElem = document.createElement("input"), │ │ │ │ - // The input shall have an id attribute so we can use │ │ │ │ - // labels to interact with them. │ │ │ │ - inputId = OpenLayers.Util.createUniqueID( │ │ │ │ - this.id + "_input_" │ │ │ │ - ); │ │ │ │ - │ │ │ │ - inputElem.id = inputId; │ │ │ │ - inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name; │ │ │ │ - inputElem.type = (baseLayer) ? "radio" : "checkbox"; │ │ │ │ - inputElem.value = layer.name; │ │ │ │ - inputElem.checked = checked; │ │ │ │ - inputElem.defaultChecked = checked; │ │ │ │ - inputElem.className = "olButton"; │ │ │ │ - inputElem._layer = layer.id; │ │ │ │ - inputElem._layerSwitcher = this.id; │ │ │ │ - │ │ │ │ - if (!baseLayer && !layer.inRange) { │ │ │ │ - inputElem.disabled = true; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // create span │ │ │ │ - var labelSpan = document.createElement("label"); │ │ │ │ - // this isn't the DOM attribute 'for', but an arbitrary name we │ │ │ │ - // use to find the appropriate input element in <onButtonClick> │ │ │ │ - labelSpan["for"] = inputElem.id; │ │ │ │ - OpenLayers.Element.addClass(labelSpan, "labelSpan olButton"); │ │ │ │ - labelSpan._layer = layer.id; │ │ │ │ - labelSpan._layerSwitcher = this.id; │ │ │ │ - if (!baseLayer && !layer.inRange) { │ │ │ │ - labelSpan.style.color = "gray"; │ │ │ │ - } │ │ │ │ - labelSpan.innerHTML = layer.name; │ │ │ │ - labelSpan.style.verticalAlign = (baseLayer) ? "bottom" : │ │ │ │ - "baseline"; │ │ │ │ - // create line break │ │ │ │ - var br = document.createElement("br"); │ │ │ │ - │ │ │ │ - │ │ │ │ - var groupArray = (baseLayer) ? this.baseLayers : │ │ │ │ - this.dataLayers; │ │ │ │ - groupArray.push({ │ │ │ │ - 'layer': layer, │ │ │ │ - 'inputElem': inputElem, │ │ │ │ - 'labelSpan': labelSpan │ │ │ │ - }); │ │ │ │ - │ │ │ │ - │ │ │ │ - var groupDiv = (baseLayer) ? this.baseLayersDiv : │ │ │ │ - this.dataLayersDiv; │ │ │ │ - groupDiv.appendChild(inputElem); │ │ │ │ - groupDiv.appendChild(labelSpan); │ │ │ │ - groupDiv.appendChild(br); │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ + var wpt = this.buildWptNode(geometry); │ │ │ │ + this.appendAttributesNode(wpt, feature); │ │ │ │ + return wpt; │ │ │ │ + } else { │ │ │ │ + var trkNode = this.createElementNS(this.namespaces.gpx, "trk"); │ │ │ │ + this.appendAttributesNode(trkNode, feature); │ │ │ │ + var trkSegNodes = this.buildTrkSegNode(geometry); │ │ │ │ + trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ? │ │ │ │ + trkSegNodes : [trkSegNodes]; │ │ │ │ + for (var i = 0, len = trkSegNodes.length; i < len; i++) { │ │ │ │ + trkNode.appendChild(trkSegNodes[i]); │ │ │ │ } │ │ │ │ + return trkNode; │ │ │ │ } │ │ │ │ - │ │ │ │ - // if no overlays, dont display the overlay label │ │ │ │ - this.dataLbl.style.display = (containsOverlays) ? "" : "none"; │ │ │ │ - │ │ │ │ - // if no baselayers, dont display the baselayer label │ │ │ │ - this.baseLbl.style.display = (containsBaseLayers) ? "" : "none"; │ │ │ │ - │ │ │ │ - return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: updateMap │ │ │ │ - * Cycles through the loaded data and base layer input arrays and makes │ │ │ │ - * the necessary calls to the Map object such that that the map's │ │ │ │ - * visual state corresponds to what the user has selected in │ │ │ │ - * the control. │ │ │ │ + * Method: buildTrkSegNode │ │ │ │ + * Builds trkseg node(s) given a geometry │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * trknode │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ */ │ │ │ │ - updateMap: function() { │ │ │ │ - │ │ │ │ - // set the newly selected base layer │ │ │ │ - for (var i = 0, len = this.baseLayers.length; i < len; i++) { │ │ │ │ - var layerEntry = this.baseLayers[i]; │ │ │ │ - if (layerEntry.inputElem.checked) { │ │ │ │ - this.map.setBaseLayer(layerEntry.layer, false); │ │ │ │ + buildTrkSegNode: function(geometry) { │ │ │ │ + var node, │ │ │ │ + i, │ │ │ │ + len, │ │ │ │ + point, │ │ │ │ + nodes; │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" || │ │ │ │ + geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { │ │ │ │ + node = this.createElementNS(this.namespaces.gpx, "trkseg"); │ │ │ │ + for (i = 0, len = geometry.components.length; i < len; i++) { │ │ │ │ + point = geometry.components[i]; │ │ │ │ + node.appendChild(this.buildTrkPtNode(point)); │ │ │ │ } │ │ │ │ + return node; │ │ │ │ + } else { │ │ │ │ + nodes = []; │ │ │ │ + for (i = 0, len = geometry.components.length; i < len; i++) { │ │ │ │ + nodes.push(this.buildTrkSegNode(geometry.components[i])); │ │ │ │ + } │ │ │ │ + return nodes; │ │ │ │ } │ │ │ │ - │ │ │ │ - // set the correct visibilities for the overlays │ │ │ │ - for (var i = 0, len = this.dataLayers.length; i < len; i++) { │ │ │ │ - var layerEntry = this.dataLayers[i]; │ │ │ │ - layerEntry.layer.setVisibility(layerEntry.inputElem.checked); │ │ │ │ - } │ │ │ │ - │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: maximizeControl │ │ │ │ - * Set up the labels and divs for the control │ │ │ │ + * Method: buildTrkPtNode │ │ │ │ + * Builds a trkpt node given a point │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * e - {Event} │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A trkpt node │ │ │ │ */ │ │ │ │ - maximizeControl: function(e) { │ │ │ │ - │ │ │ │ - // set the div's width and height to empty values, so │ │ │ │ - // the div dimensions can be controlled by CSS │ │ │ │ - this.div.style.width = ""; │ │ │ │ - this.div.style.height = ""; │ │ │ │ - │ │ │ │ - this.showControls(false); │ │ │ │ + buildTrkPtNode: function(point) { │ │ │ │ + var node = this.createElementNS(this.namespaces.gpx, "trkpt"); │ │ │ │ + node.setAttribute("lon", point.x); │ │ │ │ + node.setAttribute("lat", point.y); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (e != null) { │ │ │ │ - OpenLayers.Event.stop(e); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: buildWptNode │ │ │ │ + * Builds a wpt node given a point │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry.Point>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A wpt node │ │ │ │ + */ │ │ │ │ + buildWptNode: function(geometry) { │ │ │ │ + var node = this.createElementNS(this.namespaces.gpx, "wpt"); │ │ │ │ + node.setAttribute("lon", geometry.x); │ │ │ │ + node.setAttribute("lat", geometry.y); │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: minimizeControl │ │ │ │ - * Hide all the contents of the control, shrink the size, │ │ │ │ - * add the maximize icon │ │ │ │ + * Method: appendAttributesNode │ │ │ │ + * Adds some attributes node. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * e - {Event} │ │ │ │ + * node - {DOMElement} the node to append the attribute nodes to. │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - minimizeControl: function(e) { │ │ │ │ + appendAttributesNode: function(node, feature) { │ │ │ │ + var name = this.createElementNS(this.namespaces.gpx, 'name'); │ │ │ │ + name.appendChild(this.createTextNode( │ │ │ │ + feature.attributes.name || feature.id)); │ │ │ │ + node.appendChild(name); │ │ │ │ + var desc = this.createElementNS(this.namespaces.gpx, 'desc'); │ │ │ │ + desc.appendChild(this.createTextNode( │ │ │ │ + feature.attributes.description || this.defaultDesc)); │ │ │ │ + node.appendChild(desc); │ │ │ │ + // TBD - deal with remaining (non name/description) attributes. │ │ │ │ + }, │ │ │ │ │ │ │ │ - // to minimize the control we set its div's width │ │ │ │ - // and height to 0px, we cannot just set "display" │ │ │ │ - // to "none" because it would hide the maximize │ │ │ │ - // div │ │ │ │ - this.div.style.width = "0px"; │ │ │ │ - this.div.style.height = "0px"; │ │ │ │ + CLASS_NAME: "OpenLayers.Format.GPX" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WMTSCapabilities.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - this.showControls(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. */ │ │ │ │ │ │ │ │ - if (e != null) { │ │ │ │ - OpenLayers.Event.stop(e); │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WMTSCapabilities │ │ │ │ + * Read WMTS Capabilities. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ + * {String} Version number to assume if none found. Default is "1.0.0". │ │ │ │ + */ │ │ │ │ + defaultVersion: "1.0.0", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: yx │ │ │ │ + * {Object} Members in the yx object are used to determine if a CRS URN │ │ │ │ + * corresponds to a CRS with y,x axis order. Member names are CRS URNs │ │ │ │ + * and values are boolean. By default, the following CRS URN are │ │ │ │ + * assumed to correspond to a CRS with y,x axis order: │ │ │ │ + * │ │ │ │ + * * urn:ogc:def:crs:EPSG::4326 │ │ │ │ + */ │ │ │ │ + yx: { │ │ │ │ + "urn:ogc:def:crs:EPSG::4326": true │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: showControls │ │ │ │ - * Hide/Show all LayerSwitcher controls depending on whether we are │ │ │ │ - * minimized or not │ │ │ │ + * Constructor: OpenLayers.Format.WMTSCapabilities │ │ │ │ + * Create a new parser for WMTS capabilities. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * minimize - {Boolean} │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - showControls: function(minimize) { │ │ │ │ - │ │ │ │ - this.maximizeDiv.style.display = minimize ? "" : "none"; │ │ │ │ - this.minimizeDiv.style.display = minimize ? "none" : ""; │ │ │ │ │ │ │ │ - this.layersDiv.style.display = minimize ? "none" : ""; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read capabilities data from a string, and return information about │ │ │ │ + * the service (offering and observedProperty mostly). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} Info about the WMTS Capabilities │ │ │ │ + */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: loadContents │ │ │ │ - * Set up the labels and divs for the control │ │ │ │ + * APIMethod: createLayer │ │ │ │ + * Create a WMTS layer given a capabilities object. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * capabilities - {Object} The object returned from a <read> call to this │ │ │ │ + * format. │ │ │ │ + * config - {Object} Configuration properties for the layer. Defaults for │ │ │ │ + * the layer will apply if not provided. │ │ │ │ + * │ │ │ │ + * Required config properties: │ │ │ │ + * layer - {String} The layer identifier. │ │ │ │ + * │ │ │ │ + * Optional config properties: │ │ │ │ + * matrixSet - {String} The matrix set identifier, required if there is │ │ │ │ + * more than one matrix set in the layer capabilities. │ │ │ │ + * style - {String} The name of the style │ │ │ │ + * format - {String} Image format for the layer. Default is the first │ │ │ │ + * format returned in the GetCapabilities response. │ │ │ │ + * param - {Object} The dimensions values eg: {"Year": "2012"} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.WMTS>} A properly configured WMTS layer. Throws an │ │ │ │ + * error if an incomplete config is provided. Returns undefined if no │ │ │ │ + * layer could be created with the provided config. │ │ │ │ */ │ │ │ │ - loadContents: function() { │ │ │ │ + createLayer: function(capabilities, config) { │ │ │ │ + var layer; │ │ │ │ │ │ │ │ - // layers list div │ │ │ │ - this.layersDiv = document.createElement("div"); │ │ │ │ - this.layersDiv.id = this.id + "_layersDiv"; │ │ │ │ - OpenLayers.Element.addClass(this.layersDiv, "layersDiv"); │ │ │ │ + // confirm required properties are supplied in config │ │ │ │ + if (!('layer' in config)) { │ │ │ │ + throw new Error("Missing property 'layer' in configuration."); │ │ │ │ + } │ │ │ │ │ │ │ │ - this.baseLbl = document.createElement("div"); │ │ │ │ - this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer"); │ │ │ │ - OpenLayers.Element.addClass(this.baseLbl, "baseLbl"); │ │ │ │ + var contents = capabilities.contents; │ │ │ │ │ │ │ │ - this.baseLayersDiv = document.createElement("div"); │ │ │ │ - OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv"); │ │ │ │ + // find the layer definition with the given identifier │ │ │ │ + var layers = contents.layers; │ │ │ │ + var layerDef; │ │ │ │ + for (var i = 0, ii = contents.layers.length; i < ii; ++i) { │ │ │ │ + if (contents.layers[i].identifier === config.layer) { │ │ │ │ + layerDef = contents.layers[i]; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (!layerDef) { │ │ │ │ + throw new Error("Layer not found"); │ │ │ │ + } │ │ │ │ │ │ │ │ - this.dataLbl = document.createElement("div"); │ │ │ │ - this.dataLbl.innerHTML = OpenLayers.i18n("Overlays"); │ │ │ │ - OpenLayers.Element.addClass(this.dataLbl, "dataLbl"); │ │ │ │ + var format = config.format; │ │ │ │ + if (!format && layerDef.formats && layerDef.formats.length) { │ │ │ │ + format = layerDef.formats[0]; │ │ │ │ + } │ │ │ │ │ │ │ │ - this.dataLayersDiv = document.createElement("div"); │ │ │ │ - OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv"); │ │ │ │ + // find the matrixSet definition │ │ │ │ + var matrixSet; │ │ │ │ + if (config.matrixSet) { │ │ │ │ + matrixSet = contents.tileMatrixSets[config.matrixSet]; │ │ │ │ + } else if (layerDef.tileMatrixSetLinks.length >= 1) { │ │ │ │ + matrixSet = contents.tileMatrixSets[ │ │ │ │ + layerDef.tileMatrixSetLinks[0].tileMatrixSet]; │ │ │ │ + } │ │ │ │ + if (!matrixSet) { │ │ │ │ + throw new Error("matrixSet not found"); │ │ │ │ + } │ │ │ │ │ │ │ │ - if (this.ascending) { │ │ │ │ - this.layersDiv.appendChild(this.baseLbl); │ │ │ │ - this.layersDiv.appendChild(this.baseLayersDiv); │ │ │ │ - this.layersDiv.appendChild(this.dataLbl); │ │ │ │ - this.layersDiv.appendChild(this.dataLayersDiv); │ │ │ │ - } else { │ │ │ │ - this.layersDiv.appendChild(this.dataLbl); │ │ │ │ - this.layersDiv.appendChild(this.dataLayersDiv); │ │ │ │ - this.layersDiv.appendChild(this.baseLbl); │ │ │ │ - this.layersDiv.appendChild(this.baseLayersDiv); │ │ │ │ + // get the default style for the layer │ │ │ │ + var style; │ │ │ │ + for (var i = 0, ii = layerDef.styles.length; i < ii; ++i) { │ │ │ │ + style = layerDef.styles[i]; │ │ │ │ + if (style.isDefault) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ } │ │ │ │ │ │ │ │ - this.div.appendChild(this.layersDiv); │ │ │ │ + var requestEncoding = config.requestEncoding; │ │ │ │ + if (!requestEncoding) { │ │ │ │ + requestEncoding = "KVP"; │ │ │ │ + if (capabilities.operationsMetadata.GetTile.dcp.http) { │ │ │ │ + var http = capabilities.operationsMetadata.GetTile.dcp.http; │ │ │ │ + // Get first get method │ │ │ │ + if (http.get[0].constraints) { │ │ │ │ + var constraints = http.get[0].constraints; │ │ │ │ + var allowedValues = constraints.GetEncoding.allowedValues; │ │ │ │ │ │ │ │ - // maximize button div │ │ │ │ - var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png'); │ │ │ │ - this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ - "OpenLayers_Control_MaximizeDiv", │ │ │ │ - null, │ │ │ │ - null, │ │ │ │ - img, │ │ │ │ - "absolute"); │ │ │ │ - OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton"); │ │ │ │ - this.maximizeDiv.style.display = "none"; │ │ │ │ + // The OGC documentation is not clear if we should use │ │ │ │ + // REST or RESTful, ArcGis use RESTful, │ │ │ │ + // and OpenLayers use REST. │ │ │ │ + if (!allowedValues.KVP && │ │ │ │ + (allowedValues.REST || allowedValues.RESTful)) { │ │ │ │ + requestEncoding = "REST"; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - this.div.appendChild(this.maximizeDiv); │ │ │ │ + var dimensions = []; │ │ │ │ + var params = config.params || {}; │ │ │ │ + // to don't overwrite the changes in the applyDefaults │ │ │ │ + delete config.params; │ │ │ │ + for (var id = 0, ld = layerDef.dimensions.length; id < ld; id++) { │ │ │ │ + var dimension = layerDef.dimensions[id]; │ │ │ │ + dimensions.push(dimension.identifier); │ │ │ │ + if (!params.hasOwnProperty(dimension.identifier)) { │ │ │ │ + params[dimension.identifier] = dimension['default']; │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - // minimize button div │ │ │ │ - var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png'); │ │ │ │ - this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ - "OpenLayers_Control_MinimizeDiv", │ │ │ │ - null, │ │ │ │ - null, │ │ │ │ - img, │ │ │ │ - "absolute"); │ │ │ │ - OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton"); │ │ │ │ - this.minimizeDiv.style.display = "none"; │ │ │ │ + var projection = config.projection || matrixSet.supportedCRS.replace( │ │ │ │ + /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, "$1:$3"); │ │ │ │ + var units = config.units || │ │ │ │ + (projection === "EPSG:4326" ? "degrees" : "m"); │ │ │ │ │ │ │ │ - this.div.appendChild(this.minimizeDiv); │ │ │ │ + var resolutions = []; │ │ │ │ + for (var mid in matrixSet.matrixIds) { │ │ │ │ + if (matrixSet.matrixIds.hasOwnProperty(mid)) { │ │ │ │ + resolutions.push( │ │ │ │ + matrixSet.matrixIds[mid].scaleDenominator * 0.28E-3 / │ │ │ │ + OpenLayers.METERS_PER_INCH / │ │ │ │ + OpenLayers.INCHES_PER_UNIT[units]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + var url; │ │ │ │ + if (requestEncoding === "REST" && layerDef.resourceUrls) { │ │ │ │ + url = []; │ │ │ │ + var resourceUrls = layerDef.resourceUrls, │ │ │ │ + resourceUrl; │ │ │ │ + for (var t = 0, tt = layerDef.resourceUrls.length; t < tt; ++t) { │ │ │ │ + resourceUrl = layerDef.resourceUrls[t]; │ │ │ │ + if (resourceUrl.format === format && resourceUrl.resourceType === "tile") { │ │ │ │ + url.push(resourceUrl.template); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + var httpGet = capabilities.operationsMetadata.GetTile.dcp.http.get; │ │ │ │ + url = []; │ │ │ │ + var constraint; │ │ │ │ + for (var i = 0, ii = httpGet.length; i < ii; i++) { │ │ │ │ + constraint = httpGet[i].constraints; │ │ │ │ + if (!constraint || (constraint && constraint.GetEncoding.allowedValues[requestEncoding])) { │ │ │ │ + url.push(httpGet[i].url); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + return new OpenLayers.Layer.WMTS( │ │ │ │ + OpenLayers.Util.applyDefaults(config, { │ │ │ │ + url: url, │ │ │ │ + requestEncoding: requestEncoding, │ │ │ │ + name: layerDef.title, │ │ │ │ + style: style.identifier, │ │ │ │ + format: format, │ │ │ │ + matrixIds: matrixSet.matrixIds, │ │ │ │ + matrixSet: matrixSet.identifier, │ │ │ │ + projection: projection, │ │ │ │ + units: units, │ │ │ │ + resolutions: config.isBaseLayer === false ? undefined : resolutions, │ │ │ │ + serverResolutions: resolutions, │ │ │ │ + tileFullExtent: matrixSet.bounds, │ │ │ │ + dimensions: dimensions, │ │ │ │ + params: params │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.LayerSwitcher" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMTSCapabilities" │ │ │ │ + │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/ZoomPanel.js │ │ │ │ + OpenLayers/Format/WMSDescribeLayer.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control/Panel.js │ │ │ │ - * @requires OpenLayers/Control/ZoomIn.js │ │ │ │ - * @requires OpenLayers/Control/ZoomOut.js │ │ │ │ - * @requires OpenLayers/Control/ZoomToMaxExtent.js │ │ │ │ + * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.ZoomPanel │ │ │ │ - * The ZoomPanel control is a compact collecton of 3 zoom controls: a │ │ │ │ - * <OpenLayers.Control.ZoomIn>, a <OpenLayers.Control.ZoomToMaxExtent>, and a │ │ │ │ - * <OpenLayers.Control.ZoomOut>. By default it is drawn in the upper left │ │ │ │ - * corner of the map. │ │ │ │ - * │ │ │ │ - * Note: │ │ │ │ - * If you wish to use this class with the default images and you want │ │ │ │ - * it to look nice in ie6, you should add the following, conditionally │ │ │ │ - * added css stylesheet to your HTML file: │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * <!--[if lte IE 6]> │ │ │ │ - * <link rel="stylesheet" href="../theme/default/ie6-style.css" type="text/css" /> │ │ │ │ - * <![endif]--> │ │ │ │ - * (end) │ │ │ │ + * Class: OpenLayers.Format.WMSDescribeLayer │ │ │ │ + * Read SLD WMS DescribeLayer response │ │ │ │ + * DescribeLayer is meant to couple WMS to WFS and WCS │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control.Panel> │ │ │ │ + * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.ZoomPanel = OpenLayers.Class(OpenLayers.Control.Panel, { │ │ │ │ +OpenLayers.Format.WMSDescribeLayer = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.ZoomPanel │ │ │ │ - * Add the three zooming controls. │ │ │ │ + * APIProperty: defaultVersion │ │ │ │ + * {String} Version number to assume if none found. Default is "1.1.1". │ │ │ │ + */ │ │ │ │ + defaultVersion: "1.1.1", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WMSDescribeLayer │ │ │ │ + * Create a new parser for WMS DescribeLayer responses. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be used │ │ │ │ - * to extend the control. │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); │ │ │ │ - this.addControls([ │ │ │ │ - new OpenLayers.Control.ZoomIn(), │ │ │ │ - new OpenLayers.Control.ZoomToMaxExtent(), │ │ │ │ - new OpenLayers.Control.ZoomOut() │ │ │ │ - ]); │ │ │ │ - }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ZoomPanel" │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read DescribeLayer data from a string, and return the response. │ │ │ │ + * The OGC currently defines 2 formats which are allowed for output, │ │ │ │ + * so we need to parse these 2 types │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} Array of {<LayerDescription>} objects which have: │ │ │ │ + * - {String} owsType: WFS/WCS │ │ │ │ + * - {String} owsURL: the online resource │ │ │ │ + * - {String} typeName: the name of the typename on the service │ │ │ │ + */ │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSDescribeLayer" │ │ │ │ + │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/WMTSGetFeatureInfo.js │ │ │ │ + OpenLayers/Format/WMC/v1.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Handler/Click.js │ │ │ │ - * @requires OpenLayers/Handler/Hover.js │ │ │ │ - * @requires OpenLayers/Request.js │ │ │ │ - * @requires OpenLayers/Format/WMSGetFeatureInfo.js │ │ │ │ + * @requires OpenLayers/Format/WMC.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.WMTSGetFeatureInfo │ │ │ │ - * The WMTSGetFeatureInfo control uses a WMTS query to get information about a │ │ │ │ - * point on the map. The information may be in a display-friendly format │ │ │ │ - * such as HTML, or a machine-friendly format such as GML, depending on the │ │ │ │ - * server's capabilities and the client's configuration. This control │ │ │ │ - * handles click or hover events, attempts to parse the results using an │ │ │ │ - * OpenLayers.Format, and fires a 'getfeatureinfo' event for each layer │ │ │ │ - * queried. │ │ │ │ + * Class: OpenLayers.Format.WMC.v1 │ │ │ │ + * Superclass for WMC version 1 parsers. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.WMTSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ +OpenLayers.Format.WMC.v1 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: hover │ │ │ │ - * {Boolean} Send GetFeatureInfo requests when mouse stops moving. │ │ │ │ - * Default is false. │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ */ │ │ │ │ - hover: false, │ │ │ │ + namespaces: { │ │ │ │ + ol: "http://openlayers.org/context", │ │ │ │ + wmc: "http://www.opengis.net/context", │ │ │ │ + sld: "http://www.opengis.net/sld", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: requestEncoding │ │ │ │ - * {String} One of "KVP" or "REST". Only KVP encoding is supported at this │ │ │ │ - * time. │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} Schema location for a particular minor version. │ │ │ │ */ │ │ │ │ - requestEncoding: "KVP", │ │ │ │ + schemaLocation: "", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: drillDown │ │ │ │ - * {Boolean} Drill down over all WMTS layers in the map. When │ │ │ │ - * using drillDown mode, hover is not possible. A getfeatureinfo event │ │ │ │ - * will be fired for each layer queried. │ │ │ │ + * Method: getNamespacePrefix │ │ │ │ + * Get the namespace prefix for a given uri from the <namespaces> object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A namespace prefix or null if none found. │ │ │ │ */ │ │ │ │ - drillDown: false, │ │ │ │ + getNamespacePrefix: function(uri) { │ │ │ │ + var prefix = null; │ │ │ │ + if (uri == null) { │ │ │ │ + prefix = this.namespaces[this.defaultPrefix]; │ │ │ │ + } else { │ │ │ │ + for (prefix in this.namespaces) { │ │ │ │ + if (this.namespaces[prefix] == uri) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return prefix; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: maxFeatures │ │ │ │ - * {Integer} Maximum number of features to return from a WMTS query. This │ │ │ │ - * sets the feature_count parameter on WMTS GetFeatureInfo │ │ │ │ - * requests. │ │ │ │ + * Property: defaultPrefix │ │ │ │ */ │ │ │ │ - maxFeatures: 10, │ │ │ │ + defaultPrefix: "wmc", │ │ │ │ │ │ │ │ - /** APIProperty: clickCallback │ │ │ │ - * {String} The click callback to register in the │ │ │ │ - * {<OpenLayers.Handler.Click>} object created when the hover │ │ │ │ - * option is set to false. Default is "click". │ │ │ │ + /** │ │ │ │ + * Property: rootPrefix │ │ │ │ + * {String} Prefix on the root node that maps to the context namespace URI. │ │ │ │ */ │ │ │ │ - clickCallback: "click", │ │ │ │ + rootPrefix: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: layers │ │ │ │ - * {Array(<OpenLayers.Layer.WMTS>)} The layers to query for feature info. │ │ │ │ - * If omitted, all map WMTS layers will be considered. │ │ │ │ + * Property: defaultStyleName │ │ │ │ + * {String} Style name used if layer has no style param. Default is "". │ │ │ │ */ │ │ │ │ - layers: null, │ │ │ │ + defaultStyleName: "", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: queryVisible │ │ │ │ - * {Boolean} Filter out hidden layers when searching the map for layers to │ │ │ │ - * query. Default is true. │ │ │ │ + * Property: defaultStyleTitle │ │ │ │ + * {String} Default style title. Default is "Default". │ │ │ │ */ │ │ │ │ - queryVisible: true, │ │ │ │ + defaultStyleTitle: "Default", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: infoFormat │ │ │ │ - * {String} The mimetype to request from the server │ │ │ │ + * Constructor: OpenLayers.Format.WMC.v1 │ │ │ │ + * Instances of this class are not created directly. Use the │ │ │ │ + * <OpenLayers.Format.WMC> constructor instead. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - infoFormat: 'text/html', │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: vendorParams │ │ │ │ - * {Object} Additional parameters that will be added to the request, for │ │ │ │ - * WMTS implementations that support them. This could e.g. look like │ │ │ │ - * (start code) │ │ │ │ - * { │ │ │ │ - * radius: 5 │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ + * Method: read │ │ │ │ + * Read capabilities data from a string, and return a list of layers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} List of named layers. │ │ │ │ */ │ │ │ │ - vendorParams: {}, │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + var root = data.documentElement; │ │ │ │ + this.rootPrefix = root.prefix; │ │ │ │ + var context = { │ │ │ │ + version: root.getAttribute("version") │ │ │ │ + }; │ │ │ │ + this.runChildNodes(context, root); │ │ │ │ + return context; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: format │ │ │ │ - * {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses. │ │ │ │ - * Default is <OpenLayers.Format.WMSGetFeatureInfo>. │ │ │ │ + * Method: runChildNodes │ │ │ │ */ │ │ │ │ - format: null, │ │ │ │ + runChildNodes: function(obj, node) { │ │ │ │ + var children = node.childNodes; │ │ │ │ + var childNode, processor, prefix, local; │ │ │ │ + for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ + childNode = children[i]; │ │ │ │ + if (childNode.nodeType == 1) { │ │ │ │ + prefix = this.getNamespacePrefix(childNode.namespaceURI); │ │ │ │ + local = childNode.nodeName.split(":").pop(); │ │ │ │ + processor = this["read_" + prefix + "_" + local]; │ │ │ │ + if (processor) { │ │ │ │ + processor.apply(this, [obj, childNode]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: formatOptions │ │ │ │ - * {Object} Optional properties to set on the format (if one is not provided │ │ │ │ - * in the <format> property. │ │ │ │ + * Method: read_wmc_General │ │ │ │ */ │ │ │ │ - formatOptions: null, │ │ │ │ + read_wmc_General: function(context, node) { │ │ │ │ + this.runChildNodes(context, node); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: handlerOptions │ │ │ │ - * {Object} Additional options for the handlers used by this control, e.g. │ │ │ │ - * (start code) │ │ │ │ - * { │ │ │ │ - * "click": {delay: 100}, │ │ │ │ - * "hover": {delay: 300} │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ + * Method: read_wmc_BoundingBox │ │ │ │ */ │ │ │ │ + read_wmc_BoundingBox: function(context, node) { │ │ │ │ + context.projection = node.getAttribute("SRS"); │ │ │ │ + context.bounds = new OpenLayers.Bounds( │ │ │ │ + node.getAttribute("minx"), node.getAttribute("miny"), │ │ │ │ + node.getAttribute("maxx"), node.getAttribute("maxy") │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: handler │ │ │ │ - * {Object} Reference to the <OpenLayers.Handler> for this control │ │ │ │ + * Method: read_wmc_LayerList │ │ │ │ */ │ │ │ │ - handler: null, │ │ │ │ + read_wmc_LayerList: function(context, node) { │ │ │ │ + // layersContext is an array containing info for each layer │ │ │ │ + context.layersContext = []; │ │ │ │ + this.runChildNodes(context, node); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: hoverRequest │ │ │ │ - * {<OpenLayers.Request>} contains the currently running hover request │ │ │ │ - * (if any). │ │ │ │ + * Method: read_wmc_Layer │ │ │ │ */ │ │ │ │ - hoverRequest: null, │ │ │ │ + read_wmc_Layer: function(context, node) { │ │ │ │ + var layerContext = { │ │ │ │ + visibility: (node.getAttribute("hidden") != "1"), │ │ │ │ + queryable: (node.getAttribute("queryable") == "1"), │ │ │ │ + formats: [], │ │ │ │ + styles: [], │ │ │ │ + metadata: {} │ │ │ │ + }; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * beforegetfeatureinfo - Triggered before each request is sent. │ │ │ │ - * The event object has an *xy* property with the position of the │ │ │ │ - * mouse click or hover event that triggers the request and a *layer* │ │ │ │ - * property referencing the layer about to be queried. If a listener │ │ │ │ - * returns false, the request will not be issued. │ │ │ │ - * getfeatureinfo - Triggered when a GetFeatureInfo response is received. │ │ │ │ - * The event object has a *text* property with the body of the │ │ │ │ - * response (String), a *features* property with an array of the │ │ │ │ - * parsed features, an *xy* property with the position of the mouse │ │ │ │ - * click or hover event that triggered the request, a *layer* property │ │ │ │ - * referencing the layer queried and a *request* property with the │ │ │ │ - * request itself. If drillDown is set to true, one event will be fired │ │ │ │ - * for each layer queried. │ │ │ │ - * exception - Triggered when a GetFeatureInfo request fails (with a │ │ │ │ - * status other than 200) or whenparsing fails. Listeners will receive │ │ │ │ - * an event with *request*, *xy*, and *layer* properties. In the case │ │ │ │ - * of a parsing error, the event will also contain an *error* property. │ │ │ │ - */ │ │ │ │ + this.runChildNodes(layerContext, node); │ │ │ │ + // set properties common to multiple objects on layer options/params │ │ │ │ + context.layersContext.push(layerContext); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: pending │ │ │ │ - * {Number} The number of pending requests. │ │ │ │ + /** │ │ │ │ + * Method: read_wmc_Extension │ │ │ │ */ │ │ │ │ - pending: 0, │ │ │ │ + read_wmc_Extension: function(obj, node) { │ │ │ │ + this.runChildNodes(obj, node); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: <OpenLayers.Control.WMTSGetFeatureInfo> │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} │ │ │ │ + * Method: read_ol_units │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - options.handlerOptions = options.handlerOptions || {}; │ │ │ │ - │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + read_ol_units: function(layerContext, node) { │ │ │ │ + layerContext.units = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (!this.format) { │ │ │ │ - this.format = new OpenLayers.Format.WMSGetFeatureInfo( │ │ │ │ - options.formatOptions │ │ │ │ - ); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: read_ol_maxExtent │ │ │ │ + */ │ │ │ │ + read_ol_maxExtent: function(obj, node) { │ │ │ │ + var bounds = new OpenLayers.Bounds( │ │ │ │ + node.getAttribute("minx"), node.getAttribute("miny"), │ │ │ │ + node.getAttribute("maxx"), node.getAttribute("maxy") │ │ │ │ + ); │ │ │ │ + obj.maxExtent = bounds; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.drillDown === true) { │ │ │ │ - this.hover = false; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: read_ol_transparent │ │ │ │ + */ │ │ │ │ + read_ol_transparent: function(layerContext, node) { │ │ │ │ + layerContext.transparent = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.hover) { │ │ │ │ - this.handler = new OpenLayers.Handler.Hover( │ │ │ │ - this, { │ │ │ │ - move: this.cancelHover, │ │ │ │ - pause: this.getInfoForHover │ │ │ │ - }, │ │ │ │ - OpenLayers.Util.extend( │ │ │ │ - this.handlerOptions.hover || {}, { │ │ │ │ - delay: 250 │ │ │ │ - } │ │ │ │ - ) │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - var callbacks = {}; │ │ │ │ - callbacks[this.clickCallback] = this.getInfoForClick; │ │ │ │ - this.handler = new OpenLayers.Handler.Click( │ │ │ │ - this, callbacks, this.handlerOptions.click || {} │ │ │ │ - ); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: read_ol_numZoomLevels │ │ │ │ + */ │ │ │ │ + read_ol_numZoomLevels: function(layerContext, node) { │ │ │ │ + layerContext.numZoomLevels = parseInt(this.getChildValue(node)); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getInfoForClick │ │ │ │ - * Called on click │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * Method: read_ol_opacity │ │ │ │ */ │ │ │ │ - getInfoForClick: function(evt) { │ │ │ │ - this.request(evt.xy, {}); │ │ │ │ + read_ol_opacity: function(layerContext, node) { │ │ │ │ + layerContext.opacity = parseFloat(this.getChildValue(node)); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getInfoForHover │ │ │ │ - * Pause callback for the hover handler │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} │ │ │ │ + * Method: read_ol_singleTile │ │ │ │ */ │ │ │ │ - getInfoForHover: function(evt) { │ │ │ │ - this.request(evt.xy, { │ │ │ │ - hover: true │ │ │ │ - }); │ │ │ │ + read_ol_singleTile: function(layerContext, node) { │ │ │ │ + layerContext.singleTile = (this.getChildValue(node) == "true"); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: cancelHover │ │ │ │ - * Cancel callback for the hover handler │ │ │ │ + * Method: read_ol_tileSize │ │ │ │ */ │ │ │ │ - cancelHover: function() { │ │ │ │ - if (this.hoverRequest) { │ │ │ │ - --this.pending; │ │ │ │ - if (this.pending <= 0) { │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ - this.pending = 0; │ │ │ │ - } │ │ │ │ - this.hoverRequest.abort(); │ │ │ │ - this.hoverRequest = null; │ │ │ │ - } │ │ │ │ + read_ol_tileSize: function(layerContext, node) { │ │ │ │ + var obj = { │ │ │ │ + "width": node.getAttribute("width"), │ │ │ │ + "height": node.getAttribute("height") │ │ │ │ + }; │ │ │ │ + layerContext.tileSize = obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: findLayers │ │ │ │ - * Internal method to get the layers, independent of whether we are │ │ │ │ - * inspecting the map or using a client-provided array │ │ │ │ + * Method: read_ol_isBaseLayer │ │ │ │ */ │ │ │ │ - findLayers: function() { │ │ │ │ - var candidates = this.layers || this.map.layers; │ │ │ │ - var layers = []; │ │ │ │ - var layer; │ │ │ │ - for (var i = candidates.length - 1; i >= 0; --i) { │ │ │ │ - layer = candidates[i]; │ │ │ │ - if (layer instanceof OpenLayers.Layer.WMTS && │ │ │ │ - layer.requestEncoding === this.requestEncoding && │ │ │ │ - (!this.queryVisible || layer.getVisibility())) { │ │ │ │ - layers.push(layer); │ │ │ │ - if (!this.drillDown || this.hover) { │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return layers; │ │ │ │ + read_ol_isBaseLayer: function(layerContext, node) { │ │ │ │ + layerContext.isBaseLayer = (this.getChildValue(node) == "true"); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: buildRequestOptions │ │ │ │ - * Build an object with the relevant options for the GetFeatureInfo request. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.WMTS>} A WMTS layer. │ │ │ │ - * xy - {<OpenLayers.Pixel>} The position on the map where the │ │ │ │ - * mouse event occurred. │ │ │ │ + * Method: read_ol_displayInLayerSwitcher │ │ │ │ */ │ │ │ │ - buildRequestOptions: function(layer, xy) { │ │ │ │ - var loc = this.map.getLonLatFromPixel(xy); │ │ │ │ - var getTileUrl = layer.getURL( │ │ │ │ - new OpenLayers.Bounds(loc.lon, loc.lat, loc.lon, loc.lat) │ │ │ │ - ); │ │ │ │ - var params = OpenLayers.Util.getParameters(getTileUrl); │ │ │ │ - var tileInfo = layer.getTileInfo(loc); │ │ │ │ - OpenLayers.Util.extend(params, { │ │ │ │ - service: "WMTS", │ │ │ │ - version: layer.version, │ │ │ │ - request: "GetFeatureInfo", │ │ │ │ - infoFormat: this.infoFormat, │ │ │ │ - i: tileInfo.i, │ │ │ │ - j: tileInfo.j │ │ │ │ - }); │ │ │ │ - OpenLayers.Util.applyDefaults(params, this.vendorParams); │ │ │ │ - return { │ │ │ │ - url: OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url, │ │ │ │ - params: OpenLayers.Util.upperCaseObject(params), │ │ │ │ - callback: function(request) { │ │ │ │ - this.handleResponse(xy, request, layer); │ │ │ │ - }, │ │ │ │ - scope: this │ │ │ │ - }; │ │ │ │ + read_ol_displayInLayerSwitcher: function(layerContext, node) { │ │ │ │ + layerContext.displayInLayerSwitcher = (this.getChildValue(node) == "true"); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: request │ │ │ │ - * Sends a GetFeatureInfo request to the WMTS │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * xy - {<OpenLayers.Pixel>} The position on the map where the mouse event │ │ │ │ - * occurred. │ │ │ │ - * options - {Object} additional options for this method. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * - *hover* {Boolean} true if we do the request for the hover handler │ │ │ │ + * Method: read_wmc_Server │ │ │ │ */ │ │ │ │ - request: function(xy, options) { │ │ │ │ - options = options || {}; │ │ │ │ - var layers = this.findLayers(); │ │ │ │ - if (layers.length > 0) { │ │ │ │ - var issue, layer; │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - layer = layers[i]; │ │ │ │ - issue = this.events.triggerEvent("beforegetfeatureinfo", { │ │ │ │ - xy: xy, │ │ │ │ - layer: layer │ │ │ │ - }); │ │ │ │ - if (issue !== false) { │ │ │ │ - ++this.pending; │ │ │ │ - var requestOptions = this.buildRequestOptions(layer, xy); │ │ │ │ - var request = OpenLayers.Request.GET(requestOptions); │ │ │ │ - if (options.hover === true) { │ │ │ │ - this.hoverRequest = request; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.pending > 0) { │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + read_wmc_Server: function(layerContext, node) { │ │ │ │ + layerContext.version = node.getAttribute("version"); │ │ │ │ + layerContext.url = this.getOnlineResource_href(node); │ │ │ │ + layerContext.metadata.servertitle = node.getAttribute("title"); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleResponse │ │ │ │ - * Handler for the GetFeatureInfo response. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * xy - {<OpenLayers.Pixel>} The position on the map where the mouse event │ │ │ │ - * occurred. │ │ │ │ - * request - {XMLHttpRequest} The request object. │ │ │ │ - * layer - {<OpenLayers.Layer.WMTS>} The queried layer. │ │ │ │ + * Method: read_wmc_FormatList │ │ │ │ */ │ │ │ │ - handleResponse: function(xy, request, layer) { │ │ │ │ - --this.pending; │ │ │ │ - if (this.pending <= 0) { │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ - this.pending = 0; │ │ │ │ - } │ │ │ │ - if (request.status && (request.status < 200 || request.status >= 300)) { │ │ │ │ - this.events.triggerEvent("exception", { │ │ │ │ - xy: xy, │ │ │ │ - request: request, │ │ │ │ - layer: layer │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText; │ │ │ │ - } │ │ │ │ - var features, except; │ │ │ │ - try { │ │ │ │ - features = this.format.read(doc); │ │ │ │ - } catch (error) { │ │ │ │ - except = true; │ │ │ │ - this.events.triggerEvent("exception", { │ │ │ │ - xy: xy, │ │ │ │ - request: request, │ │ │ │ - error: error, │ │ │ │ - layer: layer │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - if (!except) { │ │ │ │ - this.events.triggerEvent("getfeatureinfo", { │ │ │ │ - text: request.responseText, │ │ │ │ - features: features, │ │ │ │ - request: request, │ │ │ │ - xy: xy, │ │ │ │ - layer: layer │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + read_wmc_FormatList: function(layerContext, node) { │ │ │ │ + this.runChildNodes(layerContext, node); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.WMTSGetFeatureInfo" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/Attribution.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.Attribution │ │ │ │ - * The attribution control adds attribution from layers to the map display. │ │ │ │ - * It uses 'attribution' property of each layer. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.Attribution = │ │ │ │ - OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: separator │ │ │ │ - * {String} String used to separate layers. │ │ │ │ - */ │ │ │ │ - separator: ", ", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: template │ │ │ │ - * {String} Template for the attribution. This has to include the substring │ │ │ │ - * "${layers}", which will be replaced by the layer specific │ │ │ │ - * attributions, separated by <separator>. The default is "${layers}". │ │ │ │ - */ │ │ │ │ - template: "${layers}", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.Attribution │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Options for control. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * Destroy control. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - this.map.events.un({ │ │ │ │ - "removelayer": this.updateAttribution, │ │ │ │ - "addlayer": this.updateAttribution, │ │ │ │ - "changelayer": this.updateAttribution, │ │ │ │ - "changebaselayer": this.updateAttribution, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ - * Initialize control. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A reference to the DIV DOMElement containing the control │ │ │ │ - */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - │ │ │ │ - this.map.events.on({ │ │ │ │ - 'changebaselayer': this.updateAttribution, │ │ │ │ - 'changelayer': this.updateAttribution, │ │ │ │ - 'addlayer': this.updateAttribution, │ │ │ │ - 'removelayer': this.updateAttribution, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.updateAttribution(); │ │ │ │ - │ │ │ │ - return this.div; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: updateAttribution │ │ │ │ - * Update attribution string. │ │ │ │ - */ │ │ │ │ - updateAttribution: function() { │ │ │ │ - var attributions = []; │ │ │ │ - if (this.map && this.map.layers) { │ │ │ │ - for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - if (layer.attribution && layer.getVisibility()) { │ │ │ │ - // add attribution only if attribution text is unique │ │ │ │ - if (OpenLayers.Util.indexOf( │ │ │ │ - attributions, layer.attribution) === -1) { │ │ │ │ - attributions.push(layer.attribution); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.div.innerHTML = OpenLayers.String.format(this.template, { │ │ │ │ - layers: attributions.join(this.separator) │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Attribution" │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/Split.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Handler/Path.js │ │ │ │ - * @requires OpenLayers/Layer/Vector.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.Split │ │ │ │ - * Acts as a split feature agent while editing vector features. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * beforesplit - Triggered before a split occurs. Listeners receive an │ │ │ │ - * event object with *source* and *target* properties. │ │ │ │ - * split - Triggered when a split occurs. Listeners receive an event with │ │ │ │ - * an *original* property and a *features* property. The original │ │ │ │ - * is a reference to the target feature that the sketch or modified │ │ │ │ - * feature intersects. The features property is a list of all features │ │ │ │ - * that result from this single split. This event is triggered before │ │ │ │ - * the resulting features are added to the layer (while the layer still │ │ │ │ - * has a reference to the original). │ │ │ │ - * aftersplit - Triggered after all splits resulting from a single sketch │ │ │ │ - * or feature modification have occurred. The original features │ │ │ │ - * have been destroyed and features that result from the split │ │ │ │ - * have already been added to the layer. Listeners receive an event │ │ │ │ - * with a *source* and *features* property. The source references the │ │ │ │ - * sketch or modified feature used as a splitter. The features │ │ │ │ - * property is a list of all resulting features. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: layer │ │ │ │ - * {<OpenLayers.Layer.Vector>} The target layer with features to be split. │ │ │ │ - * Set at construction or after construction with <setLayer>. │ │ │ │ - */ │ │ │ │ - layer: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: source │ │ │ │ - * {<OpenLayers.Layer.Vector>} Optional source layer. Any newly created │ │ │ │ - * or modified features from this layer will be used to split features │ │ │ │ - * on the target layer. If not provided, a temporary sketch layer will │ │ │ │ - * be created. │ │ │ │ - */ │ │ │ │ - source: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: sourceOptions │ │ │ │ - * {Options} If a temporary sketch layer is created, these layer options │ │ │ │ - * will be applied. │ │ │ │ - */ │ │ │ │ - sourceOptions: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: tolerance │ │ │ │ - * {Number} Distance between the calculated intersection and a vertex on │ │ │ │ - * the source geometry below which the existing vertex will be used │ │ │ │ - * for the split. Default is null. │ │ │ │ - */ │ │ │ │ - tolerance: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: edge │ │ │ │ - * {Boolean} Allow splits given intersection of edges only. Default is │ │ │ │ - * true. If false, a vertex on the source must be within the │ │ │ │ - * <tolerance> distance of the calculated intersection for a split │ │ │ │ - * to occur. │ │ │ │ - */ │ │ │ │ - edge: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: deferDelete │ │ │ │ - * {Boolean} Instead of removing features from the layer, set feature │ │ │ │ - * states of split features to DELETE. This assumes a save strategy │ │ │ │ - * or other component is in charge of removing features from the │ │ │ │ - * layer. Default is false. If false, split features will be │ │ │ │ - * immediately deleted from the layer. │ │ │ │ - */ │ │ │ │ - deferDelete: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: mutual │ │ │ │ - * {Boolean} If source and target layers are the same, split source │ │ │ │ - * features and target features where they intersect. Default is │ │ │ │ - * true. If false, only target features will be split. │ │ │ │ - */ │ │ │ │ - mutual: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: targetFilter │ │ │ │ - * {<OpenLayers.Filter>} Optional filter that will be evaluated │ │ │ │ - * to determine if a feature from the target layer is eligible for │ │ │ │ - * splitting. │ │ │ │ - */ │ │ │ │ - targetFilter: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: sourceFilter │ │ │ │ - * {<OpenLayers.Filter>} Optional filter that will be evaluated │ │ │ │ - * to determine if a feature from the source layer is eligible for │ │ │ │ - * splitting. │ │ │ │ - */ │ │ │ │ - sourceFilter: null, │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Property: handler │ │ │ │ - * {<OpenLayers.Handler.Path>} The temporary sketch handler created if │ │ │ │ - * no source layer is provided. │ │ │ │ - */ │ │ │ │ - handler: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.Split │ │ │ │ - * Creates a new split control. A control is constructed with a target │ │ │ │ - * layer and an optional source layer. While the control is active, │ │ │ │ - * creating new features or modifying existing features on the source │ │ │ │ - * layer will result in splitting any eligible features on the target │ │ │ │ - * layer. If no source layer is provided, a temporary sketch layer will │ │ │ │ - * be created to create lines for splitting features on the target. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An object containing all configuration properties for │ │ │ │ - * the control. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} The target layer. Features from this │ │ │ │ - * layer will be split by new or modified features on the source layer │ │ │ │ - * or temporary sketch layer. │ │ │ │ - * source - {<OpenLayers.Layer.Vector>} Optional source layer. If provided │ │ │ │ - * newly created features or modified features will be used to split │ │ │ │ - * features on the target layer. If not provided, a temporary sketch │ │ │ │ - * layer will be created for drawing lines. │ │ │ │ - * tolerance - {Number} Optional value for the distance between a source │ │ │ │ - * vertex and the calculated intersection below which the split will │ │ │ │ - * occur at the vertex. │ │ │ │ - * edge - {Boolean} Allow splits given intersection of edges only. Default │ │ │ │ - * is true. If false, a vertex on the source must be within the │ │ │ │ - * <tolerance> distance of the calculated intersection for a split │ │ │ │ - * to occur. │ │ │ │ - * mutual - {Boolean} If source and target are the same, split source │ │ │ │ - * features and target features where they intersect. Default is │ │ │ │ - * true. If false, only target features will be split. │ │ │ │ - * targetFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated │ │ │ │ - * to determine if a feature from the target layer is eligible for │ │ │ │ - * splitting. │ │ │ │ - * sourceFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated │ │ │ │ - * to determine if a feature from the target layer is eligible for │ │ │ │ - * splitting. │ │ │ │ + * Method: read_wmc_Format │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.options = options || {}; // TODO: this could be done by the super │ │ │ │ - │ │ │ │ - // set the source layer if provided │ │ │ │ - if (this.options.source) { │ │ │ │ - this.setSource(this.options.source); │ │ │ │ + read_wmc_Format: function(layerContext, node) { │ │ │ │ + var format = { │ │ │ │ + value: this.getChildValue(node) │ │ │ │ + }; │ │ │ │ + if (node.getAttribute("current") == "1") { │ │ │ │ + format.current = true; │ │ │ │ } │ │ │ │ + layerContext.formats.push(format); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setSource │ │ │ │ - * Set the source layer for edits layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} The new source layer layer. If │ │ │ │ - * null, a temporary sketch layer will be created. │ │ │ │ + * Method: read_wmc_StyleList │ │ │ │ */ │ │ │ │ - setSource: function(layer) { │ │ │ │ - if (this.active) { │ │ │ │ - this.deactivate(); │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.destroy(); │ │ │ │ - delete this.handler; │ │ │ │ - } │ │ │ │ - this.source = layer; │ │ │ │ - this.activate(); │ │ │ │ - } else { │ │ │ │ - this.source = layer; │ │ │ │ - } │ │ │ │ + read_wmc_StyleList: function(layerContext, node) { │ │ │ │ + this.runChildNodes(layerContext, node); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Activate the control. Activating the control registers listeners for │ │ │ │ - * editing related events so that during feature creation and │ │ │ │ - * modification, features in the target will be considered for │ │ │ │ - * splitting. │ │ │ │ + * Method: read_wmc_Style │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Control.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - if (!this.source) { │ │ │ │ - if (!this.handler) { │ │ │ │ - this.handler = new OpenLayers.Handler.Path(this, { │ │ │ │ - done: function(geometry) { │ │ │ │ - this.onSketchComplete({ │ │ │ │ - feature: new OpenLayers.Feature.Vector(geometry) │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, { │ │ │ │ - layerOptions: this.sourceOptions │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - this.handler.activate(); │ │ │ │ - } else if (this.source.events) { │ │ │ │ - this.source.events.on({ │ │ │ │ - sketchcomplete: this.onSketchComplete, │ │ │ │ - afterfeaturemodified: this.afterFeatureModified, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ + read_wmc_Style: function(layerContext, node) { │ │ │ │ + var style = {}; │ │ │ │ + this.runChildNodes(style, node); │ │ │ │ + if (node.getAttribute("current") == "1") { │ │ │ │ + style.current = true; │ │ │ │ } │ │ │ │ - return activated; │ │ │ │ + layerContext.styles.push(style); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the control. Deactivating the control unregisters listeners │ │ │ │ - * so feature editing may proceed without engaging the split agent. │ │ │ │ + * Method: read_wmc_SLD │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Control.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - if (this.source && this.source.events) { │ │ │ │ - this.source.events.un({ │ │ │ │ - sketchcomplete: this.onSketchComplete, │ │ │ │ - afterfeaturemodified: this.afterFeatureModified, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return deactivated; │ │ │ │ + read_wmc_SLD: function(style, node) { │ │ │ │ + this.runChildNodes(style, node); │ │ │ │ + // style either comes back with an href or a body property │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onSketchComplete │ │ │ │ - * Registered as a listener for the sketchcomplete event on the editable │ │ │ │ - * layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * event - {Object} The sketch complete event. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Stop the sketch from being added to the layer (it has been │ │ │ │ - * split). │ │ │ │ + * Method: read_sld_StyledLayerDescriptor │ │ │ │ */ │ │ │ │ - onSketchComplete: function(event) { │ │ │ │ - this.feature = null; │ │ │ │ - return !this.considerSplit(event.feature); │ │ │ │ + read_sld_StyledLayerDescriptor: function(sld, node) { │ │ │ │ + var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ + sld.body = xml; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: afterFeatureModified │ │ │ │ - * Registered as a listener for the afterfeaturemodified event on the │ │ │ │ - * editable layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * event - {Object} The after feature modified event. │ │ │ │ + * Method: read_sld_FeatureTypeStyle │ │ │ │ */ │ │ │ │ - afterFeatureModified: function(event) { │ │ │ │ - if (event.modified) { │ │ │ │ - var feature = event.feature; │ │ │ │ - if (typeof feature.geometry.split === "function") { │ │ │ │ - this.feature = event.feature; │ │ │ │ - this.considerSplit(event.feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + read_sld_FeatureTypeStyle: function(sld, node) { │ │ │ │ + var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ + sld.body = xml; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: removeByGeometry │ │ │ │ - * Remove a feature from a list based on the given geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} A list of features. │ │ │ │ - * geometry - {<OpenLayers.Geometry>} A geometry. │ │ │ │ + * Method: read_wmc_OnlineResource │ │ │ │ */ │ │ │ │ - removeByGeometry: function(features, geometry) { │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - if (features[i].geometry === geometry) { │ │ │ │ - features.splice(i, 1); │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + read_wmc_OnlineResource: function(obj, node) { │ │ │ │ + obj.href = this.getAttributeNS( │ │ │ │ + node, this.namespaces.xlink, "href" │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: isEligible │ │ │ │ - * Test if a target feature is eligible for splitting. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * target - {<OpenLayers.Feature.Vector>} The target feature. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The target is eligible for splitting. │ │ │ │ + * Method: read_wmc_Name │ │ │ │ */ │ │ │ │ - isEligible: function(target) { │ │ │ │ - if (!target.geometry) { │ │ │ │ - return false; │ │ │ │ - } else { │ │ │ │ - return ( │ │ │ │ - target.state !== OpenLayers.State.DELETE │ │ │ │ - ) && ( │ │ │ │ - typeof target.geometry.split === "function" │ │ │ │ - ) && ( │ │ │ │ - this.feature !== target │ │ │ │ - ) && ( │ │ │ │ - !this.targetFilter || │ │ │ │ - this.targetFilter.evaluate(target.attributes) │ │ │ │ - ); │ │ │ │ + read_wmc_Name: function(obj, node) { │ │ │ │ + var name = this.getChildValue(node); │ │ │ │ + if (name) { │ │ │ │ + obj.name = name; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: considerSplit │ │ │ │ - * Decide whether or not to split target features with the supplied │ │ │ │ - * feature. If <mutual> is true, both the source and target features │ │ │ │ - * will be split if eligible. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The newly created or modified │ │ │ │ - * feature. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The supplied feature was split (and destroyed). │ │ │ │ + * Method: read_wmc_Title │ │ │ │ */ │ │ │ │ - considerSplit: function(feature) { │ │ │ │ - var sourceSplit = false; │ │ │ │ - var targetSplit = false; │ │ │ │ - if (!this.sourceFilter || │ │ │ │ - this.sourceFilter.evaluate(feature.attributes)) { │ │ │ │ - var features = this.layer && this.layer.features || []; │ │ │ │ - var target, results, proceed; │ │ │ │ - var additions = [], │ │ │ │ - removals = []; │ │ │ │ - var mutual = (this.layer === this.source) && this.mutual; │ │ │ │ - var options = { │ │ │ │ - edge: this.edge, │ │ │ │ - tolerance: this.tolerance, │ │ │ │ - mutual: mutual │ │ │ │ - }; │ │ │ │ - var sourceParts = [feature.geometry]; │ │ │ │ - var targetFeature, targetParts; │ │ │ │ - var source, parts; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - targetFeature = features[i]; │ │ │ │ - if (this.isEligible(targetFeature)) { │ │ │ │ - targetParts = [targetFeature.geometry]; │ │ │ │ - // work through source geoms - this array may change │ │ │ │ - for (var j = 0; j < sourceParts.length; ++j) { │ │ │ │ - source = sourceParts[j]; │ │ │ │ - // work through target parts - this array may change │ │ │ │ - for (var k = 0; k < targetParts.length; ++k) { │ │ │ │ - target = targetParts[k]; │ │ │ │ - if (source.getBounds().intersectsBounds(target.getBounds())) { │ │ │ │ - results = source.split(target, options); │ │ │ │ - if (results) { │ │ │ │ - proceed = this.events.triggerEvent( │ │ │ │ - "beforesplit", { │ │ │ │ - source: feature, │ │ │ │ - target: targetFeature │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - if (proceed !== false) { │ │ │ │ - if (mutual) { │ │ │ │ - parts = results[0]; │ │ │ │ - // handle parts that result from source splitting │ │ │ │ - if (parts.length > 1) { │ │ │ │ - // splice in new source parts │ │ │ │ - parts.unshift(j, 1); // add args for splice below │ │ │ │ - Array.prototype.splice.apply(sourceParts, parts); │ │ │ │ - j += parts.length - 3; │ │ │ │ - } │ │ │ │ - results = results[1]; │ │ │ │ - } │ │ │ │ - // handle parts that result from target splitting │ │ │ │ - if (results.length > 1) { │ │ │ │ - // splice in new target parts │ │ │ │ - results.unshift(k, 1); // add args for splice below │ │ │ │ - Array.prototype.splice.apply(targetParts, results); │ │ │ │ - k += results.length - 3; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (targetParts && targetParts.length > 1) { │ │ │ │ - this.geomsToFeatures(targetFeature, targetParts); │ │ │ │ - this.events.triggerEvent("split", { │ │ │ │ - original: targetFeature, │ │ │ │ - features: targetParts │ │ │ │ - }); │ │ │ │ - Array.prototype.push.apply(additions, targetParts); │ │ │ │ - removals.push(targetFeature); │ │ │ │ - targetSplit = true; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (sourceParts && sourceParts.length > 1) { │ │ │ │ - this.geomsToFeatures(feature, sourceParts); │ │ │ │ - this.events.triggerEvent("split", { │ │ │ │ - original: feature, │ │ │ │ - features: sourceParts │ │ │ │ - }); │ │ │ │ - Array.prototype.push.apply(additions, sourceParts); │ │ │ │ - removals.push(feature); │ │ │ │ - sourceSplit = true; │ │ │ │ - } │ │ │ │ - if (sourceSplit || targetSplit) { │ │ │ │ - // remove and add feature events are suppressed │ │ │ │ - // listen for split event on this control instead │ │ │ │ - if (this.deferDelete) { │ │ │ │ - // Set state instead of removing. Take care to avoid │ │ │ │ - // setting delete for features that have not yet been │ │ │ │ - // inserted - those should be destroyed immediately. │ │ │ │ - var feat, destroys = []; │ │ │ │ - for (var i = 0, len = removals.length; i < len; ++i) { │ │ │ │ - feat = removals[i]; │ │ │ │ - if (feat.state === OpenLayers.State.INSERT) { │ │ │ │ - destroys.push(feat); │ │ │ │ - } else { │ │ │ │ - feat.state = OpenLayers.State.DELETE; │ │ │ │ - this.layer.drawFeature(feat); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.layer.destroyFeatures(destroys, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - for (var i = 0, len = additions.length; i < len; ++i) { │ │ │ │ - additions[i].state = OpenLayers.State.INSERT; │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.layer.destroyFeatures(removals, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - this.layer.addFeatures(additions, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.events.triggerEvent("aftersplit", { │ │ │ │ - source: feature, │ │ │ │ - features: additions │ │ │ │ - }); │ │ │ │ - } │ │ │ │ + read_wmc_Title: function(obj, node) { │ │ │ │ + var title = this.getChildValue(node); │ │ │ │ + if (title) { │ │ │ │ + obj.title = title; │ │ │ │ } │ │ │ │ - return sourceSplit; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: geomsToFeatures │ │ │ │ - * Create new features given a template feature and a list of geometries. │ │ │ │ - * The list of geometries is modified in place. The result will be │ │ │ │ - * a list of new features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} The feature to be cloned. │ │ │ │ - * geoms - {Array(<OpenLayers.Geometry>)} List of goemetries. This will │ │ │ │ - * become a list of new features. │ │ │ │ + * Method: read_wmc_MetadataURL │ │ │ │ */ │ │ │ │ - geomsToFeatures: function(feature, geoms) { │ │ │ │ - var clone = feature.clone(); │ │ │ │ - delete clone.geometry; │ │ │ │ - var newFeature; │ │ │ │ - for (var i = 0, len = geoms.length; i < len; ++i) { │ │ │ │ - // turn results list from geoms to features │ │ │ │ - newFeature = clone.clone(); │ │ │ │ - newFeature.geometry = geoms[i]; │ │ │ │ - newFeature.state = OpenLayers.State.INSERT; │ │ │ │ - geoms[i] = newFeature; │ │ │ │ - } │ │ │ │ + read_wmc_MetadataURL: function(layerContext, node) { │ │ │ │ + layerContext.metadataURL = this.getOnlineResource_href(node); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ - * Clean up the control. │ │ │ │ + * Method: read_wmc_KeywordList │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.deactivate(); // TODO: this should be handled by the super │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.destroy.call(this); │ │ │ │ + read_wmc_KeywordList: function(context, node) { │ │ │ │ + context.keywords = []; │ │ │ │ + this.runChildNodes(context.keywords, node); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Split" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/KeyboardDefaults.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Handler/Keyboard.js │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.KeyboardDefaults │ │ │ │ - * The KeyboardDefaults control adds panning and zooming functions, controlled │ │ │ │ - * with the keyboard. By default arrow keys pan, +/- keys zoom & Page Up/Page │ │ │ │ - * Down/Home/End scroll by three quarters of a page. │ │ │ │ - * │ │ │ │ - * This control has no visible appearance. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * true. │ │ │ │ - */ │ │ │ │ - autoActivate: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: slideFactor │ │ │ │ - * Pixels to slide by. │ │ │ │ - */ │ │ │ │ - slideFactor: 75, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: observeElement │ │ │ │ - * {DOMelement|String} The DOM element to handle keys for. You │ │ │ │ - * can use the map div here, to have the navigation keys │ │ │ │ - * work when the map div has the focus. If undefined the │ │ │ │ - * document is used. │ │ │ │ - */ │ │ │ │ - observeElement: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.KeyboardDefaults │ │ │ │ - */ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ - * Create handler. │ │ │ │ + * Method: read_wmc_Keyword │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - var observeElement = this.observeElement || document; │ │ │ │ - this.handler = new OpenLayers.Handler.Keyboard(this, { │ │ │ │ - "keydown": this.defaultKeyPress │ │ │ │ - }, { │ │ │ │ - observeElement: observeElement │ │ │ │ - }); │ │ │ │ + read_wmc_Keyword: function(keywords, node) { │ │ │ │ + keywords.push(this.getChildValue(node)); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: defaultKeyPress │ │ │ │ - * When handling the key event, we only use evt.keyCode. This holds │ │ │ │ - * some drawbacks, though we get around them below. When interpretting │ │ │ │ - * the keycodes below (including the comments associated with them), │ │ │ │ - * consult the URL below. For instance, the Safari browser returns │ │ │ │ - * "IE keycodes", and so is supported by any keycode labeled "IE". │ │ │ │ - * │ │ │ │ - * Very informative URL: │ │ │ │ - * http://unixpapa.com/js/key.html │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * Method: read_wmc_Abstract │ │ │ │ */ │ │ │ │ - defaultKeyPress: function(evt) { │ │ │ │ - var size, handled = true; │ │ │ │ - │ │ │ │ - var target = OpenLayers.Event.element(evt); │ │ │ │ - if (target && │ │ │ │ - (target.tagName == 'INPUT' || │ │ │ │ - target.tagName == 'TEXTAREA' || │ │ │ │ - target.tagName == 'SELECT')) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - │ │ │ │ - switch (evt.keyCode) { │ │ │ │ - case OpenLayers.Event.KEY_LEFT: │ │ │ │ - this.map.pan(-this.slideFactor, 0); │ │ │ │ - break; │ │ │ │ - case OpenLayers.Event.KEY_RIGHT: │ │ │ │ - this.map.pan(this.slideFactor, 0); │ │ │ │ - break; │ │ │ │ - case OpenLayers.Event.KEY_UP: │ │ │ │ - this.map.pan(0, -this.slideFactor); │ │ │ │ - break; │ │ │ │ - case OpenLayers.Event.KEY_DOWN: │ │ │ │ - this.map.pan(0, this.slideFactor); │ │ │ │ - break; │ │ │ │ - │ │ │ │ - case 33: // Page Up. Same in all browsers. │ │ │ │ - size = this.map.getSize(); │ │ │ │ - this.map.pan(0, -0.75 * size.h); │ │ │ │ - break; │ │ │ │ - case 34: // Page Down. Same in all browsers. │ │ │ │ - size = this.map.getSize(); │ │ │ │ - this.map.pan(0, 0.75 * size.h); │ │ │ │ - break; │ │ │ │ - case 35: // End. Same in all browsers. │ │ │ │ - size = this.map.getSize(); │ │ │ │ - this.map.pan(0.75 * size.w, 0); │ │ │ │ - break; │ │ │ │ - case 36: // Home. Same in all browsers. │ │ │ │ - size = this.map.getSize(); │ │ │ │ - this.map.pan(-0.75 * size.w, 0); │ │ │ │ - break; │ │ │ │ - │ │ │ │ - case 43: // +/= (ASCII), keypad + (ASCII, Opera) │ │ │ │ - case 61: // +/= (Mozilla, Opera, some ASCII) │ │ │ │ - case 187: // +/= (IE) │ │ │ │ - case 107: // keypad + (IE, Mozilla) │ │ │ │ - this.map.zoomIn(); │ │ │ │ - break; │ │ │ │ - case 45: // -/_ (ASCII, Opera), keypad - (ASCII, Opera) │ │ │ │ - case 109: // -/_ (Mozilla), keypad - (Mozilla, IE) │ │ │ │ - case 189: // -/_ (IE) │ │ │ │ - case 95: // -/_ (some ASCII) │ │ │ │ - this.map.zoomOut(); │ │ │ │ - break; │ │ │ │ - default: │ │ │ │ - handled = false; │ │ │ │ - } │ │ │ │ - if (handled) { │ │ │ │ - // prevent browser default not to move the page │ │ │ │ - // when moving the page with the keyboard │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ + read_wmc_Abstract: function(obj, node) { │ │ │ │ + var abst = this.getChildValue(node); │ │ │ │ + if (abst) { │ │ │ │ + obj["abstract"] = abst; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.KeyboardDefaults" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/SelectFeature.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ - * @requires OpenLayers/Handler/Feature.js │ │ │ │ - * @requires OpenLayers/Layer/Vector/RootContainer.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.SelectFeature │ │ │ │ - * The SelectFeature control selects vector features from a given layer on │ │ │ │ - * click or hover. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * beforefeaturehighlighted - Triggered before a feature is highlighted │ │ │ │ - * featurehighlighted - Triggered when a feature is highlighted │ │ │ │ - * featureunhighlighted - Triggered when a feature is unhighlighted │ │ │ │ - * boxselectionstart - Triggered before box selection starts │ │ │ │ - * boxselectionend - Triggered after box selection ends │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: multipleKey │ │ │ │ - * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets │ │ │ │ - * the <multiple> property to true. Default is null. │ │ │ │ - */ │ │ │ │ - multipleKey: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: toggleKey │ │ │ │ - * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets │ │ │ │ - * the <toggle> property to true. Default is null. │ │ │ │ - */ │ │ │ │ - toggleKey: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: multiple │ │ │ │ - * {Boolean} Allow selection of multiple geometries. Default is false. │ │ │ │ - */ │ │ │ │ - multiple: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: clickout │ │ │ │ - * {Boolean} Unselect features when clicking outside any feature. │ │ │ │ - * Default is true. │ │ │ │ - */ │ │ │ │ - clickout: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: toggle │ │ │ │ - * {Boolean} Unselect a selected feature on click. Default is false. Only │ │ │ │ - * has meaning if hover is false. │ │ │ │ - */ │ │ │ │ - toggle: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: hover │ │ │ │ - * {Boolean} Select on mouse over and deselect on mouse out. If true, this │ │ │ │ - * ignores clicks and only listens to mouse moves. │ │ │ │ - */ │ │ │ │ - hover: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: highlightOnly │ │ │ │ - * {Boolean} If true do not actually select features (that is place them in │ │ │ │ - * the layer's selected features array), just highlight them. This property │ │ │ │ - * has no effect if hover is false. Defaults to false. │ │ │ │ - */ │ │ │ │ - highlightOnly: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: box │ │ │ │ - * {Boolean} Allow feature selection by drawing a box. │ │ │ │ - */ │ │ │ │ - box: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: onBeforeSelect │ │ │ │ - * {Function} Optional function to be called before a feature is selected. │ │ │ │ - * The function should expect to be called with a feature. │ │ │ │ - */ │ │ │ │ - onBeforeSelect: function() {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: onSelect │ │ │ │ - * {Function} Optional function to be called when a feature is selected. │ │ │ │ - * The function should expect to be called with a feature. │ │ │ │ - */ │ │ │ │ - onSelect: function() {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: onUnselect │ │ │ │ - * {Function} Optional function to be called when a feature is unselected. │ │ │ │ - * The function should expect to be called with a feature. │ │ │ │ - */ │ │ │ │ - onUnselect: function() {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: scope │ │ │ │ - * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect │ │ │ │ - * callbacks. If null the scope will be this control. │ │ │ │ - */ │ │ │ │ - scope: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: geometryTypes │ │ │ │ - * {Array(String)} To restrict selecting to a limited set of geometry types, │ │ │ │ - * send a list of strings corresponding to the geometry class names. │ │ │ │ - */ │ │ │ │ - geometryTypes: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: layer │ │ │ │ - * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer │ │ │ │ - * root for all layers this control is configured with (if an array of │ │ │ │ - * layers was passed to the constructor), or the vector layer the control │ │ │ │ - * was configured with (if a single layer was passed to the constructor). │ │ │ │ - */ │ │ │ │ - layer: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: layers │ │ │ │ - * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on, │ │ │ │ - * or null if the control was configured with a single layer │ │ │ │ - */ │ │ │ │ - layers: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: callbacks │ │ │ │ - * {Object} The functions that are sent to the handlers.feature for callback │ │ │ │ - */ │ │ │ │ - callbacks: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: selectStyle │ │ │ │ - * {Object} Hash of styles │ │ │ │ - */ │ │ │ │ - selectStyle: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: renderIntent │ │ │ │ - * {String} key used to retrieve the select style from the layer's │ │ │ │ - * style map. │ │ │ │ - */ │ │ │ │ - renderIntent: "select", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: handlers │ │ │ │ - * {Object} Object with references to multiple <OpenLayers.Handler> │ │ │ │ - * instances. │ │ │ │ - */ │ │ │ │ - handlers: null, │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.SelectFeature │ │ │ │ - * Create a new control for selecting features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The │ │ │ │ - * layer(s) this control will select features from. │ │ │ │ - * options - {Object} │ │ │ │ + * Method: read_wmc_LogoURL │ │ │ │ */ │ │ │ │ - initialize: function(layers, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - │ │ │ │ - if (this.scope === null) { │ │ │ │ - this.scope = this; │ │ │ │ - } │ │ │ │ - this.initLayer(layers); │ │ │ │ - var callbacks = { │ │ │ │ - click: this.clickFeature, │ │ │ │ - clickout: this.clickoutFeature │ │ │ │ - }; │ │ │ │ - if (this.hover) { │ │ │ │ - callbacks.over = this.overFeature; │ │ │ │ - callbacks.out = this.outFeature; │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); │ │ │ │ - this.handlers = { │ │ │ │ - feature: new OpenLayers.Handler.Feature( │ │ │ │ - this, this.layer, this.callbacks, { │ │ │ │ - geometryTypes: this.geometryTypes │ │ │ │ - } │ │ │ │ - ) │ │ │ │ + read_wmc_LogoURL: function(context, node) { │ │ │ │ + context.logo = { │ │ │ │ + width: node.getAttribute("width"), │ │ │ │ + height: node.getAttribute("height"), │ │ │ │ + format: node.getAttribute("format"), │ │ │ │ + href: this.getOnlineResource_href(node) │ │ │ │ }; │ │ │ │ - │ │ │ │ - if (this.box) { │ │ │ │ - this.handlers.box = new OpenLayers.Handler.Box( │ │ │ │ - this, { │ │ │ │ - done: this.selectBox │ │ │ │ - }, { │ │ │ │ - boxDivClassName: "olHandlerBoxSelectFeature" │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: initLayer │ │ │ │ - * Assign the layer property. If layers is an array, we need to use │ │ │ │ - * a RootContainer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. │ │ │ │ + * Method: read_wmc_DescriptionURL │ │ │ │ */ │ │ │ │ - initLayer: function(layers) { │ │ │ │ - if (OpenLayers.Util.isArray(layers)) { │ │ │ │ - this.layers = layers; │ │ │ │ - this.layer = new OpenLayers.Layer.Vector.RootContainer( │ │ │ │ - this.id + "_container", { │ │ │ │ - layers: layers │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - this.layer = layers; │ │ │ │ - } │ │ │ │ + read_wmc_DescriptionURL: function(context, node) { │ │ │ │ + context.descriptionURL = this.getOnlineResource_href(node); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ + * Method: read_wmc_ContactInformation │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.active && this.layers) { │ │ │ │ - this.map.removeLayer(this.layer); │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - if (this.layers) { │ │ │ │ - this.layer.destroy(); │ │ │ │ - } │ │ │ │ + read_wmc_ContactInformation: function(obj, node) { │ │ │ │ + var contact = {}; │ │ │ │ + this.runChildNodes(contact, node); │ │ │ │ + obj.contactInformation = contact; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: activate │ │ │ │ - * Activates the control. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The control was effectively activated. │ │ │ │ + * Method: read_wmc_ContactPersonPrimary │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - if (!this.active) { │ │ │ │ - if (this.layers) { │ │ │ │ - this.map.addLayer(this.layer); │ │ │ │ - } │ │ │ │ - this.handlers.feature.activate(); │ │ │ │ - if (this.box && this.handlers.box) { │ │ │ │ - this.handlers.box.activate(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return OpenLayers.Control.prototype.activate.apply( │ │ │ │ - this, arguments │ │ │ │ - ); │ │ │ │ + read_wmc_ContactPersonPrimary: function(contact, node) { │ │ │ │ + var personPrimary = {}; │ │ │ │ + this.runChildNodes(personPrimary, node); │ │ │ │ + contact.personPrimary = personPrimary; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Deactivates the control. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The control was effectively deactivated. │ │ │ │ + * Method: read_wmc_ContactPerson │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.handlers.feature.deactivate(); │ │ │ │ - if (this.handlers.box) { │ │ │ │ - this.handlers.box.deactivate(); │ │ │ │ - } │ │ │ │ - if (this.layers) { │ │ │ │ - this.map.removeLayer(this.layer); │ │ │ │ - } │ │ │ │ + read_wmc_ContactPerson: function(primaryPerson, node) { │ │ │ │ + var person = this.getChildValue(node); │ │ │ │ + if (person) { │ │ │ │ + primaryPerson.person = person; │ │ │ │ } │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply( │ │ │ │ - this, arguments │ │ │ │ - ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: unselectAll │ │ │ │ - * Unselect all selected features. To unselect all except for a single │ │ │ │ - * feature, set the options.except property to the feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional configuration object. │ │ │ │ + * Method: read_wmc_ContactOrganization │ │ │ │ */ │ │ │ │ - unselectAll: function(options) { │ │ │ │ - // we'll want an option to supress notification here │ │ │ │ - var layers = this.layers || [this.layer], │ │ │ │ - layer, feature, l, numExcept; │ │ │ │ - for (l = 0; l < layers.length; ++l) { │ │ │ │ - layer = layers[l]; │ │ │ │ - numExcept = 0; │ │ │ │ - //layer.selectedFeatures is null when layer is destroyed and │ │ │ │ - //one of it's preremovelayer listener calls setLayer │ │ │ │ - //with another layer on this control │ │ │ │ - if (layer.selectedFeatures != null) { │ │ │ │ - while (layer.selectedFeatures.length > numExcept) { │ │ │ │ - feature = layer.selectedFeatures[numExcept]; │ │ │ │ - if (!options || options.except != feature) { │ │ │ │ - this.unselect(feature); │ │ │ │ - } else { │ │ │ │ - ++numExcept; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + read_wmc_ContactOrganization: function(primaryPerson, node) { │ │ │ │ + var organization = this.getChildValue(node); │ │ │ │ + if (organization) { │ │ │ │ + primaryPerson.organization = organization; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clickFeature │ │ │ │ - * Called on click in a feature │ │ │ │ - * Only responds if this.hover is false. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * Method: read_wmc_ContactPosition │ │ │ │ */ │ │ │ │ - clickFeature: function(feature) { │ │ │ │ - if (!this.hover) { │ │ │ │ - var selected = (OpenLayers.Util.indexOf( │ │ │ │ - feature.layer.selectedFeatures, feature) > -1); │ │ │ │ - if (selected) { │ │ │ │ - if (this.toggleSelect()) { │ │ │ │ - this.unselect(feature); │ │ │ │ - } else if (!this.multipleSelect()) { │ │ │ │ - this.unselectAll({ │ │ │ │ - except: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - if (!this.multipleSelect()) { │ │ │ │ - this.unselectAll({ │ │ │ │ - except: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - this.select(feature); │ │ │ │ - } │ │ │ │ + read_wmc_ContactPosition: function(contact, node) { │ │ │ │ + var position = this.getChildValue(node); │ │ │ │ + if (position) { │ │ │ │ + contact.position = position; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: multipleSelect │ │ │ │ - * Allow for multiple selected features based on <multiple> property and │ │ │ │ - * <multipleKey> event modifier. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Allow for multiple selected features. │ │ │ │ - */ │ │ │ │ - multipleSelect: function() { │ │ │ │ - return this.multiple || (this.handlers.feature.evt && │ │ │ │ - this.handlers.feature.evt[this.multipleKey]); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: toggleSelect │ │ │ │ - * Event should toggle the selected state of a feature based on <toggle> │ │ │ │ - * property and <toggleKey> event modifier. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Toggle the selected state of a feature. │ │ │ │ + * Method: read_wmc_ContactAddress │ │ │ │ */ │ │ │ │ - toggleSelect: function() { │ │ │ │ - return this.toggle || (this.handlers.feature.evt && │ │ │ │ - this.handlers.feature.evt[this.toggleKey]); │ │ │ │ + read_wmc_ContactAddress: function(contact, node) { │ │ │ │ + var contactAddress = {}; │ │ │ │ + this.runChildNodes(contactAddress, node); │ │ │ │ + contact.contactAddress = contactAddress; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clickoutFeature │ │ │ │ - * Called on click outside a previously clicked (selected) feature. │ │ │ │ - * Only responds if this.hover is false. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Vector.Feature>} │ │ │ │ + * Method: read_wmc_AddressType │ │ │ │ */ │ │ │ │ - clickoutFeature: function(feature) { │ │ │ │ - if (!this.hover && this.clickout) { │ │ │ │ - this.unselectAll(); │ │ │ │ + read_wmc_AddressType: function(contactAddress, node) { │ │ │ │ + var type = this.getChildValue(node); │ │ │ │ + if (type) { │ │ │ │ + contactAddress.type = type; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: overFeature │ │ │ │ - * Called on over a feature. │ │ │ │ - * Only responds if this.hover is true. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * Method: read_wmc_Address │ │ │ │ */ │ │ │ │ - overFeature: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - if (this.hover) { │ │ │ │ - if (this.highlightOnly) { │ │ │ │ - this.highlight(feature); │ │ │ │ - } else if (OpenLayers.Util.indexOf( │ │ │ │ - layer.selectedFeatures, feature) == -1) { │ │ │ │ - this.select(feature); │ │ │ │ - } │ │ │ │ + read_wmc_Address: function(contactAddress, node) { │ │ │ │ + var address = this.getChildValue(node); │ │ │ │ + if (address) { │ │ │ │ + contactAddress.address = address; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: outFeature │ │ │ │ - * Called on out of a selected feature. │ │ │ │ - * Only responds if this.hover is true. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * Method: read_wmc_City │ │ │ │ */ │ │ │ │ - outFeature: function(feature) { │ │ │ │ - if (this.hover) { │ │ │ │ - if (this.highlightOnly) { │ │ │ │ - // we do nothing if we're not the last highlighter of the │ │ │ │ - // feature │ │ │ │ - if (feature._lastHighlighter == this.id) { │ │ │ │ - // if another select control had highlighted the feature before │ │ │ │ - // we did it ourself then we use that control to highlight the │ │ │ │ - // feature as it was before we highlighted it, else we just │ │ │ │ - // unhighlight it │ │ │ │ - if (feature._prevHighlighter && │ │ │ │ - feature._prevHighlighter != this.id) { │ │ │ │ - delete feature._lastHighlighter; │ │ │ │ - var control = this.map.getControl( │ │ │ │ - feature._prevHighlighter); │ │ │ │ - if (control) { │ │ │ │ - control.highlight(feature); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.unhighlight(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.unselect(feature); │ │ │ │ - } │ │ │ │ + read_wmc_City: function(contactAddress, node) { │ │ │ │ + var city = this.getChildValue(node); │ │ │ │ + if (city) { │ │ │ │ + contactAddress.city = city; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: highlight │ │ │ │ - * Redraw feature with the select style. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * Method: read_wmc_StateOrProvince │ │ │ │ */ │ │ │ │ - highlight: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - var cont = this.events.triggerEvent("beforefeaturehighlighted", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (cont !== false) { │ │ │ │ - feature._prevHighlighter = feature._lastHighlighter; │ │ │ │ - feature._lastHighlighter = this.id; │ │ │ │ - var style = this.selectStyle || this.renderIntent; │ │ │ │ - layer.drawFeature(feature, style); │ │ │ │ - this.events.triggerEvent("featurehighlighted", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ + read_wmc_StateOrProvince: function(contactAddress, node) { │ │ │ │ + var stateOrProvince = this.getChildValue(node); │ │ │ │ + if (stateOrProvince) { │ │ │ │ + contactAddress.stateOrProvince = stateOrProvince; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: unhighlight │ │ │ │ - * Redraw feature with the "default" style │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * Method: read_wmc_PostCode │ │ │ │ */ │ │ │ │ - unhighlight: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - // three cases: │ │ │ │ - // 1. there's no other highlighter, in that case _prev is undefined, │ │ │ │ - // and we just need to undef _last │ │ │ │ - // 2. another control highlighted the feature after we did it, in │ │ │ │ - // that case _last references this other control, and we just │ │ │ │ - // need to undef _prev │ │ │ │ - // 3. another control highlighted the feature before we did it, in │ │ │ │ - // that case _prev references this other control, and we need to │ │ │ │ - // set _last to _prev and undef _prev │ │ │ │ - if (feature._prevHighlighter == undefined) { │ │ │ │ - delete feature._lastHighlighter; │ │ │ │ - } else if (feature._prevHighlighter == this.id) { │ │ │ │ - delete feature._prevHighlighter; │ │ │ │ - } else { │ │ │ │ - feature._lastHighlighter = feature._prevHighlighter; │ │ │ │ - delete feature._prevHighlighter; │ │ │ │ + read_wmc_PostCode: function(contactAddress, node) { │ │ │ │ + var postcode = this.getChildValue(node); │ │ │ │ + if (postcode) { │ │ │ │ + contactAddress.postcode = postcode; │ │ │ │ } │ │ │ │ - layer.drawFeature(feature, feature.style || feature.layer.style || │ │ │ │ - "default"); │ │ │ │ - this.events.triggerEvent("featureunhighlighted", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: select │ │ │ │ - * Add feature to the layer's selectedFeature array, render the feature as │ │ │ │ - * selected, and call the onSelect function. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * Method: read_wmc_Country │ │ │ │ */ │ │ │ │ - select: function(feature) { │ │ │ │ - var cont = this.onBeforeSelect.call(this.scope, feature); │ │ │ │ - var layer = feature.layer; │ │ │ │ - if (cont !== false) { │ │ │ │ - cont = layer.events.triggerEvent("beforefeatureselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (cont !== false) { │ │ │ │ - layer.selectedFeatures.push(feature); │ │ │ │ - this.highlight(feature); │ │ │ │ - // if the feature handler isn't involved in the feature │ │ │ │ - // selection (because the box handler is used or the │ │ │ │ - // feature is selected programatically) we fake the │ │ │ │ - // feature handler to allow unselecting on click │ │ │ │ - if (!this.handlers.feature.lastFeature) { │ │ │ │ - this.handlers.feature.lastFeature = layer.selectedFeatures[0]; │ │ │ │ - } │ │ │ │ - layer.events.triggerEvent("featureselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.onSelect.call(this.scope, feature); │ │ │ │ - } │ │ │ │ + read_wmc_Country: function(contactAddress, node) { │ │ │ │ + var country = this.getChildValue(node); │ │ │ │ + if (country) { │ │ │ │ + contactAddress.country = country; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: unselect │ │ │ │ - * Remove feature from the layer's selectedFeature array, render the feature as │ │ │ │ - * normal, and call the onUnselect function. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - */ │ │ │ │ - unselect: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - // Store feature style for restoration later │ │ │ │ - this.unhighlight(feature); │ │ │ │ - OpenLayers.Util.removeItem(layer.selectedFeatures, feature); │ │ │ │ - layer.events.triggerEvent("featureunselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.onUnselect.call(this.scope, feature); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: selectBox │ │ │ │ - * Callback from the handlers.box set up when <box> selection is true │ │ │ │ - * on. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> } │ │ │ │ - */ │ │ │ │ - selectBox: function(position) { │ │ │ │ - if (position instanceof OpenLayers.Bounds) { │ │ │ │ - var minXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.left, │ │ │ │ - y: position.bottom │ │ │ │ - }); │ │ │ │ - var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.right, │ │ │ │ - y: position.top │ │ │ │ - }); │ │ │ │ - var bounds = new OpenLayers.Bounds( │ │ │ │ - minXY.lon, minXY.lat, maxXY.lon, maxXY.lat │ │ │ │ - ); │ │ │ │ - │ │ │ │ - // if multiple is false, first deselect currently selected features │ │ │ │ - if (!this.multipleSelect()) { │ │ │ │ - this.unselectAll(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // because we're using a box, we consider we want multiple selection │ │ │ │ - var prevMultiple = this.multiple; │ │ │ │ - this.multiple = true; │ │ │ │ - var layers = this.layers || [this.layer]; │ │ │ │ - this.events.triggerEvent("boxselectionstart", { │ │ │ │ - layers: layers │ │ │ │ - }); │ │ │ │ - var layer; │ │ │ │ - for (var l = 0; l < layers.length; ++l) { │ │ │ │ - layer = layers[l]; │ │ │ │ - for (var i = 0, len = layer.features.length; i < len; ++i) { │ │ │ │ - var feature = layer.features[i]; │ │ │ │ - // check if the feature is displayed │ │ │ │ - if (!feature.getVisibility()) { │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.geometryTypes == null || OpenLayers.Util.indexOf( │ │ │ │ - this.geometryTypes, feature.geometry.CLASS_NAME) > -1) { │ │ │ │ - if (bounds.toGeometry().intersects(feature.geometry)) { │ │ │ │ - if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { │ │ │ │ - this.select(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.multiple = prevMultiple; │ │ │ │ - this.events.triggerEvent("boxselectionend", { │ │ │ │ - layers: layers │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the control. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * Method: read_wmc_ContactVoiceTelephone │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - this.handlers.feature.setMap(map); │ │ │ │ - if (this.box) { │ │ │ │ - this.handlers.box.setMap(map); │ │ │ │ + read_wmc_ContactVoiceTelephone: function(contact, node) { │ │ │ │ + var phone = this.getChildValue(node); │ │ │ │ + if (phone) { │ │ │ │ + contact.phone = phone; │ │ │ │ } │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setLayer │ │ │ │ - * Attach a new layer to the control, overriding any existing layers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layers - Array of {<OpenLayers.Layer.Vector>} or a single │ │ │ │ - * {<OpenLayers.Layer.Vector>} │ │ │ │ + * Method: read_wmc_ContactFacsimileTelephone │ │ │ │ */ │ │ │ │ - setLayer: function(layers) { │ │ │ │ - var isActive = this.active; │ │ │ │ - this.unselectAll(); │ │ │ │ - this.deactivate(); │ │ │ │ - if (this.layers) { │ │ │ │ - this.layer.destroy(); │ │ │ │ - this.layers = null; │ │ │ │ - } │ │ │ │ - this.initLayer(layers); │ │ │ │ - this.handlers.feature.layer = this.layer; │ │ │ │ - if (isActive) { │ │ │ │ - this.activate(); │ │ │ │ + read_wmc_ContactFacsimileTelephone: function(contact, node) { │ │ │ │ + var fax = this.getChildValue(node); │ │ │ │ + if (fax) { │ │ │ │ + contact.fax = fax; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.SelectFeature" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/MousePosition.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.MousePosition │ │ │ │ - * The MousePosition control displays geographic coordinates of the mouse │ │ │ │ - * pointer, as it is moved about the map. │ │ │ │ - * │ │ │ │ - * You can use the <prefix>- or <suffix>-properties to provide more information │ │ │ │ - * about the displayed coordinates to the user: │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * var mousePositionCtrl = new OpenLayers.Control.MousePosition({ │ │ │ │ - * prefix: '<a target="_blank" ' + │ │ │ │ - * 'href="http://spatialreference.org/ref/epsg/4326/">' + │ │ │ │ - * 'EPSG:4326</a> coordinates: ' │ │ │ │ - * } │ │ │ │ - * ); │ │ │ │ - * (end code) │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * true. │ │ │ │ - */ │ │ │ │ - autoActivate: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: element │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - element: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: prefix │ │ │ │ - * {String} A string to be prepended to the current pointers coordinates │ │ │ │ - * when it is rendered. Defaults to the empty string ''. │ │ │ │ - */ │ │ │ │ - prefix: '', │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: separator │ │ │ │ - * {String} A string to be used to seperate the two coordinates from each │ │ │ │ - * other. Defaults to the string ', ', which will result in a │ │ │ │ - * rendered coordinate of e.g. '42.12, 21.22'. │ │ │ │ - */ │ │ │ │ - separator: ', ', │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: suffix │ │ │ │ - * {String} A string to be appended to the current pointers coordinates │ │ │ │ - * when it is rendered. Defaults to the empty string ''. │ │ │ │ - */ │ │ │ │ - suffix: '', │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: numDigits │ │ │ │ - * {Integer} The number of digits each coordinate shall have when being │ │ │ │ - * rendered, Defaults to 5. │ │ │ │ - */ │ │ │ │ - numDigits: 5, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: granularity │ │ │ │ - * {Integer} │ │ │ │ - */ │ │ │ │ - granularity: 10, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: emptyString │ │ │ │ - * {String} Set this to some value to set when the mouse is outside the │ │ │ │ - * map. │ │ │ │ - */ │ │ │ │ - emptyString: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: lastXy │ │ │ │ - * {<OpenLayers.Pixel>} │ │ │ │ - */ │ │ │ │ - lastXy: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: displayProjection │ │ │ │ - * {<OpenLayers.Projection>} The projection in which the mouse position is │ │ │ │ - * displayed. │ │ │ │ - */ │ │ │ │ - displayProjection: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.MousePosition │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Options for control. │ │ │ │ - */ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ + * Method: read_wmc_ContactElectronicMailAddress │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + read_wmc_ContactElectronicMailAddress: function(contact, node) { │ │ │ │ + var email = this.getChildValue(node); │ │ │ │ + if (email) { │ │ │ │ + contact.email = email; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: activate │ │ │ │ + * Method: read_wmc_DataURL │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.map.events.register('mousemove', this, this.redraw); │ │ │ │ - this.map.events.register('mouseout', this, this.reset); │ │ │ │ - this.redraw(); │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ + read_wmc_DataURL: function(layerContext, node) { │ │ │ │ + layerContext.dataURL = this.getOnlineResource_href(node); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ + * Method: read_wmc_LegendURL │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.map.events.unregister('mousemove', this, this.redraw); │ │ │ │ - this.map.events.unregister('mouseout', this, this.reset); │ │ │ │ - this.element.innerHTML = ""; │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ + read_wmc_LegendURL: function(style, node) { │ │ │ │ + var legend = { │ │ │ │ + width: node.getAttribute('width'), │ │ │ │ + height: node.getAttribute('height'), │ │ │ │ + format: node.getAttribute('format'), │ │ │ │ + href: this.getOnlineResource_href(node) │ │ │ │ + }; │ │ │ │ + style.legend = legend; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ - * {DOMElement} │ │ │ │ + * Method: read_wmc_DimensionList │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - │ │ │ │ - if (!this.element) { │ │ │ │ - this.div.left = ""; │ │ │ │ - this.div.top = ""; │ │ │ │ - this.element = this.div; │ │ │ │ - } │ │ │ │ - │ │ │ │ - return this.div; │ │ │ │ + read_wmc_DimensionList: function(layerContext, node) { │ │ │ │ + layerContext.dimensions = {}; │ │ │ │ + this.runChildNodes(layerContext.dimensions, node); │ │ │ │ }, │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: redraw │ │ │ │ + * Method: read_wmc_Dimension │ │ │ │ */ │ │ │ │ - redraw: function(evt) { │ │ │ │ - │ │ │ │ - var lonLat; │ │ │ │ - │ │ │ │ - if (evt == null) { │ │ │ │ - this.reset(); │ │ │ │ - return; │ │ │ │ - } else { │ │ │ │ - if (this.lastXy == null || │ │ │ │ - Math.abs(evt.xy.x - this.lastXy.x) > this.granularity || │ │ │ │ - Math.abs(evt.xy.y - this.lastXy.y) > this.granularity) { │ │ │ │ - this.lastXy = evt.xy; │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - │ │ │ │ - lonLat = this.map.getLonLatFromPixel(evt.xy); │ │ │ │ - if (!lonLat) { │ │ │ │ - // map has not yet been properly initialized │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (this.displayProjection) { │ │ │ │ - lonLat.transform(this.map.getProjectionObject(), │ │ │ │ - this.displayProjection); │ │ │ │ - } │ │ │ │ - this.lastXy = evt.xy; │ │ │ │ - │ │ │ │ - } │ │ │ │ - │ │ │ │ - var newHtml = this.formatOutput(lonLat); │ │ │ │ + read_wmc_Dimension: function(dimensions, node) { │ │ │ │ + var name = node.getAttribute("name").toLowerCase(); │ │ │ │ │ │ │ │ - if (newHtml != this.element.innerHTML) { │ │ │ │ - this.element.innerHTML = newHtml; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + var dim = { │ │ │ │ + name: name, │ │ │ │ + units: node.getAttribute("units") || "", │ │ │ │ + unitSymbol: node.getAttribute("unitSymbol") || "", │ │ │ │ + userValue: node.getAttribute("userValue") || "", │ │ │ │ + nearestValue: node.getAttribute("nearestValue") === "1", │ │ │ │ + multipleValues: node.getAttribute("multipleValues") === "1", │ │ │ │ + current: node.getAttribute("current") === "1", │ │ │ │ + "default": node.getAttribute("default") || "" │ │ │ │ + }; │ │ │ │ + var values = this.getChildValue(node); │ │ │ │ + dim.values = values.split(","); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: reset │ │ │ │ - */ │ │ │ │ - reset: function(evt) { │ │ │ │ - if (this.emptyString != null) { │ │ │ │ - this.element.innerHTML = this.emptyString; │ │ │ │ - } │ │ │ │ + dimensions[dim.name] = dim; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: formatOutput │ │ │ │ - * Override to provide custom display output │ │ │ │ + * Method: write │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * lonLat - {<OpenLayers.LonLat>} Location to display │ │ │ │ - */ │ │ │ │ - formatOutput: function(lonLat) { │ │ │ │ - var digits = parseInt(this.numDigits); │ │ │ │ - var newHtml = │ │ │ │ - this.prefix + │ │ │ │ - lonLat.lon.toFixed(digits) + │ │ │ │ - this.separator + │ │ │ │ - lonLat.lat.toFixed(digits) + │ │ │ │ - this.suffix; │ │ │ │ - return newHtml; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Control.MousePosition" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/TouchNavigation.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control/DragPan.js │ │ │ │ - * @requires OpenLayers/Control/PinchZoom.js │ │ │ │ - * @requires OpenLayers/Handler/Click.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.TouchNavigation │ │ │ │ - * The navigation control handles map browsing with touch events (dragging, │ │ │ │ - * double-tapping, tap with two fingers, and pinch zoom). Create a new │ │ │ │ - * control with the <OpenLayers.Control.TouchNavigation> constructor. │ │ │ │ - * │ │ │ │ - * If you’re only targeting touch enabled devices with your mapping application, │ │ │ │ - * you can create a map with only a TouchNavigation control. The │ │ │ │ - * <OpenLayers.Control.Navigation> control is mobile ready by default, but │ │ │ │ - * you can generate a smaller build of the library by only including this │ │ │ │ - * touch navigation control if you aren't concerned about mouse interaction. │ │ │ │ - * │ │ │ │ - * Inherits: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: dragPan │ │ │ │ - * {<OpenLayers.Control.DragPan>} │ │ │ │ - */ │ │ │ │ - dragPan: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: dragPanOptions │ │ │ │ - * {Object} Options passed to the DragPan control. │ │ │ │ - */ │ │ │ │ - dragPanOptions: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: pinchZoom │ │ │ │ - * {<OpenLayers.Control.PinchZoom>} │ │ │ │ + * context - {Object} An object representing the map context. │ │ │ │ + * options - {Object} Optional object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A WMC document string. │ │ │ │ */ │ │ │ │ - pinchZoom: null, │ │ │ │ + write: function(context, options) { │ │ │ │ + var root = this.createElementDefaultNS("ViewContext"); │ │ │ │ + this.setAttributes(root, { │ │ │ │ + version: this.VERSION, │ │ │ │ + id: (options && typeof options.id == "string") ? │ │ │ │ + options.id : OpenLayers.Util.createUniqueID("OpenLayers_Context_") │ │ │ │ + }); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: pinchZoomOptions │ │ │ │ - * {Object} Options passed to the PinchZoom control. │ │ │ │ - */ │ │ │ │ - pinchZoomOptions: null, │ │ │ │ + // add schemaLocation attribute │ │ │ │ + this.setAttributeNS( │ │ │ │ + root, this.namespaces.xsi, │ │ │ │ + "xsi:schemaLocation", this.schemaLocation │ │ │ │ + ); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: clickHandlerOptions │ │ │ │ - * {Object} Options passed to the Click handler. │ │ │ │ - */ │ │ │ │ - clickHandlerOptions: null, │ │ │ │ + // required General element │ │ │ │ + root.appendChild(this.write_wmc_General(context)); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: documentDrag │ │ │ │ - * {Boolean} Allow panning of the map by dragging outside map viewport. │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - documentDrag: false, │ │ │ │ + // required LayerList element │ │ │ │ + root.appendChild(this.write_wmc_LayerList(context)); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * true. │ │ │ │ - */ │ │ │ │ - autoActivate: true, │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [root]); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.TouchNavigation │ │ │ │ - * Create a new navigation control │ │ │ │ + * Method: createElementDefaultNS │ │ │ │ + * Shorthand for createElementNS with namespace from <defaultPrefix>. │ │ │ │ + * Can optionally be used to set attributes and a text child value. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * the control │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - this.handlers = {}; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * The destroy method is used to perform any clean up before the control │ │ │ │ - * is dereferenced. Typically this is where event listeners are removed │ │ │ │ - * to prevent memory leaks. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - if (this.dragPan) { │ │ │ │ - this.dragPan.destroy(); │ │ │ │ - } │ │ │ │ - this.dragPan = null; │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.destroy(); │ │ │ │ - delete this.pinchZoom; │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: activate │ │ │ │ + * name - {String} The qualified node name. │ │ │ │ + * childValue - {String} Optional value for text child node. │ │ │ │ + * attributes - {Object} Optional object representing attributes. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Element} An element node. │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.dragPan.activate(); │ │ │ │ - this.handlers.click.activate(); │ │ │ │ - this.pinchZoom.activate(); │ │ │ │ - return true; │ │ │ │ + createElementDefaultNS: function(name, childValue, attributes) { │ │ │ │ + var node = this.createElementNS( │ │ │ │ + this.namespaces[this.defaultPrefix], │ │ │ │ + name │ │ │ │ + ); │ │ │ │ + if (childValue) { │ │ │ │ + node.appendChild(this.createTextNode(childValue)); │ │ │ │ } │ │ │ │ - return false; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: deactivate │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.dragPan.deactivate(); │ │ │ │ - this.handlers.click.deactivate(); │ │ │ │ - this.pinchZoom.deactivate(); │ │ │ │ - return true; │ │ │ │ + if (attributes) { │ │ │ │ + this.setAttributes(node, attributes); │ │ │ │ } │ │ │ │ - return false; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ - */ │ │ │ │ - draw: function() { │ │ │ │ - var clickCallbacks = { │ │ │ │ - click: this.defaultClick, │ │ │ │ - dblclick: this.defaultDblClick │ │ │ │ - }; │ │ │ │ - var clickOptions = OpenLayers.Util.extend({ │ │ │ │ - "double": true, │ │ │ │ - stopDouble: true, │ │ │ │ - pixelTolerance: 2 │ │ │ │ - }, this.clickHandlerOptions); │ │ │ │ - this.handlers.click = new OpenLayers.Handler.Click( │ │ │ │ - this, clickCallbacks, clickOptions │ │ │ │ - ); │ │ │ │ - this.dragPan = new OpenLayers.Control.DragPan( │ │ │ │ - OpenLayers.Util.extend({ │ │ │ │ - map: this.map, │ │ │ │ - documentDrag: this.documentDrag │ │ │ │ - }, this.dragPanOptions) │ │ │ │ - ); │ │ │ │ - this.dragPan.draw(); │ │ │ │ - this.pinchZoom = new OpenLayers.Control.PinchZoom( │ │ │ │ - OpenLayers.Util.extend({ │ │ │ │ - map: this.map │ │ │ │ - }, this.pinchZoomOptions) │ │ │ │ - ); │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: defaultClick │ │ │ │ + * Method: setAttributes │ │ │ │ + * Set multiple attributes given key value pairs from an object. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * node - {Element} An element node. │ │ │ │ + * obj - {Object} An object whose properties represent attribute names and │ │ │ │ + * values represent attribute values. │ │ │ │ */ │ │ │ │ - defaultClick: function(evt) { │ │ │ │ - if (evt.lastTouches && evt.lastTouches.length == 2) { │ │ │ │ - this.map.zoomOut(); │ │ │ │ + setAttributes: function(node, obj) { │ │ │ │ + var value; │ │ │ │ + for (var name in obj) { │ │ │ │ + value = obj[name].toString(); │ │ │ │ + if (value.match(/[A-Z]/)) { │ │ │ │ + // safari lowercases attributes with setAttribute │ │ │ │ + this.setAttributeNS(node, null, name, value); │ │ │ │ + } else { │ │ │ │ + node.setAttribute(name, value); │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: defaultDblClick │ │ │ │ + * Method: write_wmc_General │ │ │ │ + * Create a General node given an context object. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * context - {Object} Context object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Element} A WMC General element node. │ │ │ │ */ │ │ │ │ - defaultDblClick: function(evt) { │ │ │ │ - this.map.zoomTo(this.map.zoom + 1, evt.xy); │ │ │ │ - }, │ │ │ │ + write_wmc_General: function(context) { │ │ │ │ + var node = this.createElementDefaultNS("General"); │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.TouchNavigation" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Strategy/Cluster.js │ │ │ │ - ====================================================================== */ │ │ │ │ + // optional Window element │ │ │ │ + if (context.size) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "Window", null, { │ │ │ │ + width: context.size.w, │ │ │ │ + height: context.size.h │ │ │ │ + } │ │ │ │ + )); │ │ │ │ + } │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + // required BoundingBox element │ │ │ │ + var bounds = context.bounds; │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "BoundingBox", null, { │ │ │ │ + minx: bounds.left.toPrecision(18), │ │ │ │ + miny: bounds.bottom.toPrecision(18), │ │ │ │ + maxx: bounds.right.toPrecision(18), │ │ │ │ + maxy: bounds.top.toPrecision(18), │ │ │ │ + SRS: context.projection │ │ │ │ + } │ │ │ │ + )); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Strategy.js │ │ │ │ - */ │ │ │ │ + // required Title element │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "Title", context.title │ │ │ │ + )); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Strategy.Cluster │ │ │ │ - * Strategy for vector feature clustering. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Strategy> │ │ │ │ - */ │ │ │ │ -OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ + // optional KeywordList element │ │ │ │ + if (context.keywords) { │ │ │ │ + node.appendChild(this.write_wmc_KeywordList(context.keywords)); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: distance │ │ │ │ - * {Integer} Pixel distance between features that should be considered a │ │ │ │ - * single cluster. Default is 20 pixels. │ │ │ │ - */ │ │ │ │ - distance: 20, │ │ │ │ + // optional Abstract element │ │ │ │ + if (context["abstract"]) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "Abstract", context["abstract"] │ │ │ │ + )); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: threshold │ │ │ │ - * {Integer} Optional threshold below which original features will be │ │ │ │ - * added to the layer instead of clusters. For example, a threshold │ │ │ │ - * of 3 would mean that any time there are 2 or fewer features in │ │ │ │ - * a cluster, those features will be added directly to the layer instead │ │ │ │ - * of a cluster representing those features. Default is null (which is │ │ │ │ - * equivalent to 1 - meaning that clusters may contain just one feature). │ │ │ │ - */ │ │ │ │ - threshold: null, │ │ │ │ + // Optional LogoURL element │ │ │ │ + if (context.logo) { │ │ │ │ + node.appendChild(this.write_wmc_URLType("LogoURL", context.logo.href, context.logo)); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: features │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} Cached features. │ │ │ │ - */ │ │ │ │ - features: null, │ │ │ │ + // Optional DescriptionURL element │ │ │ │ + if (context.descriptionURL) { │ │ │ │ + node.appendChild(this.write_wmc_URLType("DescriptionURL", context.descriptionURL)); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: clusters │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} Calculated clusters. │ │ │ │ - */ │ │ │ │ - clusters: null, │ │ │ │ + // Optional ContactInformation element │ │ │ │ + if (context.contactInformation) { │ │ │ │ + node.appendChild(this.write_wmc_ContactInformation(context.contactInformation)); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: clustering │ │ │ │ - * {Boolean} The strategy is currently clustering features. │ │ │ │ - */ │ │ │ │ - clustering: false, │ │ │ │ + // OpenLayers specific map properties │ │ │ │ + node.appendChild(this.write_ol_MapExtension(context)); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: resolution │ │ │ │ - * {Float} The resolution (map units per pixel) of the current cluster set. │ │ │ │ - */ │ │ │ │ - resolution: null, │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Strategy.Cluster │ │ │ │ - * Create a new clustering strategy. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * Method: write_wmc_KeywordList │ │ │ │ */ │ │ │ │ + write_wmc_KeywordList: function(keywords) { │ │ │ │ + var node = this.createElementDefaultNS("KeywordList"); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The strategy was successfully activated. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - this.layer.events.on({ │ │ │ │ - "beforefeaturesadded": this.cacheFeatures, │ │ │ │ - "featuresremoved": this.clearCache, │ │ │ │ - "moveend": this.cluster, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + for (var i = 0, len = keywords.length; i < len; i++) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "Keyword", keywords[i] │ │ │ │ + )); │ │ │ │ } │ │ │ │ - return activated; │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ - * tear-down. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The strategy was successfully deactivated. │ │ │ │ + * Method: write_wmc_ContactInformation │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - this.clearCache(); │ │ │ │ - this.layer.events.un({ │ │ │ │ - "beforefeaturesadded": this.cacheFeatures, │ │ │ │ - "featuresremoved": this.clearCache, │ │ │ │ - "moveend": this.cluster, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - return deactivated; │ │ │ │ - }, │ │ │ │ + write_wmc_ContactInformation: function(contact) { │ │ │ │ + var node = this.createElementDefaultNS("ContactInformation"); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: cacheFeatures │ │ │ │ - * Cache features before they are added to the layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * event - {Object} The event that this was listening for. This will come │ │ │ │ - * with a batch of features to be clustered. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} False to stop features from being added to the layer. │ │ │ │ - */ │ │ │ │ - cacheFeatures: function(event) { │ │ │ │ - var propagate = true; │ │ │ │ - if (!this.clustering) { │ │ │ │ - this.clearCache(); │ │ │ │ - this.features = event.features; │ │ │ │ - this.cluster(); │ │ │ │ - propagate = false; │ │ │ │ + if (contact.personPrimary) { │ │ │ │ + node.appendChild(this.write_wmc_ContactPersonPrimary(contact.personPrimary)); │ │ │ │ } │ │ │ │ - return propagate; │ │ │ │ + if (contact.position) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "ContactPosition", contact.position │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + if (contact.contactAddress) { │ │ │ │ + node.appendChild(this.write_wmc_ContactAddress(contact.contactAddress)); │ │ │ │ + } │ │ │ │ + if (contact.phone) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "ContactVoiceTelephone", contact.phone │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + if (contact.fax) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "ContactFacsimileTelephone", contact.fax │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + if (contact.email) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "ContactElectronicMailAddress", contact.email │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clearCache │ │ │ │ - * Clear out the cached features. │ │ │ │ + * Method: write_wmc_ContactPersonPrimary │ │ │ │ */ │ │ │ │ - clearCache: function() { │ │ │ │ - if (!this.clustering) { │ │ │ │ - this.features = null; │ │ │ │ + write_wmc_ContactPersonPrimary: function(personPrimary) { │ │ │ │ + var node = this.createElementDefaultNS("ContactPersonPrimary"); │ │ │ │ + if (personPrimary.person) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "ContactPerson", personPrimary.person │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + if (personPrimary.organization) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "ContactOrganization", personPrimary.organization │ │ │ │ + )); │ │ │ │ } │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: cluster │ │ │ │ - * Cluster features based on some threshold distance. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * event - {Object} The event received when cluster is called as a │ │ │ │ - * result of a moveend event. │ │ │ │ + * Method: write_wmc_ContactAddress │ │ │ │ */ │ │ │ │ - cluster: function(event) { │ │ │ │ - if ((!event || event.zoomChanged) && this.features) { │ │ │ │ - var resolution = this.layer.map.getResolution(); │ │ │ │ - if (resolution != this.resolution || !this.clustersExist()) { │ │ │ │ - this.resolution = resolution; │ │ │ │ - var clusters = []; │ │ │ │ - var feature, clustered, cluster; │ │ │ │ - for (var i = 0; i < this.features.length; ++i) { │ │ │ │ - feature = this.features[i]; │ │ │ │ - if (feature.geometry) { │ │ │ │ - clustered = false; │ │ │ │ - for (var j = clusters.length - 1; j >= 0; --j) { │ │ │ │ - cluster = clusters[j]; │ │ │ │ - if (this.shouldCluster(cluster, feature)) { │ │ │ │ - this.addToCluster(cluster, feature); │ │ │ │ - clustered = true; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (!clustered) { │ │ │ │ - clusters.push(this.createCluster(this.features[i])); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.clustering = true; │ │ │ │ - this.layer.removeAllFeatures(); │ │ │ │ - this.clustering = false; │ │ │ │ - if (clusters.length > 0) { │ │ │ │ - if (this.threshold > 1) { │ │ │ │ - var clone = clusters.slice(); │ │ │ │ - clusters = []; │ │ │ │ - var candidate; │ │ │ │ - for (var i = 0, len = clone.length; i < len; ++i) { │ │ │ │ - candidate = clone[i]; │ │ │ │ - if (candidate.attributes.count < this.threshold) { │ │ │ │ - Array.prototype.push.apply(clusters, candidate.cluster); │ │ │ │ - } else { │ │ │ │ - clusters.push(candidate); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.clustering = true; │ │ │ │ - // A legitimate feature addition could occur during this │ │ │ │ - // addFeatures call. For clustering to behave well, features │ │ │ │ - // should be removed from a layer before requesting a new batch. │ │ │ │ - this.layer.addFeatures(clusters); │ │ │ │ - this.clustering = false; │ │ │ │ - } │ │ │ │ - this.clusters = clusters; │ │ │ │ - } │ │ │ │ + write_wmc_ContactAddress: function(contactAddress) { │ │ │ │ + var node = this.createElementDefaultNS("ContactAddress"); │ │ │ │ + if (contactAddress.type) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "AddressType", contactAddress.type │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + if (contactAddress.address) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "Address", contactAddress.address │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + if (contactAddress.city) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "City", contactAddress.city │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + if (contactAddress.stateOrProvince) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "StateOrProvince", contactAddress.stateOrProvince │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + if (contactAddress.postcode) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "PostCode", contactAddress.postcode │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + if (contactAddress.country) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "Country", contactAddress.country │ │ │ │ + )); │ │ │ │ } │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clustersExist │ │ │ │ - * Determine whether calculated clusters are already on the layer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The calculated clusters are already on the layer. │ │ │ │ + * Method: write_ol_MapExtension │ │ │ │ */ │ │ │ │ - clustersExist: function() { │ │ │ │ - var exist = false; │ │ │ │ - if (this.clusters && this.clusters.length > 0 && │ │ │ │ - this.clusters.length == this.layer.features.length) { │ │ │ │ - exist = true; │ │ │ │ - for (var i = 0; i < this.clusters.length; ++i) { │ │ │ │ - if (this.clusters[i] != this.layer.features[i]) { │ │ │ │ - exist = false; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + write_ol_MapExtension: function(context) { │ │ │ │ + var node = this.createElementDefaultNS("Extension"); │ │ │ │ + │ │ │ │ + var bounds = context.maxExtent; │ │ │ │ + if (bounds) { │ │ │ │ + var maxExtent = this.createElementNS( │ │ │ │ + this.namespaces.ol, "ol:maxExtent" │ │ │ │ + ); │ │ │ │ + this.setAttributes(maxExtent, { │ │ │ │ + minx: bounds.left.toPrecision(18), │ │ │ │ + miny: bounds.bottom.toPrecision(18), │ │ │ │ + maxx: bounds.right.toPrecision(18), │ │ │ │ + maxy: bounds.top.toPrecision(18) │ │ │ │ + }); │ │ │ │ + node.appendChild(maxExtent); │ │ │ │ } │ │ │ │ - return exist; │ │ │ │ + │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: shouldCluster │ │ │ │ - * Determine whether to include a feature in a given cluster. │ │ │ │ + * Method: write_wmc_LayerList │ │ │ │ + * Create a LayerList node given an context object. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * cluster - {<OpenLayers.Feature.Vector>} A cluster. │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} A feature. │ │ │ │ + * context - {Object} Context object. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The feature should be included in the cluster. │ │ │ │ + * {Element} A WMC LayerList element node. │ │ │ │ */ │ │ │ │ - shouldCluster: function(cluster, feature) { │ │ │ │ - var cc = cluster.geometry.getBounds().getCenterLonLat(); │ │ │ │ - var fc = feature.geometry.getBounds().getCenterLonLat(); │ │ │ │ - var distance = ( │ │ │ │ - Math.sqrt( │ │ │ │ - Math.pow((cc.lon - fc.lon), 2) + Math.pow((cc.lat - fc.lat), 2) │ │ │ │ - ) / this.resolution │ │ │ │ - ); │ │ │ │ - return (distance <= this.distance); │ │ │ │ - }, │ │ │ │ + write_wmc_LayerList: function(context) { │ │ │ │ + var list = this.createElementDefaultNS("LayerList"); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: addToCluster │ │ │ │ - * Add a feature to a cluster. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * cluster - {<OpenLayers.Feature.Vector>} A cluster. │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} A feature. │ │ │ │ - */ │ │ │ │ - addToCluster: function(cluster, feature) { │ │ │ │ - cluster.cluster.push(feature); │ │ │ │ - cluster.attributes.count += 1; │ │ │ │ + for (var i = 0, len = context.layersContext.length; i < len; ++i) { │ │ │ │ + list.appendChild(this.write_wmc_Layer(context.layersContext[i])); │ │ │ │ + } │ │ │ │ + │ │ │ │ + return list; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createCluster │ │ │ │ - * Given a feature, create a cluster. │ │ │ │ + * Method: write_wmc_Layer │ │ │ │ + * Create a Layer node given a layer context object. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * context - {Object} A layer context object.} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A cluster. │ │ │ │ + * {Element} A WMC Layer element node. │ │ │ │ */ │ │ │ │ - createCluster: function(feature) { │ │ │ │ - var center = feature.geometry.getBounds().getCenterLonLat(); │ │ │ │ - var cluster = new OpenLayers.Feature.Vector( │ │ │ │ - new OpenLayers.Geometry.Point(center.lon, center.lat), { │ │ │ │ - count: 1 │ │ │ │ + write_wmc_Layer: function(context) { │ │ │ │ + var node = this.createElementDefaultNS( │ │ │ │ + "Layer", null, { │ │ │ │ + queryable: context.queryable ? "1" : "0", │ │ │ │ + hidden: context.visibility ? "0" : "1" │ │ │ │ } │ │ │ │ ); │ │ │ │ - cluster.cluster = [feature]; │ │ │ │ - return cluster; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy.Cluster" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Strategy/BBOX.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Strategy.js │ │ │ │ - * @requires OpenLayers/Filter/Spatial.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Strategy.BBOX │ │ │ │ - * A simple strategy that reads new features when the viewport invalidates │ │ │ │ - * some bounds. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Strategy> │ │ │ │ - */ │ │ │ │ -OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: bounds │ │ │ │ - * {<OpenLayers.Bounds>} The current data bounds (in the same projection │ │ │ │ - * as the layer - not always the same projection as the map). │ │ │ │ - */ │ │ │ │ - bounds: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: resolution │ │ │ │ - * {Float} The current data resolution. │ │ │ │ - */ │ │ │ │ - resolution: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: ratio │ │ │ │ - * {Float} The ratio of the data bounds to the viewport bounds (in each │ │ │ │ - * dimension). Default is 2. │ │ │ │ - */ │ │ │ │ - ratio: 2, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: resFactor │ │ │ │ - * {Float} Optional factor used to determine when previously requested │ │ │ │ - * features are invalid. If set, the resFactor will be compared to the │ │ │ │ - * resolution of the previous request to the current map resolution. │ │ │ │ - * If resFactor > (old / new) and 1/resFactor < (old / new). If you │ │ │ │ - * set a resFactor of 1, data will be requested every time the │ │ │ │ - * resolution changes. If you set a resFactor of 3, data will be │ │ │ │ - * requested if the old resolution is 3 times the new, or if the new is │ │ │ │ - * 3 times the old. If the old bounds do not contain the new bounds │ │ │ │ - * new data will always be requested (with or without considering │ │ │ │ - * resFactor). │ │ │ │ - */ │ │ │ │ - resFactor: null, │ │ │ │ + // required Server element │ │ │ │ + node.appendChild(this.write_wmc_Server(context)); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: response │ │ │ │ - * {<OpenLayers.Protocol.Response>} The protocol response object returned │ │ │ │ - * by the layer protocol. │ │ │ │ - */ │ │ │ │ - response: null, │ │ │ │ + // required Name element │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "Name", context.name │ │ │ │ + )); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Strategy.BBOX │ │ │ │ - * Create a new BBOX strategy. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - */ │ │ │ │ + // required Title element │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "Title", context.title │ │ │ │ + )); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: activate │ │ │ │ - * Set up strategy with regard to reading new batches of remote data. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The strategy was successfully activated. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - this.layer.events.on({ │ │ │ │ - "moveend": this.update, │ │ │ │ - "refresh": this.update, │ │ │ │ - "visibilitychanged": this.update, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.update(); │ │ │ │ + // optional Abstract element │ │ │ │ + if (context["abstract"]) { │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "Abstract", context["abstract"] │ │ │ │ + )); │ │ │ │ } │ │ │ │ - return activated; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Tear down strategy with regard to reading new batches of remote data. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The strategy was successfully deactivated. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - this.layer.events.un({ │ │ │ │ - "moveend": this.update, │ │ │ │ - "refresh": this.update, │ │ │ │ - "visibilitychanged": this.update, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + // optional DataURL element │ │ │ │ + if (context.dataURL) { │ │ │ │ + node.appendChild(this.write_wmc_URLType("DataURL", context.dataURL)); │ │ │ │ } │ │ │ │ - return deactivated; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: update │ │ │ │ - * Callback function called on "moveend" or "refresh" layer events. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will determine │ │ │ │ - * the behaviour of this Strategy │ │ │ │ - * │ │ │ │ - * Valid options include: │ │ │ │ - * force - {Boolean} if true, new data must be unconditionally read. │ │ │ │ - * noAbort - {Boolean} if true, do not abort previous requests. │ │ │ │ - */ │ │ │ │ - update: function(options) { │ │ │ │ - var mapBounds = this.getMapBounds(); │ │ │ │ - if (mapBounds !== null && ((options && options.force) || │ │ │ │ - (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) { │ │ │ │ - this.calculateBounds(mapBounds); │ │ │ │ - this.resolution = this.layer.map.getResolution(); │ │ │ │ - this.triggerRead(options); │ │ │ │ + // optional MetadataURL element │ │ │ │ + if (context.metadataURL) { │ │ │ │ + node.appendChild(this.write_wmc_URLType("MetadataURL", context.metadataURL)); │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getMapBounds │ │ │ │ - * Get the map bounds expressed in the same projection as this layer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} Map bounds in the projection of the layer. │ │ │ │ - */ │ │ │ │ - getMapBounds: function() { │ │ │ │ - if (this.layer.map === null) { │ │ │ │ - return null; │ │ │ │ - } │ │ │ │ - var bounds = this.layer.map.getExtent(); │ │ │ │ - if (bounds && !this.layer.projection.equals( │ │ │ │ - this.layer.map.getProjectionObject())) { │ │ │ │ - bounds = bounds.clone().transform( │ │ │ │ - this.layer.map.getProjectionObject(), this.layer.projection │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - return bounds; │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: invalidBounds │ │ │ │ - * Determine whether the previously requested set of features is invalid. │ │ │ │ - * This occurs when the new map bounds do not contain the previously │ │ │ │ - * requested bounds. In addition, if <resFactor> is set, it will be │ │ │ │ - * considered. │ │ │ │ + * Method: write_wmc_LayerExtension │ │ │ │ + * Add OpenLayers specific layer parameters to an Extension element. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be │ │ │ │ - * retrieved from the map object if not provided │ │ │ │ + * context - {Object} A layer context object. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} │ │ │ │ + * {Element} A WMC Extension element (for a layer). │ │ │ │ */ │ │ │ │ - invalidBounds: function(mapBounds) { │ │ │ │ - if (!mapBounds) { │ │ │ │ - mapBounds = this.getMapBounds(); │ │ │ │ - } │ │ │ │ - var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds); │ │ │ │ - if (!invalid && this.resFactor) { │ │ │ │ - var ratio = this.resolution / this.layer.map.getResolution(); │ │ │ │ - invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor)); │ │ │ │ - } │ │ │ │ - return invalid; │ │ │ │ - }, │ │ │ │ + write_wmc_LayerExtension: function(context) { │ │ │ │ + var node = this.createElementDefaultNS("Extension"); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: calculateBounds │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be │ │ │ │ - * retrieved from the map object if not provided │ │ │ │ - */ │ │ │ │ - calculateBounds: function(mapBounds) { │ │ │ │ - if (!mapBounds) { │ │ │ │ - mapBounds = this.getMapBounds(); │ │ │ │ - } │ │ │ │ - var center = mapBounds.getCenterLonLat(); │ │ │ │ - var dataWidth = mapBounds.getWidth() * this.ratio; │ │ │ │ - var dataHeight = mapBounds.getHeight() * this.ratio; │ │ │ │ - this.bounds = new OpenLayers.Bounds( │ │ │ │ - center.lon - (dataWidth / 2), │ │ │ │ - center.lat - (dataHeight / 2), │ │ │ │ - center.lon + (dataWidth / 2), │ │ │ │ - center.lat + (dataHeight / 2) │ │ │ │ + var bounds = context.maxExtent; │ │ │ │ + var maxExtent = this.createElementNS( │ │ │ │ + this.namespaces.ol, "ol:maxExtent" │ │ │ │ ); │ │ │ │ - }, │ │ │ │ + this.setAttributes(maxExtent, { │ │ │ │ + minx: bounds.left.toPrecision(18), │ │ │ │ + miny: bounds.bottom.toPrecision(18), │ │ │ │ + maxx: bounds.right.toPrecision(18), │ │ │ │ + maxy: bounds.top.toPrecision(18) │ │ │ │ + }); │ │ │ │ + node.appendChild(maxExtent); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: triggerRead │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Additional options for the protocol's read method │ │ │ │ - * (optional) │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} The protocol response object │ │ │ │ - * returned by the layer protocol. │ │ │ │ - */ │ │ │ │ - triggerRead: function(options) { │ │ │ │ - if (this.response && !(options && options.noAbort === true)) { │ │ │ │ - this.layer.protocol.abort(this.response); │ │ │ │ - this.layer.events.triggerEvent("loadend"); │ │ │ │ + if (context.tileSize && !context.singleTile) { │ │ │ │ + var size = this.createElementNS( │ │ │ │ + this.namespaces.ol, "ol:tileSize" │ │ │ │ + ); │ │ │ │ + this.setAttributes(size, context.tileSize); │ │ │ │ + node.appendChild(size); │ │ │ │ } │ │ │ │ - var evt = { │ │ │ │ - filter: this.createFilter() │ │ │ │ - }; │ │ │ │ - this.layer.events.triggerEvent("loadstart", evt); │ │ │ │ - this.response = this.layer.protocol.read( │ │ │ │ - OpenLayers.Util.applyDefaults({ │ │ │ │ - filter: evt.filter, │ │ │ │ - callback: this.merge, │ │ │ │ - scope: this │ │ │ │ - }, options)); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: createFilter │ │ │ │ - * Creates a spatial BBOX filter. If the layer that this strategy belongs │ │ │ │ - * to has a filter property, this filter will be combined with the BBOX │ │ │ │ - * filter. │ │ │ │ - * │ │ │ │ - * Returns │ │ │ │ - * {<OpenLayers.Filter>} The filter object. │ │ │ │ - */ │ │ │ │ - createFilter: function() { │ │ │ │ - var filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ - value: this.bounds, │ │ │ │ - projection: this.layer.projection │ │ │ │ - }); │ │ │ │ - if (this.layer.filter) { │ │ │ │ - filter = new OpenLayers.Filter.Logical({ │ │ │ │ - type: OpenLayers.Filter.Logical.AND, │ │ │ │ - filters: [this.layer.filter, filter] │ │ │ │ - }); │ │ │ │ + var properties = [ │ │ │ │ + "transparent", "numZoomLevels", "units", "isBaseLayer", │ │ │ │ + "opacity", "displayInLayerSwitcher", "singleTile" │ │ │ │ + ]; │ │ │ │ + var child; │ │ │ │ + for (var i = 0, len = properties.length; i < len; ++i) { │ │ │ │ + child = this.createOLPropertyNode(context, properties[i]); │ │ │ │ + if (child) { │ │ │ │ + node.appendChild(child); │ │ │ │ + } │ │ │ │ } │ │ │ │ - return filter; │ │ │ │ + │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: merge │ │ │ │ - * Given a list of features, determine which ones to add to the layer. │ │ │ │ - * If the layer projection differs from the map projection, features │ │ │ │ - * will be transformed from the layer projection to the map projection. │ │ │ │ + * Method: createOLPropertyNode │ │ │ │ + * Create a node representing an OpenLayers property. If the property is │ │ │ │ + * null or undefined, null will be returned. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object passed │ │ │ │ - * by the protocol. │ │ │ │ + * obj - {Object} An object. │ │ │ │ + * prop - {String} A property. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Element} A property node. │ │ │ │ */ │ │ │ │ - merge: function(resp) { │ │ │ │ - this.layer.destroyFeatures(); │ │ │ │ - if (resp.success()) { │ │ │ │ - var features = resp.features; │ │ │ │ - if (features && features.length > 0) { │ │ │ │ - var remote = this.layer.projection; │ │ │ │ - var local = this.layer.map.getProjectionObject(); │ │ │ │ - if (!local.equals(remote)) { │ │ │ │ - var geom; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - geom = features[i].geometry; │ │ │ │ - if (geom) { │ │ │ │ - geom.transform(remote, local); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.layer.addFeatures(features); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.bounds = null; │ │ │ │ + createOLPropertyNode: function(obj, prop) { │ │ │ │ + var node = null; │ │ │ │ + if (obj[prop] != null) { │ │ │ │ + node = this.createElementNS(this.namespaces.ol, "ol:" + prop); │ │ │ │ + node.appendChild(this.createTextNode(obj[prop].toString())); │ │ │ │ } │ │ │ │ - this.response = null; │ │ │ │ - this.layer.events.triggerEvent("loadend", { │ │ │ │ - response: resp │ │ │ │ - }); │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy.BBOX" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Strategy/Save.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Strategy.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Strategy.Save │ │ │ │ - * A strategy that commits newly created or modified features. By default │ │ │ │ - * the strategy waits for a call to <save> before persisting changes. By │ │ │ │ - * configuring the strategy with the <auto> option, changes can be saved │ │ │ │ - * automatically. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Strategy> │ │ │ │ - */ │ │ │ │ -OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} An events object that handles all │ │ │ │ - * events on the strategy object. │ │ │ │ + * Method: write_wmc_Server │ │ │ │ + * Create a Server node given a layer context object. │ │ │ │ * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * strategy.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ + * Parameters: │ │ │ │ + * context - {Object} Layer context object. │ │ │ │ * │ │ │ │ - * Supported event types: │ │ │ │ - * start - Triggered before saving │ │ │ │ - * success - Triggered after a successful transaction │ │ │ │ - * fail - Triggered after a failed transaction │ │ │ │ - * │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for triggering this protocol │ │ │ │ - * events. │ │ │ │ + * Returns: │ │ │ │ + * {Element} A WMC Server element node. │ │ │ │ */ │ │ │ │ - events: null, │ │ │ │ + write_wmc_Server: function(context) { │ │ │ │ + var server = context.server; │ │ │ │ + var node = this.createElementDefaultNS("Server"); │ │ │ │ + var attributes = { │ │ │ │ + service: "OGC:WMS", │ │ │ │ + version: server.version │ │ │ │ + }; │ │ │ │ + if (server.title) { │ │ │ │ + attributes.title = server.title; │ │ │ │ + } │ │ │ │ + this.setAttributes(node, attributes); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: auto │ │ │ │ - * {Boolean | Number} Auto-save. Default is false. If true, features will be │ │ │ │ - * saved immediately after being added to the layer and with each │ │ │ │ - * modification or deletion. If auto is a number, features will be │ │ │ │ - * saved on an interval provided by the value (in seconds). │ │ │ │ - */ │ │ │ │ - auto: false, │ │ │ │ + // required OnlineResource element │ │ │ │ + node.appendChild(this.write_wmc_OnlineResource(server.url)); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: timer │ │ │ │ - * {Number} The id of the timer. │ │ │ │ - */ │ │ │ │ - timer: null, │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Strategy.Save │ │ │ │ - * Create a new Save strategy. │ │ │ │ + * Method: write_wmc_URLType │ │ │ │ + * Create a LogoURL/DescriptionURL/MetadataURL/DataURL/LegendURL node given a object and elementName. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Strategy.prototype.initialize.apply(this, [options]); │ │ │ │ - this.events = new OpenLayers.Events(this); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ - * │ │ │ │ + * elName - {String} Name of element (LogoURL/DescriptionURL/MetadataURL/LegendURL) │ │ │ │ + * url - {String} URL string value │ │ │ │ + * attr - {Object} Optional attributes (width, height, format) │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The strategy was successfully activated. │ │ │ │ + * {Element} A WMC element node. │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - if (this.auto) { │ │ │ │ - if (typeof this.auto === "number") { │ │ │ │ - this.timer = window.setInterval( │ │ │ │ - OpenLayers.Function.bind(this.save, this), │ │ │ │ - this.auto * 1000 │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - this.layer.events.on({ │ │ │ │ - "featureadded": this.triggerSave, │ │ │ │ - "afterfeaturemodified": this.triggerSave, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + write_wmc_URLType: function(elName, url, attr) { │ │ │ │ + var node = this.createElementDefaultNS(elName); │ │ │ │ + node.appendChild(this.write_wmc_OnlineResource(url)); │ │ │ │ + if (attr) { │ │ │ │ + var optionalAttributes = ["width", "height", "format"]; │ │ │ │ + for (var i = 0; i < optionalAttributes.length; i++) { │ │ │ │ + if (optionalAttributes[i] in attr) { │ │ │ │ + node.setAttribute(optionalAttributes[i], attr[optionalAttributes[i]]); │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ - return activated; │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ - * tear-down. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The strategy was successfully deactivated. │ │ │ │ + * Method: write_wmc_DimensionList │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - if (this.auto) { │ │ │ │ - if (typeof this.auto === "number") { │ │ │ │ - window.clearInterval(this.timer); │ │ │ │ + write_wmc_DimensionList: function(context) { │ │ │ │ + var node = this.createElementDefaultNS("DimensionList"); │ │ │ │ + var required_attributes = { │ │ │ │ + name: true, │ │ │ │ + units: true, │ │ │ │ + unitSymbol: true, │ │ │ │ + userValue: true │ │ │ │ + }; │ │ │ │ + for (var dim in context.dimensions) { │ │ │ │ + var attributes = {}; │ │ │ │ + var dimension = context.dimensions[dim]; │ │ │ │ + for (var name in dimension) { │ │ │ │ + if (typeof dimension[name] == "boolean") { │ │ │ │ + attributes[name] = Number(dimension[name]); │ │ │ │ } else { │ │ │ │ - this.layer.events.un({ │ │ │ │ - "featureadded": this.triggerSave, │ │ │ │ - "afterfeaturemodified": this.triggerSave, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + attributes[name] = dimension[name]; │ │ │ │ } │ │ │ │ } │ │ │ │ - } │ │ │ │ - return deactivated; │ │ │ │ - }, │ │ │ │ + var values = ""; │ │ │ │ + if (attributes.values) { │ │ │ │ + values = attributes.values.join(","); │ │ │ │ + delete attributes.values; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: triggerSave │ │ │ │ - * Registered as a listener. Calls save if a feature has insert, update, │ │ │ │ - * or delete state. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * event - {Object} The event this function is listening for. │ │ │ │ - */ │ │ │ │ - triggerSave: function(event) { │ │ │ │ - var feature = event.feature; │ │ │ │ - if (feature.state === OpenLayers.State.INSERT || │ │ │ │ - feature.state === OpenLayers.State.UPDATE || │ │ │ │ - feature.state === OpenLayers.State.DELETE) { │ │ │ │ - this.save([event.feature]); │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "Dimension", values, attributes │ │ │ │ + )); │ │ │ │ } │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: save │ │ │ │ - * Tell the layer protocol to commit unsaved features. If the layer │ │ │ │ - * projection differs from the map projection, features will be │ │ │ │ - * transformed into the layer projection before being committed. │ │ │ │ + * Method: write_wmc_FormatList │ │ │ │ + * Create a FormatList node given a layer context. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array} Features to be saved. If null, then default is all │ │ │ │ - * features in the layer. Features are assumed to be in the map │ │ │ │ - * projection. │ │ │ │ + * context - {Object} Layer context object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Element} A WMC FormatList element node. │ │ │ │ */ │ │ │ │ - save: function(features) { │ │ │ │ - if (!features) { │ │ │ │ - features = this.layer.features; │ │ │ │ - } │ │ │ │ - this.events.triggerEvent("start", { │ │ │ │ - features: features │ │ │ │ - }); │ │ │ │ - var remote = this.layer.projection; │ │ │ │ - var local = this.layer.map.getProjectionObject(); │ │ │ │ - if (!local.equals(remote)) { │ │ │ │ - var len = features.length; │ │ │ │ - var clones = new Array(len); │ │ │ │ - var orig, clone; │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ - orig = features[i]; │ │ │ │ - clone = orig.clone(); │ │ │ │ - clone.fid = orig.fid; │ │ │ │ - clone.state = orig.state; │ │ │ │ - if (orig.url) { │ │ │ │ - clone.url = orig.url; │ │ │ │ - } │ │ │ │ - clone._original = orig; │ │ │ │ - clone.geometry.transform(local, remote); │ │ │ │ - clones[i] = clone; │ │ │ │ - } │ │ │ │ - features = clones; │ │ │ │ + write_wmc_FormatList: function(context) { │ │ │ │ + var node = this.createElementDefaultNS("FormatList"); │ │ │ │ + for (var i = 0, len = context.formats.length; i < len; i++) { │ │ │ │ + var format = context.formats[i]; │ │ │ │ + node.appendChild(this.createElementDefaultNS( │ │ │ │ + "Format", │ │ │ │ + format.value, │ │ │ │ + (format.current && format.current == true) ? { │ │ │ │ + current: "1" │ │ │ │ + } : null │ │ │ │ + )); │ │ │ │ } │ │ │ │ - this.layer.protocol.commit(features, { │ │ │ │ - callback: this.onCommit, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onCommit │ │ │ │ - * Called after protocol commit. │ │ │ │ + * Method: write_wmc_StyleList │ │ │ │ + * Create a StyleList node given a layer context. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} A response object. │ │ │ │ + * layer - {Object} Layer context object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Element} A WMC StyleList element node. │ │ │ │ */ │ │ │ │ - onCommit: function(response) { │ │ │ │ - var evt = { │ │ │ │ - "response": response │ │ │ │ - }; │ │ │ │ - if (response.success()) { │ │ │ │ - var features = response.reqFeatures; │ │ │ │ - // deal with inserts, updates, and deletes │ │ │ │ - var state, feature; │ │ │ │ - var destroys = []; │ │ │ │ - var insertIds = response.insertIds || []; │ │ │ │ - var j = 0; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - feature = features[i]; │ │ │ │ - // if projection was different, we may be dealing with clones │ │ │ │ - feature = feature._original || feature; │ │ │ │ - state = feature.state; │ │ │ │ - if (state) { │ │ │ │ - if (state == OpenLayers.State.DELETE) { │ │ │ │ - destroys.push(feature); │ │ │ │ - } else if (state == OpenLayers.State.INSERT) { │ │ │ │ - feature.fid = insertIds[j]; │ │ │ │ - ++j; │ │ │ │ - } │ │ │ │ - feature.state = null; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + write_wmc_StyleList: function(layer) { │ │ │ │ + var node = this.createElementDefaultNS("StyleList"); │ │ │ │ │ │ │ │ - if (destroys.length > 0) { │ │ │ │ - this.layer.destroyFeatures(destroys); │ │ │ │ - } │ │ │ │ + var styles = layer.styles; │ │ │ │ + if (styles && OpenLayers.Util.isArray(styles)) { │ │ │ │ + var sld; │ │ │ │ + for (var i = 0, len = styles.length; i < len; i++) { │ │ │ │ + var s = styles[i]; │ │ │ │ + // three style types to consider │ │ │ │ + // [1] linked SLD │ │ │ │ + // [2] inline SLD │ │ │ │ + // [3] named style │ │ │ │ + // running child nodes always gets name, optionally gets href or body │ │ │ │ + var style = this.createElementDefaultNS( │ │ │ │ + "Style", │ │ │ │ + null, │ │ │ │ + (s.current && s.current == true) ? { │ │ │ │ + current: "1" │ │ │ │ + } : null │ │ │ │ + ); │ │ │ │ + if (s.href) { // [1] │ │ │ │ + sld = this.createElementDefaultNS("SLD"); │ │ │ │ + // Name is optional. │ │ │ │ + if (s.name) { │ │ │ │ + sld.appendChild(this.createElementDefaultNS("Name", s.name)); │ │ │ │ + } │ │ │ │ + // Title is optional. │ │ │ │ + if (s.title) { │ │ │ │ + sld.appendChild(this.createElementDefaultNS("Title", s.title)); │ │ │ │ + } │ │ │ │ + // LegendURL is optional │ │ │ │ + if (s.legend) { │ │ │ │ + sld.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend)); │ │ │ │ + } │ │ │ │ │ │ │ │ - this.events.triggerEvent("success", evt); │ │ │ │ + var link = this.write_wmc_OnlineResource(s.href); │ │ │ │ + sld.appendChild(link); │ │ │ │ + style.appendChild(sld); │ │ │ │ + } else if (s.body) { // [2] │ │ │ │ + sld = this.createElementDefaultNS("SLD"); │ │ │ │ + // Name is optional. │ │ │ │ + if (s.name) { │ │ │ │ + sld.appendChild(this.createElementDefaultNS("Name", s.name)); │ │ │ │ + } │ │ │ │ + // Title is optional. │ │ │ │ + if (s.title) { │ │ │ │ + sld.appendChild(this.createElementDefaultNS("Title", s.title)); │ │ │ │ + } │ │ │ │ + // LegendURL is optional │ │ │ │ + if (s.legend) { │ │ │ │ + sld.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend)); │ │ │ │ + } │ │ │ │ │ │ │ │ - } else { │ │ │ │ - this.events.triggerEvent("fail", evt); │ │ │ │ + // read in body as xml doc - assume proper namespace declarations │ │ │ │ + var doc = OpenLayers.Format.XML.prototype.read.apply(this, [s.body]); │ │ │ │ + // append to StyledLayerDescriptor node │ │ │ │ + var imported = doc.documentElement; │ │ │ │ + if (sld.ownerDocument && sld.ownerDocument.importNode) { │ │ │ │ + imported = sld.ownerDocument.importNode(imported, true); │ │ │ │ + } │ │ │ │ + sld.appendChild(imported); │ │ │ │ + style.appendChild(sld); │ │ │ │ + } else { // [3] │ │ │ │ + // both Name and Title are required. │ │ │ │ + style.appendChild(this.createElementDefaultNS("Name", s.name)); │ │ │ │ + style.appendChild(this.createElementDefaultNS("Title", s.title)); │ │ │ │ + // Abstract is optional │ │ │ │ + if (s['abstract']) { // abstract is a js keyword │ │ │ │ + style.appendChild(this.createElementDefaultNS( │ │ │ │ + "Abstract", s['abstract'] │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + // LegendURL is optional │ │ │ │ + if (s.legend) { │ │ │ │ + style.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend)); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + node.appendChild(style); │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy.Save" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Strategy/Fixed.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Strategy.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Strategy.Fixed │ │ │ │ - * A simple strategy that requests features once and never requests new data. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Strategy> │ │ │ │ - */ │ │ │ │ -OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: preload │ │ │ │ - * {Boolean} Load data before layer made visible. Enabling this may result │ │ │ │ - * in considerable overhead if your application loads many data layers │ │ │ │ - * that are not visible by default. Default is false. │ │ │ │ - */ │ │ │ │ - preload: false, │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Strategy.Fixed │ │ │ │ - * Create a new Fixed strategy. │ │ │ │ + * Method: write_wmc_OnlineResource │ │ │ │ + * Create an OnlineResource node given a URL. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: activate │ │ │ │ - * Activate the strategy: load data or add listener to load when visible │ │ │ │ + * href - {String} URL for the resource. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} True if the strategy was successfully activated or false if │ │ │ │ - * the strategy was already active. │ │ │ │ + * {Element} A WMC OnlineResource element node. │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); │ │ │ │ - if (activated) { │ │ │ │ - this.layer.events.on({ │ │ │ │ - "refresh": this.load, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - if (this.layer.visibility == true || this.preload) { │ │ │ │ - this.load(); │ │ │ │ - } else { │ │ │ │ - this.layer.events.on({ │ │ │ │ - "visibilitychanged": this.load, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return activated; │ │ │ │ + write_wmc_OnlineResource: function(href) { │ │ │ │ + var node = this.createElementDefaultNS("OnlineResource"); │ │ │ │ + this.setAttributeNS(node, this.namespaces.xlink, "xlink:type", "simple"); │ │ │ │ + this.setAttributeNS(node, this.namespaces.xlink, "xlink:href", href); │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Deactivate the strategy. Undo what is done in <activate>. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The strategy was successfully deactivated. │ │ │ │ + * Method: getOnlineResource_href │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - this.layer.events.un({ │ │ │ │ - "refresh": this.load, │ │ │ │ - "visibilitychanged": this.load, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + getOnlineResource_href: function(node) { │ │ │ │ + var object = {}; │ │ │ │ + var links = node.getElementsByTagName("OnlineResource"); │ │ │ │ + if (links.length > 0) { │ │ │ │ + this.read_wmc_OnlineResource(object, links[0]); │ │ │ │ } │ │ │ │ - return deactivated; │ │ │ │ + return object.href; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: load │ │ │ │ - * Tells protocol to load data and unhooks the visibilitychanged event │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} options to pass to protocol read. │ │ │ │ - */ │ │ │ │ - load: function(options) { │ │ │ │ - var layer = this.layer; │ │ │ │ - layer.events.triggerEvent("loadstart", { │ │ │ │ - filter: layer.filter │ │ │ │ - }); │ │ │ │ - layer.protocol.read(OpenLayers.Util.applyDefaults({ │ │ │ │ - callback: this.merge, │ │ │ │ - filter: layer.filter, │ │ │ │ - scope: this │ │ │ │ - }, options)); │ │ │ │ - layer.events.un({ │ │ │ │ - "visibilitychanged": this.load, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: merge │ │ │ │ - * Add all features to the layer. │ │ │ │ - * If the layer projection differs from the map projection, features │ │ │ │ - * will be transformed from the layer projection to the map projection. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object passed │ │ │ │ - * by the protocol. │ │ │ │ - */ │ │ │ │ - merge: function(resp) { │ │ │ │ - var layer = this.layer; │ │ │ │ - layer.destroyFeatures(); │ │ │ │ - var features = resp.features; │ │ │ │ - if (features && features.length > 0) { │ │ │ │ - var remote = layer.projection; │ │ │ │ - var local = layer.map.getProjectionObject(); │ │ │ │ - if (!local.equals(remote)) { │ │ │ │ - var geom; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - geom = features[i].geometry; │ │ │ │ - if (geom) { │ │ │ │ - geom.transform(remote, local); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - layer.addFeatures(features); │ │ │ │ - } │ │ │ │ - layer.events.triggerEvent("loadend", { │ │ │ │ - response: resp │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMC.v1" │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy.Fixed" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Strategy/Refresh.js │ │ │ │ + OpenLayers/Format/WMC/v1_0_0.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Strategy.js │ │ │ │ + * @requires OpenLayers/Format/WMC/v1.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Strategy.Refresh │ │ │ │ - * A strategy that refreshes the layer. By default the strategy waits for a │ │ │ │ - * call to <refresh> before refreshing. By configuring the strategy with │ │ │ │ - * the <interval> option, refreshing can take place automatically. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Format.WMC.v1_0_0 │ │ │ │ + * Read and write WMC version 1.0.0. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Strategy> │ │ │ │ + * - <OpenLayers.Format.WMC.v1> │ │ │ │ */ │ │ │ │ -OpenLayers.Strategy.Refresh = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: force │ │ │ │ - * {Boolean} Force a refresh on the layer. Default is false. │ │ │ │ - */ │ │ │ │ - force: false, │ │ │ │ +OpenLayers.Format.WMC.v1_0_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WMC.v1, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: interval │ │ │ │ - * {Number} Auto-refresh. Default is 0. If > 0, layer will be refreshed │ │ │ │ - * every N milliseconds. │ │ │ │ - */ │ │ │ │ - interval: 0, │ │ │ │ + /** │ │ │ │ + * Constant: VERSION │ │ │ │ + * {String} 1.0.0 │ │ │ │ + */ │ │ │ │ + VERSION: "1.0.0", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: timer │ │ │ │ - * {Number} The id of the timer. │ │ │ │ - */ │ │ │ │ - timer: null, │ │ │ │ + /** │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} http://www.opengis.net/context │ │ │ │ + * http://schemas.opengis.net/context/1.0.0/context.xsd │ │ │ │ + */ │ │ │ │ + schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.0.0/context.xsd", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Strategy.Refresh │ │ │ │ - * Create a new Refresh strategy. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WMC.v1_0_0 │ │ │ │ + * Instances of this class are not created directly. Use the │ │ │ │ + * <OpenLayers.Format.WMC> constructor instead. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.WMC.v1.prototype.initialize.apply( │ │ │ │ + this, [options] │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} True if the strategy was successfully activated. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - if (this.layer.visibility === true) { │ │ │ │ - this.start(); │ │ │ │ + /** │ │ │ │ + * Method: read_wmc_SRS │ │ │ │ + */ │ │ │ │ + read_wmc_SRS: function(layerContext, node) { │ │ │ │ + var srs = this.getChildValue(node); │ │ │ │ + if (typeof layerContext.projections != "object") { │ │ │ │ + layerContext.projections = {}; │ │ │ │ } │ │ │ │ - this.layer.events.on({ │ │ │ │ - "visibilitychanged": this.reset, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - return activated; │ │ │ │ - }, │ │ │ │ + var values = srs.split(/ +/); │ │ │ │ + for (var i = 0, len = values.length; i < len; i++) { │ │ │ │ + layerContext.projections[values[i]] = true; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ - * tear-down. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} True if the strategy was successfully deactivated. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - this.stop(); │ │ │ │ - this.layer.events.un({ │ │ │ │ - "visibilitychanged": this.reset, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - return deactivated; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: write_wmc_Layer │ │ │ │ + * Create a Layer node given a layer context object. This method adds │ │ │ │ + * elements specific to version 1.0.0. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * context - {Object} A layer context object.} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Element} A WMC Layer element node. │ │ │ │ + */ │ │ │ │ + write_wmc_Layer: function(context) { │ │ │ │ + var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply( │ │ │ │ + this, [context] │ │ │ │ + ); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: reset │ │ │ │ - * Start or cancel the refresh interval depending on the visibility of │ │ │ │ - * the layer. │ │ │ │ - */ │ │ │ │ - reset: function() { │ │ │ │ - if (this.layer.visibility === true) { │ │ │ │ - this.start(); │ │ │ │ - } else { │ │ │ │ - this.stop(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + // optional SRS element(s) │ │ │ │ + if (context.srs) { │ │ │ │ + var projections = []; │ │ │ │ + for (var name in context.srs) { │ │ │ │ + projections.push(name); │ │ │ │ + } │ │ │ │ + node.appendChild(this.createElementDefaultNS("SRS", projections.join(" "))); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: start │ │ │ │ - * Start the refresh interval. │ │ │ │ - */ │ │ │ │ - start: function() { │ │ │ │ - if (this.interval && typeof this.interval === "number" && │ │ │ │ - this.interval > 0) { │ │ │ │ + // optional FormatList element │ │ │ │ + node.appendChild(this.write_wmc_FormatList(context)); │ │ │ │ │ │ │ │ - this.timer = window.setInterval( │ │ │ │ - OpenLayers.Function.bind(this.refresh, this), │ │ │ │ - this.interval); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + // optional StyleList element │ │ │ │ + node.appendChild(this.write_wmc_StyleList(context)); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: refresh │ │ │ │ - * Tell the strategy to refresh which will refresh the layer. │ │ │ │ - */ │ │ │ │ - refresh: function() { │ │ │ │ - if (this.layer && this.layer.refresh && │ │ │ │ - typeof this.layer.refresh == "function") { │ │ │ │ + // optional DimensionList element │ │ │ │ + if (context.dimensions) { │ │ │ │ + node.appendChild(this.write_wmc_DimensionList(context)); │ │ │ │ + } │ │ │ │ │ │ │ │ - this.layer.refresh({ │ │ │ │ - force: this.force │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + // OpenLayers specific properties go in an Extension element │ │ │ │ + node.appendChild(this.write_wmc_LayerExtension(context)); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: stop │ │ │ │ - * Cancels the refresh interval. │ │ │ │ - */ │ │ │ │ - stop: function() { │ │ │ │ - if (this.timer !== null) { │ │ │ │ - window.clearInterval(this.timer); │ │ │ │ - this.timer = null; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMC.v1_0_0" │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy.Refresh" │ │ │ │ -}); │ │ │ │ + }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Strategy/Paging.js │ │ │ │ + OpenLayers/Format/WMC/v1_1_0.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Strategy.js │ │ │ │ + * @requires OpenLayers/Format/WMC/v1.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Strategy.Paging │ │ │ │ - * Strategy for vector feature paging │ │ │ │ + * Class: OpenLayers.Format.WMC.v1_1_0 │ │ │ │ + * Read and write WMC version 1.1.0. │ │ │ │ * │ │ │ │ + * Differences between 1.1.0 and 1.0.0: │ │ │ │ + * - 1.1.0 Layers have optional sld:MinScaleDenominator and │ │ │ │ + * sld:MaxScaleDenominator │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Strategy> │ │ │ │ + * - <OpenLayers.Format.WMC.v1> │ │ │ │ */ │ │ │ │ -OpenLayers.Strategy.Paging = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ +OpenLayers.Format.WMC.v1_1_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WMC.v1, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: features │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} Cached features. │ │ │ │ - */ │ │ │ │ - features: null, │ │ │ │ + /** │ │ │ │ + * Constant: VERSION │ │ │ │ + * {String} 1.1.0 │ │ │ │ + */ │ │ │ │ + VERSION: "1.1.0", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: length │ │ │ │ - * {Integer} Number of features per page. Default is 10. │ │ │ │ - */ │ │ │ │ - length: 10, │ │ │ │ + /** │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} http://www.opengis.net/context │ │ │ │ + * http://schemas.opengis.net/context/1.1.0/context.xsd │ │ │ │ + */ │ │ │ │ + schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: num │ │ │ │ - * {Integer} The currently displayed page number. │ │ │ │ - */ │ │ │ │ - num: null, │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WMC.v1_1_0 │ │ │ │ + * Instances of this class are not created directly. Use the │ │ │ │ + * <OpenLayers.Format.WMC> constructor instead. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.WMC.v1.prototype.initialize.apply( │ │ │ │ + this, [options] │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: paging │ │ │ │ - * {Boolean} The strategy is currently changing pages. │ │ │ │ - */ │ │ │ │ - paging: false, │ │ │ │ + /** │ │ │ │ + * Method: read_sld_MinScaleDenominator │ │ │ │ + * Read a sld:MinScaleDenominator node. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layerContext - {Object} An object representing a layer. │ │ │ │ + * node - {Element} An element node. │ │ │ │ + */ │ │ │ │ + read_sld_MinScaleDenominator: function(layerContext, node) { │ │ │ │ + var minScaleDenominator = parseFloat(this.getChildValue(node)); │ │ │ │ + if (minScaleDenominator > 0) { │ │ │ │ + layerContext.maxScale = minScaleDenominator; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Strategy.Paging │ │ │ │ - * Create a new paging strategy. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Method: read_sld_MaxScaleDenominator │ │ │ │ + * Read a sld:MaxScaleDenominator node. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layerContext - {Object} An object representing a layer. │ │ │ │ + * node - {Element} An element node. │ │ │ │ + */ │ │ │ │ + read_sld_MaxScaleDenominator: function(layerContext, node) { │ │ │ │ + layerContext.minScale = parseFloat(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The strategy was successfully activated. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - this.layer.events.on({ │ │ │ │ - "beforefeaturesadded": this.cacheFeatures, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - return activated; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: read_wmc_SRS │ │ │ │ + */ │ │ │ │ + read_wmc_SRS: function(layerContext, node) { │ │ │ │ + if (!("srs" in layerContext)) { │ │ │ │ + layerContext.srs = {}; │ │ │ │ + } │ │ │ │ + layerContext.srs[this.getChildValue(node)] = true; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ - * tear-down. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The strategy was successfully deactivated. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - this.clearCache(); │ │ │ │ - this.layer.events.un({ │ │ │ │ - "beforefeaturesadded": this.cacheFeatures, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - return deactivated; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: write_wmc_Layer │ │ │ │ + * Create a Layer node given a layer context object. This method adds │ │ │ │ + * elements specific to version 1.1.0. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * context - {Object} A layer context object.} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Element} A WMC Layer element node. │ │ │ │ + */ │ │ │ │ + write_wmc_Layer: function(context) { │ │ │ │ + var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply( │ │ │ │ + this, [context] │ │ │ │ + ); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: cacheFeatures │ │ │ │ - * Cache features before they are added to the layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * event - {Object} The event that this was listening for. This will come │ │ │ │ - * with a batch of features to be paged. │ │ │ │ - */ │ │ │ │ - cacheFeatures: function(event) { │ │ │ │ - if (!this.paging) { │ │ │ │ - this.clearCache(); │ │ │ │ - this.features = event.features; │ │ │ │ - this.pageNext(event); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + // min/max scale denominator elements go before the 4th element in v1 │ │ │ │ + if (context.maxScale) { │ │ │ │ + var minSD = this.createElementNS( │ │ │ │ + this.namespaces.sld, "sld:MinScaleDenominator" │ │ │ │ + ); │ │ │ │ + minSD.appendChild(this.createTextNode(context.maxScale.toPrecision(16))); │ │ │ │ + node.appendChild(minSD); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: clearCache │ │ │ │ - * Clear out the cached features. This destroys features, assuming │ │ │ │ - * nothing else has a reference. │ │ │ │ - */ │ │ │ │ - clearCache: function() { │ │ │ │ - if (this.features) { │ │ │ │ - for (var i = 0; i < this.features.length; ++i) { │ │ │ │ - this.features[i].destroy(); │ │ │ │ + if (context.minScale) { │ │ │ │ + var maxSD = this.createElementNS( │ │ │ │ + this.namespaces.sld, "sld:MaxScaleDenominator" │ │ │ │ + ); │ │ │ │ + maxSD.appendChild(this.createTextNode(context.minScale.toPrecision(16))); │ │ │ │ + node.appendChild(maxSD); │ │ │ │ } │ │ │ │ - } │ │ │ │ - this.features = null; │ │ │ │ - this.num = null; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: pageCount │ │ │ │ - * Get the total count of pages given the current cache of features. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} The page count. │ │ │ │ - */ │ │ │ │ - pageCount: function() { │ │ │ │ - var numFeatures = this.features ? this.features.length : 0; │ │ │ │ - return Math.ceil(numFeatures / this.length); │ │ │ │ - }, │ │ │ │ + // optional SRS element(s) │ │ │ │ + if (context.srs) { │ │ │ │ + for (var name in context.srs) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("SRS", name)); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: pageNum │ │ │ │ - * Get the zero based page number. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} The current page number being displayed. │ │ │ │ - */ │ │ │ │ - pageNum: function() { │ │ │ │ - return this.num; │ │ │ │ - }, │ │ │ │ + // optional FormatList element │ │ │ │ + node.appendChild(this.write_wmc_FormatList(context)); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: pageLength │ │ │ │ - * Gets or sets page length. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * newLength - {Integer} Optional length to be set. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} The length of a page (number of features per page). │ │ │ │ - */ │ │ │ │ - pageLength: function(newLength) { │ │ │ │ - if (newLength && newLength > 0) { │ │ │ │ - this.length = newLength; │ │ │ │ - } │ │ │ │ - return this.length; │ │ │ │ - }, │ │ │ │ + // optional StyleList element │ │ │ │ + node.appendChild(this.write_wmc_StyleList(context)); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: pageNext │ │ │ │ - * Display the next page of features. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} A new page was displayed. │ │ │ │ - */ │ │ │ │ - pageNext: function(event) { │ │ │ │ - var changed = false; │ │ │ │ - if (this.features) { │ │ │ │ - if (this.num === null) { │ │ │ │ - this.num = -1; │ │ │ │ + // optional DimensionList element │ │ │ │ + if (context.dimensions) { │ │ │ │ + node.appendChild(this.write_wmc_DimensionList(context)); │ │ │ │ } │ │ │ │ - var start = (this.num + 1) * this.length; │ │ │ │ - changed = this.page(start, event); │ │ │ │ - } │ │ │ │ - return changed; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: pagePrevious │ │ │ │ - * Display the previous page of features. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} A new page was displayed. │ │ │ │ - */ │ │ │ │ - pagePrevious: function() { │ │ │ │ - var changed = false; │ │ │ │ - if (this.features) { │ │ │ │ - if (this.num === null) { │ │ │ │ - this.num = this.pageCount(); │ │ │ │ - } │ │ │ │ - var start = (this.num - 1) * this.length; │ │ │ │ - changed = this.page(start); │ │ │ │ - } │ │ │ │ - return changed; │ │ │ │ - }, │ │ │ │ + // OpenLayers specific properties go in an Extension element │ │ │ │ + node.appendChild(this.write_wmc_LayerExtension(context)); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: page │ │ │ │ - * Display the page starting at the given index from the cache. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} A new page was displayed. │ │ │ │ - */ │ │ │ │ - page: function(start, event) { │ │ │ │ - var changed = false; │ │ │ │ - if (this.features) { │ │ │ │ - if (start >= 0 && start < this.features.length) { │ │ │ │ - var num = Math.floor(start / this.length); │ │ │ │ - if (num != this.num) { │ │ │ │ - this.paging = true; │ │ │ │ - var features = this.features.slice(start, start + this.length); │ │ │ │ - this.layer.removeFeatures(this.layer.features); │ │ │ │ - this.num = num; │ │ │ │ - // modify the event if any │ │ │ │ - if (event && event.features) { │ │ │ │ - // this.was called by an event listener │ │ │ │ - event.features = features; │ │ │ │ - } else { │ │ │ │ - // this was called directly on the strategy │ │ │ │ - this.layer.addFeatures(features); │ │ │ │ - } │ │ │ │ - this.paging = false; │ │ │ │ - changed = true; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return changed; │ │ │ │ - }, │ │ │ │ + return node; │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy.Paging" │ │ │ │ -}); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMC.v1_1_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Strategy/Filter.js │ │ │ │ + OpenLayers/Format/SLD/v1.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Strategy.js │ │ │ │ - * @requires OpenLayers/Filter.js │ │ │ │ + * @requires OpenLayers/Rule.js │ │ │ │ + * @requires OpenLayers/Format/SLD.js │ │ │ │ + * @requires OpenLayers/Format/Filter/v1_0_0.js │ │ │ │ + * @requires OpenLayers/Symbolizer/Point.js │ │ │ │ + * @requires OpenLayers/Symbolizer/Line.js │ │ │ │ + * @requires OpenLayers/Symbolizer/Polygon.js │ │ │ │ + * @requires OpenLayers/Symbolizer/Text.js │ │ │ │ + * @requires OpenLayers/Symbolizer/Raster.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Strategy.Filter │ │ │ │ - * Strategy for limiting features that get added to a layer by │ │ │ │ - * evaluating a filter. The strategy maintains a cache of │ │ │ │ - * all features until removeFeatures is called on the layer. │ │ │ │ + * Class: OpenLayers.Format.SLD.v1 │ │ │ │ + * Superclass for SLD version 1 parsers. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Strategy> │ │ │ │ + * - <OpenLayers.Format.Filter.v1_0_0> │ │ │ │ */ │ │ │ │ -OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: filter │ │ │ │ - * {<OpenLayers.Filter>} Filter for limiting features sent to the layer. │ │ │ │ - * Use the <setFilter> method to update this filter after construction. │ │ │ │ - */ │ │ │ │ - filter: null, │ │ │ │ +OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: cache │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} List of currently cached │ │ │ │ - * features. │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ */ │ │ │ │ - cache: null, │ │ │ │ + namespaces: { │ │ │ │ + sld: "http://www.opengis.net/sld", │ │ │ │ + ogc: "http://www.opengis.net/ogc", │ │ │ │ + gml: "http://www.opengis.net/gml", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: caching │ │ │ │ - * {Boolean} The filter is currently caching features. │ │ │ │ + * Property: defaultPrefix │ │ │ │ */ │ │ │ │ - caching: false, │ │ │ │ + defaultPrefix: "sld", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Strategy.Filter │ │ │ │ - * Create a new filter strategy. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} Schema location for a particular minor version. │ │ │ │ */ │ │ │ │ + schemaLocation: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ - * By default, this strategy automatically activates itself when a layer │ │ │ │ - * is added to a map. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} True if the strategy was successfully activated or false if │ │ │ │ - * the strategy was already active. │ │ │ │ + /** │ │ │ │ + * APIProperty: multipleSymbolizers │ │ │ │ + * {Boolean} Support multiple symbolizers per rule. Default is false. if │ │ │ │ + * true, an OpenLayers.Style2 instance will be created to represent │ │ │ │ + * user styles instead of an OpenLayers.Style instace. The │ │ │ │ + * OpenLayers.Style2 class allows collections of rules with multiple │ │ │ │ + * symbolizers, but is not currently useful for client side rendering. │ │ │ │ + * If multiple symbolizers is true, multiple FeatureTypeStyle elements │ │ │ │ + * are preserved in reading/writing by setting symbolizer zIndex values. │ │ │ │ + * In addition, the <defaultSymbolizer> property is ignored if │ │ │ │ + * multiple symbolizers are supported (defaults should be applied │ │ │ │ + * when rendering). │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); │ │ │ │ - if (activated) { │ │ │ │ - this.cache = []; │ │ │ │ - this.layer.events.on({ │ │ │ │ - "beforefeaturesadded": this.handleAdd, │ │ │ │ - "beforefeaturesremoved": this.handleRemove, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - return activated; │ │ │ │ - }, │ │ │ │ + multipleSymbolizers: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the strategy. Clear the feature cache. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} True if the strategy was successfully deactivated or false if │ │ │ │ - * the strategy was already inactive. │ │ │ │ + * Property: featureTypeCounter │ │ │ │ + * {Number} Private counter for multiple feature type styles. │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - this.cache = null; │ │ │ │ - if (this.layer && this.layer.events) { │ │ │ │ - this.layer.events.un({ │ │ │ │ - "beforefeaturesadded": this.handleAdd, │ │ │ │ - "beforefeaturesremoved": this.handleRemove, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + featureTypeCounter: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleAdd │ │ │ │ + * APIProperty: defaultSymbolizer. │ │ │ │ + * {Object} A symbolizer with the SLD defaults. │ │ │ │ */ │ │ │ │ - handleAdd: function(event) { │ │ │ │ - if (!this.caching && this.filter) { │ │ │ │ - var features = event.features; │ │ │ │ - event.features = []; │ │ │ │ - var feature; │ │ │ │ - for (var i = 0, ii = features.length; i < ii; ++i) { │ │ │ │ - feature = features[i]; │ │ │ │ - if (this.filter.evaluate(feature)) { │ │ │ │ - event.features.push(feature); │ │ │ │ - } else { │ │ │ │ - this.cache.push(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + defaultSymbolizer: { │ │ │ │ + fillColor: "#808080", │ │ │ │ + fillOpacity: 1, │ │ │ │ + strokeColor: "#000000", │ │ │ │ + strokeOpacity: 1, │ │ │ │ + strokeWidth: 1, │ │ │ │ + strokeDashstyle: "solid", │ │ │ │ + pointRadius: 3, │ │ │ │ + graphicName: "square" │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleRemove │ │ │ │ - */ │ │ │ │ - handleRemove: function(event) { │ │ │ │ - if (!this.caching) { │ │ │ │ - this.cache = []; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setFilter │ │ │ │ - * Update the filter for this strategy. This will re-evaluate │ │ │ │ - * any features on the layer and in the cache. Only features │ │ │ │ - * for which filter.evalute(feature) returns true will be │ │ │ │ - * added to the layer. Others will be cached by the strategy. │ │ │ │ + * Constructor: OpenLayers.Format.SLD.v1 │ │ │ │ + * Instances of this class are not created directly. Use the │ │ │ │ + * <OpenLayers.Format.SLD> constructor instead. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * filter - {<OpenLayers.Filter>} A filter for evaluating features. │ │ │ │ - */ │ │ │ │ - setFilter: function(filter) { │ │ │ │ - this.filter = filter; │ │ │ │ - var previousCache = this.cache; │ │ │ │ - this.cache = []; │ │ │ │ - // look through layer for features to remove from layer │ │ │ │ - this.handleAdd({ │ │ │ │ - features: this.layer.features │ │ │ │ - }); │ │ │ │ - // cache now contains features to remove from layer │ │ │ │ - if (this.cache.length > 0) { │ │ │ │ - this.caching = true; │ │ │ │ - this.layer.removeFeatures(this.cache.slice()); │ │ │ │ - this.caching = false; │ │ │ │ - } │ │ │ │ - // now look through previous cache for features to add to layer │ │ │ │ - if (previousCache.length > 0) { │ │ │ │ - var event = { │ │ │ │ - features: previousCache │ │ │ │ - }; │ │ │ │ - this.handleAdd(event); │ │ │ │ - if (event.features.length > 0) { │ │ │ │ - // event has features to add to layer │ │ │ │ - this.caching = true; │ │ │ │ - this.layer.addFeatures(event.features); │ │ │ │ - this.caching = false; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy.Filter" │ │ │ │ - │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Renderer/Elements.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Renderer.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.ElementsIndexer │ │ │ │ - * This class takes care of figuring out which order elements should be │ │ │ │ - * placed in the DOM based on given indexing methods. │ │ │ │ - */ │ │ │ │ -OpenLayers.ElementsIndexer = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: maxZIndex │ │ │ │ - * {Integer} This is the largest-most z-index value for a node │ │ │ │ - * contained within the indexer. │ │ │ │ - */ │ │ │ │ - maxZIndex: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: order │ │ │ │ - * {Array<String>} This is an array of node id's stored in the │ │ │ │ - * order that they should show up on screen. Id's higher up in the │ │ │ │ - * array (higher array index) represent nodes with higher z-indeces. │ │ │ │ - */ │ │ │ │ - order: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: indices │ │ │ │ - * {Object} This is a hash that maps node ids to their z-index value │ │ │ │ - * stored in the indexer. This is done to make finding a nodes z-index │ │ │ │ - * value O(1). │ │ │ │ - */ │ │ │ │ - indices: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: compare │ │ │ │ - * {Function} This is the function used to determine placement of │ │ │ │ - * of a new node within the indexer. If null, this defaults to to │ │ │ │ - * the Z_ORDER_DRAWING_ORDER comparison method. │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - compare: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: initialize │ │ │ │ - * Create a new indexer with │ │ │ │ - * │ │ │ │ + * Method: read │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * yOrdering - {Boolean} Whether to use y-ordering. │ │ │ │ + * data - {DOMElement} An SLD document element. │ │ │ │ + * options - {Object} Options for the reader. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * namedLayersAsArray - {Boolean} Generate a namedLayers array. If false, │ │ │ │ + * the namedLayers property value will be an object keyed by layer name. │ │ │ │ + * Default is false. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An object representing the SLD. │ │ │ │ */ │ │ │ │ - initialize: function(yOrdering) { │ │ │ │ - │ │ │ │ - this.compare = yOrdering ? │ │ │ │ - OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : │ │ │ │ - OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER; │ │ │ │ - │ │ │ │ - this.clear(); │ │ │ │ + read: function(data, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var sld = { │ │ │ │ + namedLayers: options.namedLayersAsArray === true ? [] : {} │ │ │ │ + }; │ │ │ │ + this.readChildNodes(data, sld); │ │ │ │ + return sld; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: insert │ │ │ │ - * Insert a new node into the indexer. In order to find the correct │ │ │ │ - * positioning for the node to be inserted, this method uses a binary │ │ │ │ - * search. This makes inserting O(log(n)). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * newNode - {DOMElement} The new node to be inserted. │ │ │ │ - * │ │ │ │ - * Returns │ │ │ │ - * {DOMElement} the node before which we should insert our newNode, or │ │ │ │ - * null if newNode can just be appended. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + readers: OpenLayers.Util.applyDefaults({ │ │ │ │ + "sld": { │ │ │ │ + "StyledLayerDescriptor": function(node, sld) { │ │ │ │ + sld.version = node.getAttribute("version"); │ │ │ │ + this.readChildNodes(node, sld); │ │ │ │ + }, │ │ │ │ + "Name": function(node, obj) { │ │ │ │ + obj.name = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Title": function(node, obj) { │ │ │ │ + obj.title = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Abstract": function(node, obj) { │ │ │ │ + obj.description = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "NamedLayer": function(node, sld) { │ │ │ │ + var layer = { │ │ │ │ + userStyles: [], │ │ │ │ + namedStyles: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, layer); │ │ │ │ + // give each of the user styles this layer name │ │ │ │ + for (var i = 0, len = layer.userStyles.length; i < len; ++i) { │ │ │ │ + layer.userStyles[i].layerName = layer.name; │ │ │ │ + } │ │ │ │ + if (OpenLayers.Util.isArray(sld.namedLayers)) { │ │ │ │ + sld.namedLayers.push(layer); │ │ │ │ + } else { │ │ │ │ + sld.namedLayers[layer.name] = layer; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "NamedStyle": function(node, layer) { │ │ │ │ + layer.namedStyles.push( │ │ │ │ + this.getChildName(node.firstChild) │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + "UserStyle": function(node, layer) { │ │ │ │ + var obj = { │ │ │ │ + defaultsPerSymbolizer: true, │ │ │ │ + rules: [] │ │ │ │ + }; │ │ │ │ + this.featureTypeCounter = -1; │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + var style; │ │ │ │ + if (this.multipleSymbolizers) { │ │ │ │ + delete obj.defaultsPerSymbolizer; │ │ │ │ + style = new OpenLayers.Style2(obj); │ │ │ │ + } else { │ │ │ │ + style = new OpenLayers.Style(this.defaultSymbolizer, obj); │ │ │ │ + } │ │ │ │ + layer.userStyles.push(style); │ │ │ │ + }, │ │ │ │ + "IsDefault": function(node, style) { │ │ │ │ + if (this.getChildValue(node) == "1") { │ │ │ │ + style.isDefault = true; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "FeatureTypeStyle": function(node, style) { │ │ │ │ + ++this.featureTypeCounter; │ │ │ │ + var obj = { │ │ │ │ + rules: this.multipleSymbolizers ? style.rules : [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + if (!this.multipleSymbolizers) { │ │ │ │ + style.rules = obj.rules; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Rule": function(node, obj) { │ │ │ │ + var config; │ │ │ │ + if (this.multipleSymbolizers) { │ │ │ │ + config = { │ │ │ │ + symbolizers: [] │ │ │ │ + }; │ │ │ │ + } │ │ │ │ + var rule = new OpenLayers.Rule(config); │ │ │ │ + this.readChildNodes(node, rule); │ │ │ │ + obj.rules.push(rule); │ │ │ │ + }, │ │ │ │ + "ElseFilter": function(node, rule) { │ │ │ │ + rule.elseFilter = true; │ │ │ │ + }, │ │ │ │ + "MinScaleDenominator": function(node, rule) { │ │ │ │ + rule.minScaleDenominator = parseFloat(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "MaxScaleDenominator": function(node, rule) { │ │ │ │ + rule.maxScaleDenominator = parseFloat(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "TextSymbolizer": function(node, rule) { │ │ │ │ + var config = {}; │ │ │ │ + this.readChildNodes(node, config); │ │ │ │ + if (this.multipleSymbolizers) { │ │ │ │ + config.zIndex = this.featureTypeCounter; │ │ │ │ + rule.symbolizers.push( │ │ │ │ + new OpenLayers.Symbolizer.Text(config) │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + rule.symbolizer["Text"] = OpenLayers.Util.applyDefaults( │ │ │ │ + config, rule.symbolizer["Text"] │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "LabelPlacement": function(node, symbolizer) { │ │ │ │ + this.readChildNodes(node, symbolizer); │ │ │ │ + }, │ │ │ │ + "PointPlacement": function(node, symbolizer) { │ │ │ │ + var config = {}; │ │ │ │ + this.readChildNodes(node, config); │ │ │ │ + config.labelRotation = config.rotation; │ │ │ │ + delete config.rotation; │ │ │ │ + var labelAlign, │ │ │ │ + x = symbolizer.labelAnchorPointX, │ │ │ │ + y = symbolizer.labelAnchorPointY; │ │ │ │ + if (x <= 1 / 3) { │ │ │ │ + labelAlign = 'l'; │ │ │ │ + } else if (x > 1 / 3 && x < 2 / 3) { │ │ │ │ + labelAlign = 'c'; │ │ │ │ + } else if (x >= 2 / 3) { │ │ │ │ + labelAlign = 'r'; │ │ │ │ + } │ │ │ │ + if (y <= 1 / 3) { │ │ │ │ + labelAlign += 'b'; │ │ │ │ + } else if (y > 1 / 3 && y < 2 / 3) { │ │ │ │ + labelAlign += 'm'; │ │ │ │ + } else if (y >= 2 / 3) { │ │ │ │ + labelAlign += 't'; │ │ │ │ + } │ │ │ │ + config.labelAlign = labelAlign; │ │ │ │ + OpenLayers.Util.applyDefaults(symbolizer, config); │ │ │ │ + }, │ │ │ │ + "AnchorPoint": function(node, symbolizer) { │ │ │ │ + this.readChildNodes(node, symbolizer); │ │ │ │ + }, │ │ │ │ + "AnchorPointX": function(node, symbolizer) { │ │ │ │ + var labelAnchorPointX = this.readers.ogc._expression.call(this, node); │ │ │ │ + // always string, could be empty string │ │ │ │ + if (labelAnchorPointX) { │ │ │ │ + symbolizer.labelAnchorPointX = labelAnchorPointX; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "AnchorPointY": function(node, symbolizer) { │ │ │ │ + var labelAnchorPointY = this.readers.ogc._expression.call(this, node); │ │ │ │ + // always string, could be empty string │ │ │ │ + if (labelAnchorPointY) { │ │ │ │ + symbolizer.labelAnchorPointY = labelAnchorPointY; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Displacement": function(node, symbolizer) { │ │ │ │ + this.readChildNodes(node, symbolizer); │ │ │ │ + }, │ │ │ │ + "DisplacementX": function(node, symbolizer) { │ │ │ │ + var labelXOffset = this.readers.ogc._expression.call(this, node); │ │ │ │ + // always string, could be empty string │ │ │ │ + if (labelXOffset) { │ │ │ │ + symbolizer.labelXOffset = labelXOffset; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "DisplacementY": function(node, symbolizer) { │ │ │ │ + var labelYOffset = this.readers.ogc._expression.call(this, node); │ │ │ │ + // always string, could be empty string │ │ │ │ + if (labelYOffset) { │ │ │ │ + symbolizer.labelYOffset = labelYOffset; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "LinePlacement": function(node, symbolizer) { │ │ │ │ + this.readChildNodes(node, symbolizer); │ │ │ │ + }, │ │ │ │ + "PerpendicularOffset": function(node, symbolizer) { │ │ │ │ + var labelPerpendicularOffset = this.readers.ogc._expression.call(this, node); │ │ │ │ + // always string, could be empty string │ │ │ │ + if (labelPerpendicularOffset) { │ │ │ │ + symbolizer.labelPerpendicularOffset = labelPerpendicularOffset; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Label": function(node, symbolizer) { │ │ │ │ + var value = this.readers.ogc._expression.call(this, node); │ │ │ │ + if (value) { │ │ │ │ + symbolizer.label = value; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Font": function(node, symbolizer) { │ │ │ │ + this.readChildNodes(node, symbolizer); │ │ │ │ + }, │ │ │ │ + "Halo": function(node, symbolizer) { │ │ │ │ + // halo has a fill, so send fresh object │ │ │ │ + var obj = {}; │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + symbolizer.haloRadius = obj.haloRadius; │ │ │ │ + symbolizer.haloColor = obj.fillColor; │ │ │ │ + symbolizer.haloOpacity = obj.fillOpacity; │ │ │ │ + }, │ │ │ │ + "Radius": function(node, symbolizer) { │ │ │ │ + var radius = this.readers.ogc._expression.call(this, node); │ │ │ │ + if (radius != null) { │ │ │ │ + // radius is only used for halo │ │ │ │ + symbolizer.haloRadius = radius; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "RasterSymbolizer": function(node, rule) { │ │ │ │ + var config = {}; │ │ │ │ + this.readChildNodes(node, config); │ │ │ │ + if (this.multipleSymbolizers) { │ │ │ │ + config.zIndex = this.featureTypeCounter; │ │ │ │ + rule.symbolizers.push( │ │ │ │ + new OpenLayers.Symbolizer.Raster(config) │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + rule.symbolizer["Raster"] = OpenLayers.Util.applyDefaults( │ │ │ │ + config, rule.symbolizer["Raster"] │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Geometry": function(node, obj) { │ │ │ │ + obj.geometry = {}; │ │ │ │ + this.readChildNodes(node, obj.geometry); │ │ │ │ + }, │ │ │ │ + "ColorMap": function(node, symbolizer) { │ │ │ │ + symbolizer.colorMap = []; │ │ │ │ + this.readChildNodes(node, symbolizer.colorMap); │ │ │ │ + }, │ │ │ │ + "ColorMapEntry": function(node, colorMap) { │ │ │ │ + var q = node.getAttribute("quantity"); │ │ │ │ + var o = node.getAttribute("opacity"); │ │ │ │ + colorMap.push({ │ │ │ │ + color: node.getAttribute("color"), │ │ │ │ + quantity: q !== null ? parseFloat(q) : undefined, │ │ │ │ + label: node.getAttribute("label") || undefined, │ │ │ │ + opacity: o !== null ? parseFloat(o) : undefined │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "LineSymbolizer": function(node, rule) { │ │ │ │ + var config = {}; │ │ │ │ + this.readChildNodes(node, config); │ │ │ │ + if (this.multipleSymbolizers) { │ │ │ │ + config.zIndex = this.featureTypeCounter; │ │ │ │ + rule.symbolizers.push( │ │ │ │ + new OpenLayers.Symbolizer.Line(config) │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + rule.symbolizer["Line"] = OpenLayers.Util.applyDefaults( │ │ │ │ + config, rule.symbolizer["Line"] │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "PolygonSymbolizer": function(node, rule) { │ │ │ │ + var config = { │ │ │ │ + fill: false, │ │ │ │ + stroke: false │ │ │ │ + }; │ │ │ │ + if (!this.multipleSymbolizers) { │ │ │ │ + config = rule.symbolizer["Polygon"] || config; │ │ │ │ + } │ │ │ │ + this.readChildNodes(node, config); │ │ │ │ + if (this.multipleSymbolizers) { │ │ │ │ + config.zIndex = this.featureTypeCounter; │ │ │ │ + rule.symbolizers.push( │ │ │ │ + new OpenLayers.Symbolizer.Polygon(config) │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + rule.symbolizer["Polygon"] = config; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "PointSymbolizer": function(node, rule) { │ │ │ │ + var config = { │ │ │ │ + fill: false, │ │ │ │ + stroke: false, │ │ │ │ + graphic: false │ │ │ │ + }; │ │ │ │ + if (!this.multipleSymbolizers) { │ │ │ │ + config = rule.symbolizer["Point"] || config; │ │ │ │ + } │ │ │ │ + this.readChildNodes(node, config); │ │ │ │ + if (this.multipleSymbolizers) { │ │ │ │ + config.zIndex = this.featureTypeCounter; │ │ │ │ + rule.symbolizers.push( │ │ │ │ + new OpenLayers.Symbolizer.Point(config) │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + rule.symbolizer["Point"] = config; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Stroke": function(node, symbolizer) { │ │ │ │ + symbolizer.stroke = true; │ │ │ │ + this.readChildNodes(node, symbolizer); │ │ │ │ + }, │ │ │ │ + "Fill": function(node, symbolizer) { │ │ │ │ + symbolizer.fill = true; │ │ │ │ + this.readChildNodes(node, symbolizer); │ │ │ │ + }, │ │ │ │ + "CssParameter": function(node, symbolizer) { │ │ │ │ + var cssProperty = node.getAttribute("name"); │ │ │ │ + var symProperty = this.cssMap[cssProperty]; │ │ │ │ + // for labels, fill should map to fontColor and fill-opacity │ │ │ │ + // to fontOpacity │ │ │ │ + if (symbolizer.label) { │ │ │ │ + if (cssProperty === 'fill') { │ │ │ │ + symProperty = "fontColor"; │ │ │ │ + } else if (cssProperty === 'fill-opacity') { │ │ │ │ + symProperty = "fontOpacity"; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (symProperty) { │ │ │ │ + // Limited support for parsing of OGC expressions │ │ │ │ + var value = this.readers.ogc._expression.call(this, node); │ │ │ │ + // always string, could be an empty string │ │ │ │ + if (value) { │ │ │ │ + symbolizer[symProperty] = value; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Graphic": function(node, symbolizer) { │ │ │ │ + symbolizer.graphic = true; │ │ │ │ + var graphic = {}; │ │ │ │ + // painter's order not respected here, clobber previous with next │ │ │ │ + this.readChildNodes(node, graphic); │ │ │ │ + // directly properties with names that match symbolizer properties │ │ │ │ + var properties = [ │ │ │ │ + "stroke", "strokeColor", "strokeWidth", "strokeOpacity", │ │ │ │ + "strokeLinecap", "fill", "fillColor", "fillOpacity", │ │ │ │ + "graphicName", "rotation", "graphicFormat" │ │ │ │ + ]; │ │ │ │ + var prop, value; │ │ │ │ + for (var i = 0, len = properties.length; i < len; ++i) { │ │ │ │ + prop = properties[i]; │ │ │ │ + value = graphic[prop]; │ │ │ │ + if (value != undefined) { │ │ │ │ + symbolizer[prop] = value; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + // set other generic properties with specific graphic property names │ │ │ │ + if (graphic.opacity != undefined) { │ │ │ │ + symbolizer.graphicOpacity = graphic.opacity; │ │ │ │ + } │ │ │ │ + if (graphic.size != undefined) { │ │ │ │ + var pointRadius = graphic.size / 2; │ │ │ │ + if (isNaN(pointRadius)) { │ │ │ │ + // likely a property name │ │ │ │ + symbolizer.graphicWidth = graphic.size; │ │ │ │ + } else { │ │ │ │ + symbolizer.pointRadius = graphic.size / 2; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (graphic.href != undefined) { │ │ │ │ + symbolizer.externalGraphic = graphic.href; │ │ │ │ + } │ │ │ │ + if (graphic.rotation != undefined) { │ │ │ │ + symbolizer.rotation = graphic.rotation; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "ExternalGraphic": function(node, graphic) { │ │ │ │ + this.readChildNodes(node, graphic); │ │ │ │ + }, │ │ │ │ + "Mark": function(node, graphic) { │ │ │ │ + this.readChildNodes(node, graphic); │ │ │ │ + }, │ │ │ │ + "WellKnownName": function(node, graphic) { │ │ │ │ + graphic.graphicName = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Opacity": function(node, obj) { │ │ │ │ + var opacity = this.readers.ogc._expression.call(this, node); │ │ │ │ + // always string, could be empty string │ │ │ │ + if (opacity) { │ │ │ │ + obj.opacity = opacity; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Size": function(node, obj) { │ │ │ │ + var size = this.readers.ogc._expression.call(this, node); │ │ │ │ + // always string, could be empty string │ │ │ │ + if (size) { │ │ │ │ + obj.size = size; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Rotation": function(node, obj) { │ │ │ │ + var rotation = this.readers.ogc._expression.call(this, node); │ │ │ │ + // always string, could be empty string │ │ │ │ + if (rotation) { │ │ │ │ + obj.rotation = rotation; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "OnlineResource": function(node, obj) { │ │ │ │ + obj.href = this.getAttributeNS( │ │ │ │ + node, this.namespaces.xlink, "href" │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + "Format": function(node, graphic) { │ │ │ │ + graphic.graphicFormat = this.getChildValue(node); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.Filter.v1_0_0.prototype.readers), │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: cssMap │ │ │ │ + * {Object} Object mapping supported css property names to OpenLayers │ │ │ │ + * symbolizer property names. │ │ │ │ + */ │ │ │ │ + cssMap: { │ │ │ │ + "stroke": "strokeColor", │ │ │ │ + "stroke-opacity": "strokeOpacity", │ │ │ │ + "stroke-width": "strokeWidth", │ │ │ │ + "stroke-linecap": "strokeLinecap", │ │ │ │ + "stroke-dasharray": "strokeDashstyle", │ │ │ │ + "fill": "fillColor", │ │ │ │ + "fill-opacity": "fillOpacity", │ │ │ │ + "font-family": "fontFamily", │ │ │ │ + "font-size": "fontSize", │ │ │ │ + "font-weight": "fontWeight", │ │ │ │ + "font-style": "fontStyle" │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getCssProperty │ │ │ │ + * Given a symbolizer property, get the corresponding CSS property │ │ │ │ + * from the <cssMap>. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * sym - {String} A symbolizer property name. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A CSS property name or null if none found. │ │ │ │ + */ │ │ │ │ + getCssProperty: function(sym) { │ │ │ │ + var css = null; │ │ │ │ + for (var prop in this.cssMap) { │ │ │ │ + if (this.cssMap[prop] == sym) { │ │ │ │ + css = prop; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return css; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getGraphicFormat │ │ │ │ + * Given a href for an external graphic, try to determine the mime-type. │ │ │ │ + * This method doesn't try too hard, and will fall back to │ │ │ │ + * <defaultGraphicFormat> if one of the known <graphicFormats> is not │ │ │ │ + * the file extension of the provided href. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * href - {String} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The graphic format. │ │ │ │ + */ │ │ │ │ + getGraphicFormat: function(href) { │ │ │ │ + var format, regex; │ │ │ │ + for (var key in this.graphicFormats) { │ │ │ │ + if (this.graphicFormats[key].test(href)) { │ │ │ │ + format = key; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return format || this.defaultGraphicFormat; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: defaultGraphicFormat │ │ │ │ + * {String} If none other can be determined from <getGraphicFormat>, this │ │ │ │ + * default will be returned. │ │ │ │ + */ │ │ │ │ + defaultGraphicFormat: "image/png", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: graphicFormats │ │ │ │ + * {Object} Mapping of image mime-types to regular extensions matching │ │ │ │ + * well-known file extensions. │ │ │ │ + */ │ │ │ │ + graphicFormats: { │ │ │ │ + "image/jpeg": /\.jpe?g$/i, │ │ │ │ + "image/gif": /\.gif$/i, │ │ │ │ + "image/png": /\.png$/i │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: write │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * sld - {Object} An object representing the SLD. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The root of an SLD document. │ │ │ │ + */ │ │ │ │ + write: function(sld) { │ │ │ │ + return this.writers.sld.StyledLayerDescriptor.apply(this, [sld]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: writers │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ + * node names they produce. │ │ │ │ + */ │ │ │ │ + writers: OpenLayers.Util.applyDefaults({ │ │ │ │ + "sld": { │ │ │ │ + "_OGCExpression": function(nodeName, value) { │ │ │ │ + // only the simplest of ogc:expression handled │ │ │ │ + // {label: "some text and a ${propertyName}"} │ │ │ │ + var node = this.createElementNSPlus(nodeName); │ │ │ │ + var tokens = typeof value == "string" ? │ │ │ │ + value.split("${") : [value]; │ │ │ │ + node.appendChild(this.createTextNode(tokens[0])); │ │ │ │ + var item, last; │ │ │ │ + for (var i = 1, len = tokens.length; i < len; i++) { │ │ │ │ + item = tokens[i]; │ │ │ │ + last = item.indexOf("}"); │ │ │ │ + if (last > 0) { │ │ │ │ + this.writeNode( │ │ │ │ + "ogc:PropertyName", { │ │ │ │ + property: item.substring(0, last) │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + node.appendChild( │ │ │ │ + this.createTextNode(item.substring(++last)) │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + // no ending }, so this is a literal ${ │ │ │ │ + node.appendChild( │ │ │ │ + this.createTextNode("${" + item) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "StyledLayerDescriptor": function(sld) { │ │ │ │ + var root = this.createElementNSPlus( │ │ │ │ + "sld:StyledLayerDescriptor", { │ │ │ │ + attributes: { │ │ │ │ + "version": this.VERSION, │ │ │ │ + "xsi:schemaLocation": this.schemaLocation │ │ │ │ + } │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + │ │ │ │ + // For ArcGIS Server it is necessary to define this │ │ │ │ + // at the root level (see ticket:2166). │ │ │ │ + root.setAttribute("xmlns:ogc", this.namespaces.ogc); │ │ │ │ + root.setAttribute("xmlns:gml", this.namespaces.gml); │ │ │ │ + │ │ │ │ + // add in optional name │ │ │ │ + if (sld.name) { │ │ │ │ + this.writeNode("Name", sld.name, root); │ │ │ │ + } │ │ │ │ + // add in optional title │ │ │ │ + if (sld.title) { │ │ │ │ + this.writeNode("Title", sld.title, root); │ │ │ │ + } │ │ │ │ + // add in optional description │ │ │ │ + if (sld.description) { │ │ │ │ + this.writeNode("Abstract", sld.description, root); │ │ │ │ + } │ │ │ │ + // add in named layers │ │ │ │ + // allow namedLayers to be an array │ │ │ │ + if (OpenLayers.Util.isArray(sld.namedLayers)) { │ │ │ │ + for (var i = 0, len = sld.namedLayers.length; i < len; ++i) { │ │ │ │ + this.writeNode("NamedLayer", sld.namedLayers[i], root); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + for (var name in sld.namedLayers) { │ │ │ │ + this.writeNode("NamedLayer", sld.namedLayers[name], root); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return root; │ │ │ │ + }, │ │ │ │ + "Name": function(name) { │ │ │ │ + return this.createElementNSPlus("sld:Name", { │ │ │ │ + value: name │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "Title": function(title) { │ │ │ │ + return this.createElementNSPlus("sld:Title", { │ │ │ │ + value: title │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "Abstract": function(description) { │ │ │ │ + return this.createElementNSPlus( │ │ │ │ + "sld:Abstract", { │ │ │ │ + value: description │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + "NamedLayer": function(layer) { │ │ │ │ + var node = this.createElementNSPlus("sld:NamedLayer"); │ │ │ │ + │ │ │ │ + // add in required name │ │ │ │ + this.writeNode("Name", layer.name, node); │ │ │ │ + │ │ │ │ + // optional sld:LayerFeatureConstraints here │ │ │ │ + │ │ │ │ + // add in named styles │ │ │ │ + if (layer.namedStyles) { │ │ │ │ + for (var i = 0, len = layer.namedStyles.length; i < len; ++i) { │ │ │ │ + this.writeNode( │ │ │ │ + "NamedStyle", layer.namedStyles[i], node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // add in user styles │ │ │ │ + if (layer.userStyles) { │ │ │ │ + for (var i = 0, len = layer.userStyles.length; i < len; ++i) { │ │ │ │ + this.writeNode( │ │ │ │ + "UserStyle", layer.userStyles[i], node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "NamedStyle": function(name) { │ │ │ │ + var node = this.createElementNSPlus("sld:NamedStyle"); │ │ │ │ + this.writeNode("Name", name, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "UserStyle": function(style) { │ │ │ │ + var node = this.createElementNSPlus("sld:UserStyle"); │ │ │ │ + │ │ │ │ + // add in optional name │ │ │ │ + if (style.name) { │ │ │ │ + this.writeNode("Name", style.name, node); │ │ │ │ + } │ │ │ │ + // add in optional title │ │ │ │ + if (style.title) { │ │ │ │ + this.writeNode("Title", style.title, node); │ │ │ │ + } │ │ │ │ + // add in optional description │ │ │ │ + if (style.description) { │ │ │ │ + this.writeNode("Abstract", style.description, node); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // add isdefault │ │ │ │ + if (style.isDefault) { │ │ │ │ + this.writeNode("IsDefault", style.isDefault, node); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // add FeatureTypeStyles │ │ │ │ + if (this.multipleSymbolizers && style.rules) { │ │ │ │ + // group style objects by symbolizer zIndex │ │ │ │ + var rulesByZ = { │ │ │ │ + 0: [] │ │ │ │ + }; │ │ │ │ + var zValues = [0]; │ │ │ │ + var rule, ruleMap, symbolizer, zIndex, clone; │ │ │ │ + for (var i = 0, ii = style.rules.length; i < ii; ++i) { │ │ │ │ + rule = style.rules[i]; │ │ │ │ + if (rule.symbolizers) { │ │ │ │ + ruleMap = {}; │ │ │ │ + for (var j = 0, jj = rule.symbolizers.length; j < jj; ++j) { │ │ │ │ + symbolizer = rule.symbolizers[j]; │ │ │ │ + zIndex = symbolizer.zIndex; │ │ │ │ + if (!(zIndex in ruleMap)) { │ │ │ │ + clone = rule.clone(); │ │ │ │ + clone.symbolizers = []; │ │ │ │ + ruleMap[zIndex] = clone; │ │ │ │ + } │ │ │ │ + ruleMap[zIndex].symbolizers.push(symbolizer.clone()); │ │ │ │ + } │ │ │ │ + for (zIndex in ruleMap) { │ │ │ │ + if (!(zIndex in rulesByZ)) { │ │ │ │ + zValues.push(zIndex); │ │ │ │ + rulesByZ[zIndex] = []; │ │ │ │ + } │ │ │ │ + rulesByZ[zIndex].push(ruleMap[zIndex]); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + // no symbolizers in rule │ │ │ │ + rulesByZ[0].push(rule.clone()); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + // write one FeatureTypeStyle per zIndex │ │ │ │ + zValues.sort(); │ │ │ │ + var rules; │ │ │ │ + for (var i = 0, ii = zValues.length; i < ii; ++i) { │ │ │ │ + rules = rulesByZ[zValues[i]]; │ │ │ │ + if (rules.length > 0) { │ │ │ │ + clone = style.clone(); │ │ │ │ + clone.rules = rulesByZ[zValues[i]]; │ │ │ │ + this.writeNode("FeatureTypeStyle", clone, node); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.writeNode("FeatureTypeStyle", style, node); │ │ │ │ + } │ │ │ │ + │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "IsDefault": function(bool) { │ │ │ │ + return this.createElementNSPlus( │ │ │ │ + "sld:IsDefault", { │ │ │ │ + value: (bool) ? "1" : "0" │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + "FeatureTypeStyle": function(style) { │ │ │ │ + var node = this.createElementNSPlus("sld:FeatureTypeStyle"); │ │ │ │ + │ │ │ │ + // OpenLayers currently stores no Name, Title, Abstract, │ │ │ │ + // FeatureTypeName, or SemanticTypeIdentifier information │ │ │ │ + // related to FeatureTypeStyle │ │ │ │ + │ │ │ │ + // add in rules │ │ │ │ + for (var i = 0, len = style.rules.length; i < len; ++i) { │ │ │ │ + this.writeNode("Rule", style.rules[i], node); │ │ │ │ + } │ │ │ │ + │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Rule": function(rule) { │ │ │ │ + var node = this.createElementNSPlus("sld:Rule"); │ │ │ │ + │ │ │ │ + // add in optional name │ │ │ │ + if (rule.name) { │ │ │ │ + this.writeNode("Name", rule.name, node); │ │ │ │ + } │ │ │ │ + // add in optional title │ │ │ │ + if (rule.title) { │ │ │ │ + this.writeNode("Title", rule.title, node); │ │ │ │ + } │ │ │ │ + // add in optional description │ │ │ │ + if (rule.description) { │ │ │ │ + this.writeNode("Abstract", rule.description, node); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // add in LegendGraphic here │ │ │ │ + │ │ │ │ + // add in optional filters │ │ │ │ + if (rule.elseFilter) { │ │ │ │ + this.writeNode("ElseFilter", null, node); │ │ │ │ + } else if (rule.filter) { │ │ │ │ + this.writeNode("ogc:Filter", rule.filter, node); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // add in scale limits │ │ │ │ + if (rule.minScaleDenominator != undefined) { │ │ │ │ + this.writeNode( │ │ │ │ + "MinScaleDenominator", rule.minScaleDenominator, node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (rule.maxScaleDenominator != undefined) { │ │ │ │ + this.writeNode( │ │ │ │ + "MaxScaleDenominator", rule.maxScaleDenominator, node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var type, symbolizer; │ │ │ │ + if (this.multipleSymbolizers && rule.symbolizers) { │ │ │ │ + var symbolizer; │ │ │ │ + for (var i = 0, ii = rule.symbolizers.length; i < ii; ++i) { │ │ │ │ + symbolizer = rule.symbolizers[i]; │ │ │ │ + type = symbolizer.CLASS_NAME.split(".").pop(); │ │ │ │ + this.writeNode( │ │ │ │ + type + "Symbolizer", symbolizer, node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + // add in symbolizers (relies on geometry type keys) │ │ │ │ + var types = OpenLayers.Style.SYMBOLIZER_PREFIXES; │ │ │ │ + for (var i = 0, len = types.length; i < len; ++i) { │ │ │ │ + type = types[i]; │ │ │ │ + symbolizer = rule.symbolizer[type]; │ │ │ │ + if (symbolizer) { │ │ │ │ + this.writeNode( │ │ │ │ + type + "Symbolizer", symbolizer, node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + │ │ │ │ + }, │ │ │ │ + "ElseFilter": function() { │ │ │ │ + return this.createElementNSPlus("sld:ElseFilter"); │ │ │ │ + }, │ │ │ │ + "MinScaleDenominator": function(scale) { │ │ │ │ + return this.createElementNSPlus( │ │ │ │ + "sld:MinScaleDenominator", { │ │ │ │ + value: scale │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + "MaxScaleDenominator": function(scale) { │ │ │ │ + return this.createElementNSPlus( │ │ │ │ + "sld:MaxScaleDenominator", { │ │ │ │ + value: scale │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + "LineSymbolizer": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:LineSymbolizer"); │ │ │ │ + this.writeNode("Stroke", symbolizer, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Stroke": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:Stroke"); │ │ │ │ + │ │ │ │ + // GraphicFill here │ │ │ │ + // GraphicStroke here │ │ │ │ + │ │ │ │ + // add in CssParameters │ │ │ │ + if (symbolizer.strokeColor != undefined) { │ │ │ │ + this.writeNode( │ │ │ │ + "CssParameter", { │ │ │ │ + symbolizer: symbolizer, │ │ │ │ + key: "strokeColor" │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (symbolizer.strokeOpacity != undefined) { │ │ │ │ + this.writeNode( │ │ │ │ + "CssParameter", { │ │ │ │ + symbolizer: symbolizer, │ │ │ │ + key: "strokeOpacity" │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (symbolizer.strokeWidth != undefined) { │ │ │ │ + this.writeNode( │ │ │ │ + "CssParameter", { │ │ │ │ + symbolizer: symbolizer, │ │ │ │ + key: "strokeWidth" │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (symbolizer.strokeDashstyle != undefined && symbolizer.strokeDashstyle !== "solid") { │ │ │ │ + // assumes valid stroke-dasharray value │ │ │ │ + this.writeNode( │ │ │ │ + "CssParameter", { │ │ │ │ + symbolizer: symbolizer, │ │ │ │ + key: "strokeDashstyle" │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (symbolizer.strokeLinecap != undefined) { │ │ │ │ + this.writeNode( │ │ │ │ + "CssParameter", { │ │ │ │ + symbolizer: symbolizer, │ │ │ │ + key: "strokeLinecap" │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "CssParameter": function(obj) { │ │ │ │ + // not handling ogc:expressions for now │ │ │ │ + return this.createElementNSPlus("sld:CssParameter", { │ │ │ │ + attributes: { │ │ │ │ + name: this.getCssProperty(obj.key) │ │ │ │ + }, │ │ │ │ + value: obj.symbolizer[obj.key] │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "TextSymbolizer": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:TextSymbolizer"); │ │ │ │ + // add in optional Label │ │ │ │ + if (symbolizer.label != null) { │ │ │ │ + this.writeNode("Label", symbolizer.label, node); │ │ │ │ + } │ │ │ │ + // add in optional Font │ │ │ │ + if (symbolizer.fontFamily != null || │ │ │ │ + symbolizer.fontSize != null || │ │ │ │ + symbolizer.fontWeight != null || │ │ │ │ + symbolizer.fontStyle != null) { │ │ │ │ + this.writeNode("Font", symbolizer, node); │ │ │ │ + } │ │ │ │ + // add in optional LabelPlacement │ │ │ │ + if (symbolizer.labelAnchorPointX != null || │ │ │ │ + symbolizer.labelAnchorPointY != null || │ │ │ │ + symbolizer.labelAlign != null || │ │ │ │ + symbolizer.labelXOffset != null || │ │ │ │ + symbolizer.labelYOffset != null || │ │ │ │ + symbolizer.labelRotation != null || │ │ │ │ + symbolizer.labelPerpendicularOffset != null) { │ │ │ │ + this.writeNode("LabelPlacement", symbolizer, node); │ │ │ │ + } │ │ │ │ + // add in optional Halo │ │ │ │ + if (symbolizer.haloRadius != null || │ │ │ │ + symbolizer.haloColor != null || │ │ │ │ + symbolizer.haloOpacity != null) { │ │ │ │ + this.writeNode("Halo", symbolizer, node); │ │ │ │ + } │ │ │ │ + // add in optional Fill │ │ │ │ + if (symbolizer.fontColor != null || │ │ │ │ + symbolizer.fontOpacity != null) { │ │ │ │ + this.writeNode("Fill", { │ │ │ │ + fillColor: symbolizer.fontColor, │ │ │ │ + fillOpacity: symbolizer.fontOpacity │ │ │ │ + }, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "LabelPlacement": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:LabelPlacement"); │ │ │ │ + // PointPlacement and LinePlacement are choices, so don't output both │ │ │ │ + if ((symbolizer.labelAnchorPointX != null || │ │ │ │ + symbolizer.labelAnchorPointY != null || │ │ │ │ + symbolizer.labelAlign != null || │ │ │ │ + symbolizer.labelXOffset != null || │ │ │ │ + symbolizer.labelYOffset != null || │ │ │ │ + symbolizer.labelRotation != null) && │ │ │ │ + symbolizer.labelPerpendicularOffset == null) { │ │ │ │ + this.writeNode("PointPlacement", symbolizer, node); │ │ │ │ + } │ │ │ │ + if (symbolizer.labelPerpendicularOffset != null) { │ │ │ │ + this.writeNode("LinePlacement", symbolizer, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "LinePlacement": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:LinePlacement"); │ │ │ │ + this.writeNode("PerpendicularOffset", symbolizer.labelPerpendicularOffset, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "PerpendicularOffset": function(value) { │ │ │ │ + return this.createElementNSPlus("sld:PerpendicularOffset", { │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "PointPlacement": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:PointPlacement"); │ │ │ │ + if (symbolizer.labelAnchorPointX != null || │ │ │ │ + symbolizer.labelAnchorPointY != null || │ │ │ │ + symbolizer.labelAlign != null) { │ │ │ │ + this.writeNode("AnchorPoint", symbolizer, node); │ │ │ │ + } │ │ │ │ + if (symbolizer.labelXOffset != null || │ │ │ │ + symbolizer.labelYOffset != null) { │ │ │ │ + this.writeNode("Displacement", symbolizer, node); │ │ │ │ + } │ │ │ │ + if (symbolizer.labelRotation != null) { │ │ │ │ + this.writeNode("Rotation", symbolizer.labelRotation, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "AnchorPoint": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:AnchorPoint"); │ │ │ │ + var x = symbolizer.labelAnchorPointX, │ │ │ │ + y = symbolizer.labelAnchorPointY; │ │ │ │ + if (x != null) { │ │ │ │ + this.writeNode("AnchorPointX", x, node); │ │ │ │ + } │ │ │ │ + if (y != null) { │ │ │ │ + this.writeNode("AnchorPointY", y, node); │ │ │ │ + } │ │ │ │ + if (x == null && y == null) { │ │ │ │ + var xAlign = symbolizer.labelAlign.substr(0, 1), │ │ │ │ + yAlign = symbolizer.labelAlign.substr(1, 1); │ │ │ │ + if (xAlign === "l") { │ │ │ │ + x = 0; │ │ │ │ + } else if (xAlign === "c") { │ │ │ │ + x = 0.5; │ │ │ │ + } else if (xAlign === "r") { │ │ │ │ + x = 1; │ │ │ │ + } │ │ │ │ + if (yAlign === "b") { │ │ │ │ + y = 0; │ │ │ │ + } else if (yAlign === "m") { │ │ │ │ + y = 0.5; │ │ │ │ + } else if (yAlign === "t") { │ │ │ │ + y = 1; │ │ │ │ + } │ │ │ │ + this.writeNode("AnchorPointX", x, node); │ │ │ │ + this.writeNode("AnchorPointY", y, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "AnchorPointX": function(value) { │ │ │ │ + return this.createElementNSPlus("sld:AnchorPointX", { │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "AnchorPointY": function(value) { │ │ │ │ + return this.createElementNSPlus("sld:AnchorPointY", { │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "Displacement": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:Displacement"); │ │ │ │ + if (symbolizer.labelXOffset != null) { │ │ │ │ + this.writeNode("DisplacementX", symbolizer.labelXOffset, node); │ │ │ │ + } │ │ │ │ + if (symbolizer.labelYOffset != null) { │ │ │ │ + this.writeNode("DisplacementY", symbolizer.labelYOffset, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "DisplacementX": function(value) { │ │ │ │ + return this.createElementNSPlus("sld:DisplacementX", { │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "DisplacementY": function(value) { │ │ │ │ + return this.createElementNSPlus("sld:DisplacementY", { │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "Font": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:Font"); │ │ │ │ + // add in CssParameters │ │ │ │ + if (symbolizer.fontFamily) { │ │ │ │ + this.writeNode( │ │ │ │ + "CssParameter", { │ │ │ │ + symbolizer: symbolizer, │ │ │ │ + key: "fontFamily" │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (symbolizer.fontSize) { │ │ │ │ + this.writeNode( │ │ │ │ + "CssParameter", { │ │ │ │ + symbolizer: symbolizer, │ │ │ │ + key: "fontSize" │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (symbolizer.fontWeight) { │ │ │ │ + this.writeNode( │ │ │ │ + "CssParameter", { │ │ │ │ + symbolizer: symbolizer, │ │ │ │ + key: "fontWeight" │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (symbolizer.fontStyle) { │ │ │ │ + this.writeNode( │ │ │ │ + "CssParameter", { │ │ │ │ + symbolizer: symbolizer, │ │ │ │ + key: "fontStyle" │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Label": function(label) { │ │ │ │ + return this.writers.sld._OGCExpression.call( │ │ │ │ + this, "sld:Label", label │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + "Halo": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:Halo"); │ │ │ │ + if (symbolizer.haloRadius) { │ │ │ │ + this.writeNode("Radius", symbolizer.haloRadius, node); │ │ │ │ + } │ │ │ │ + if (symbolizer.haloColor || symbolizer.haloOpacity) { │ │ │ │ + this.writeNode("Fill", { │ │ │ │ + fillColor: symbolizer.haloColor, │ │ │ │ + fillOpacity: symbolizer.haloOpacity │ │ │ │ + }, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Radius": function(value) { │ │ │ │ + return this.createElementNSPlus("sld:Radius", { │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "RasterSymbolizer": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:RasterSymbolizer"); │ │ │ │ + if (symbolizer.geometry) { │ │ │ │ + this.writeNode("Geometry", symbolizer.geometry, node); │ │ │ │ + } │ │ │ │ + if (symbolizer.opacity) { │ │ │ │ + this.writeNode("Opacity", symbolizer.opacity, node); │ │ │ │ + } │ │ │ │ + if (symbolizer.colorMap) { │ │ │ │ + this.writeNode("ColorMap", symbolizer.colorMap, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Geometry": function(geometry) { │ │ │ │ + var node = this.createElementNSPlus("sld:Geometry"); │ │ │ │ + if (geometry.property) { │ │ │ │ + this.writeNode("ogc:PropertyName", geometry, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "ColorMap": function(colorMap) { │ │ │ │ + var node = this.createElementNSPlus("sld:ColorMap"); │ │ │ │ + for (var i = 0, len = colorMap.length; i < len; ++i) { │ │ │ │ + this.writeNode("ColorMapEntry", colorMap[i], node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "ColorMapEntry": function(colorMapEntry) { │ │ │ │ + var node = this.createElementNSPlus("sld:ColorMapEntry"); │ │ │ │ + var a = colorMapEntry; │ │ │ │ + node.setAttribute("color", a.color); │ │ │ │ + a.opacity !== undefined && node.setAttribute("opacity", │ │ │ │ + parseFloat(a.opacity)); │ │ │ │ + a.quantity !== undefined && node.setAttribute("quantity", │ │ │ │ + parseFloat(a.quantity)); │ │ │ │ + a.label !== undefined && node.setAttribute("label", a.label); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "PolygonSymbolizer": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:PolygonSymbolizer"); │ │ │ │ + if (symbolizer.fill !== false) { │ │ │ │ + this.writeNode("Fill", symbolizer, node); │ │ │ │ + } │ │ │ │ + if (symbolizer.stroke !== false) { │ │ │ │ + this.writeNode("Stroke", symbolizer, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Fill": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:Fill"); │ │ │ │ + │ │ │ │ + // GraphicFill here │ │ │ │ + │ │ │ │ + // add in CssParameters │ │ │ │ + if (symbolizer.fillColor) { │ │ │ │ + this.writeNode( │ │ │ │ + "CssParameter", { │ │ │ │ + symbolizer: symbolizer, │ │ │ │ + key: "fillColor" │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (symbolizer.fillOpacity != null) { │ │ │ │ + this.writeNode( │ │ │ │ + "CssParameter", { │ │ │ │ + symbolizer: symbolizer, │ │ │ │ + key: "fillOpacity" │ │ │ │ + }, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "PointSymbolizer": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:PointSymbolizer"); │ │ │ │ + this.writeNode("Graphic", symbolizer, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Graphic": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:Graphic"); │ │ │ │ + if (symbolizer.externalGraphic != undefined) { │ │ │ │ + this.writeNode("ExternalGraphic", symbolizer, node); │ │ │ │ + } else { │ │ │ │ + this.writeNode("Mark", symbolizer, node); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (symbolizer.graphicOpacity != undefined) { │ │ │ │ + this.writeNode("Opacity", symbolizer.graphicOpacity, node); │ │ │ │ + } │ │ │ │ + if (symbolizer.pointRadius != undefined) { │ │ │ │ + this.writeNode("Size", symbolizer.pointRadius * 2, node); │ │ │ │ + } else if (symbolizer.graphicWidth != undefined) { │ │ │ │ + this.writeNode("Size", symbolizer.graphicWidth, node); │ │ │ │ + } │ │ │ │ + if (symbolizer.rotation != undefined) { │ │ │ │ + this.writeNode("Rotation", symbolizer.rotation, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "ExternalGraphic": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:ExternalGraphic"); │ │ │ │ + this.writeNode( │ │ │ │ + "OnlineResource", symbolizer.externalGraphic, node │ │ │ │ + ); │ │ │ │ + var format = symbolizer.graphicFormat || │ │ │ │ + this.getGraphicFormat(symbolizer.externalGraphic); │ │ │ │ + this.writeNode("Format", format, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Mark": function(symbolizer) { │ │ │ │ + var node = this.createElementNSPlus("sld:Mark"); │ │ │ │ + if (symbolizer.graphicName) { │ │ │ │ + this.writeNode("WellKnownName", symbolizer.graphicName, node); │ │ │ │ + } │ │ │ │ + if (symbolizer.fill !== false) { │ │ │ │ + this.writeNode("Fill", symbolizer, node); │ │ │ │ + } │ │ │ │ + if (symbolizer.stroke !== false) { │ │ │ │ + this.writeNode("Stroke", symbolizer, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "WellKnownName": function(name) { │ │ │ │ + return this.createElementNSPlus("sld:WellKnownName", { │ │ │ │ + value: name │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "Opacity": function(value) { │ │ │ │ + return this.createElementNSPlus("sld:Opacity", { │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "Size": function(value) { │ │ │ │ + return this.writers.sld._OGCExpression.call( │ │ │ │ + this, "sld:Size", value │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + "Rotation": function(value) { │ │ │ │ + return this.createElementNSPlus("sld:Rotation", { │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "OnlineResource": function(href) { │ │ │ │ + return this.createElementNSPlus("sld:OnlineResource", { │ │ │ │ + attributes: { │ │ │ │ + "xlink:type": "simple", │ │ │ │ + "xlink:href": href │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "Format": function(format) { │ │ │ │ + return this.createElementNSPlus("sld:Format", { │ │ │ │ + value: format │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.Filter.v1_0_0.prototype.writers), │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.SLD.v1" │ │ │ │ + │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/SLD/v1_0_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/SLD/v1.js │ │ │ │ + * @requires OpenLayers/Format/Filter/v1_0_0.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.SLD.v1_0_0 │ │ │ │ + * Write SLD version 1.0.0. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.SLD.v1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.SLD.v1_0_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.SLD.v1, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: VERSION │ │ │ │ + * {String} 1.0.0 │ │ │ │ + */ │ │ │ │ + VERSION: "1.0.0", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} http://www.opengis.net/sld │ │ │ │ + * http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd │ │ │ │ + */ │ │ │ │ + schemaLocation: "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.SLD.v1_0_0 │ │ │ │ + * Instances of this class are not created directly. Use the │ │ │ │ + * <OpenLayers.Format.SLD> constructor instead. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.SLD.v1_0_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/OWSContext/v0_3_1.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Format/KML.js │ │ │ │ + * @requires OpenLayers/Format/GML.js │ │ │ │ + * @requires OpenLayers/Format/GML/v2.js │ │ │ │ + * @requires OpenLayers/Format/SLD/v1_0_0.js │ │ │ │ + * @requires OpenLayers/Format/OWSContext.js │ │ │ │ + * @requires OpenLayers/Format/OWSCommon/v1_0_0.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.OWSContext.v0_3_1 │ │ │ │ + * Read and write OWSContext version 0.3.1. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.OWSContext.v0_3_1 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + */ │ │ │ │ + namespaces: { │ │ │ │ + owc: "http://www.opengis.net/ows-context", │ │ │ │ + gml: "http://www.opengis.net/gml", │ │ │ │ + kml: "http://www.opengis.net/kml/2.2", │ │ │ │ + ogc: "http://www.opengis.net/ogc", │ │ │ │ + ows: "http://www.opengis.net/ows", │ │ │ │ + sld: "http://www.opengis.net/sld", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: VERSION │ │ │ │ + * {String} 0.3.1 │ │ │ │ + */ │ │ │ │ + VERSION: "0.3.1", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} Schema location │ │ │ │ + */ │ │ │ │ + schemaLocation: "http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: defaultPrefix │ │ │ │ + * {String} Default namespace prefix to use. │ │ │ │ + */ │ │ │ │ + defaultPrefix: "owc", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: extractAttributes │ │ │ │ + * {Boolean} Extract attributes from GML. Default is true. │ │ │ │ + */ │ │ │ │ + extractAttributes: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: xy │ │ │ │ + * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x) │ │ │ │ + * Changing is not recommended, a new Format should be instantiated. │ │ │ │ + */ │ │ │ │ + xy: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: regExes │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ + */ │ │ │ │ + regExes: { │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: featureNS │ │ │ │ + * {String} The namespace uri to use for writing InlineGeometry │ │ │ │ + */ │ │ │ │ + featureNS: "http://mapserver.gis.umn.edu/mapserver", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: featureType │ │ │ │ + * {String} The name to use as the feature type when writing out │ │ │ │ + * InlineGeometry │ │ │ │ + */ │ │ │ │ + featureType: 'vector', │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: geometryName │ │ │ │ + * {String} The name to use for the geometry attribute when writing out │ │ │ │ + * InlineGeometry │ │ │ │ + */ │ │ │ │ + geometryName: 'geometry', │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: nestingLayerLookup │ │ │ │ + * {Object} Hashtable lookup for nesting layer nodes. Used while writing │ │ │ │ + * the OWS context document. It is necessary to keep track of the │ │ │ │ + * nestingPaths for which nesting layer nodes have already been │ │ │ │ + * created, so (nesting) layer nodes are added to those nodes. │ │ │ │ + * │ │ │ │ + * For example: │ │ │ │ + * │ │ │ │ + * If there are three layers with nestingPaths: │ │ │ │ + * layer1.metadata.nestingPath = "a/b/" │ │ │ │ + * layer2.metadata.nestingPath = "a/b/" │ │ │ │ + * layer2.metadata.nestingPath = "a/c" │ │ │ │ + * │ │ │ │ + * then a nesting layer node "a" should be created once and added │ │ │ │ + * to the resource list, a nesting layer node "b" should be created │ │ │ │ + * once and added under "a", and a nesting layer node "c" should be │ │ │ │ + * created and added under "a". The lookup paths for these nodes │ │ │ │ + * will be "a", "a/b", and "a/c" respectively. │ │ │ │ + */ │ │ │ │ + nestingLayerLookup: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.OWSContext.v0_3_1 │ │ │ │ + * Instances of this class are not created directly. Use the │ │ │ │ + * <OpenLayers.Format.OWSContext> constructor instead. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ + OpenLayers.Format.GML.v2.prototype.setGeometryTypes.call(this); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setNestingPath │ │ │ │ + * Set the nestingPath property of the layer depending on the position │ │ │ │ + * of the layer in hierarchy of layers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * l - {Object} An object that may have a layersContext array property. │ │ │ │ + * │ │ │ │ + */ │ │ │ │ + setNestingPath: function(l) { │ │ │ │ + if (l.layersContext) { │ │ │ │ + for (var i = 0, len = l.layersContext.length; i < len; i++) { │ │ │ │ + var layerContext = l.layersContext[i]; │ │ │ │ + var nPath = []; │ │ │ │ + var nTitle = l.title || ""; │ │ │ │ + if (l.metadata && l.metadata.nestingPath) { │ │ │ │ + nPath = l.metadata.nestingPath.slice(); │ │ │ │ + } │ │ │ │ + if (nTitle != "") { │ │ │ │ + nPath.push(nTitle); │ │ │ │ + } │ │ │ │ + layerContext.metadata.nestingPath = nPath; │ │ │ │ + if (layerContext.layersContext) { │ │ │ │ + this.setNestingPath(layerContext); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: decomposeNestingPath │ │ │ │ + * Takes a nestingPath like "a/b/c" and decomposes it into subpaths: │ │ │ │ + * "a", "a/b", "a/b/c" │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * nPath - {Array} the nesting path │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * Array({String}) Array with subpaths, or empty array if there is nothing │ │ │ │ + * to decompose │ │ │ │ + */ │ │ │ │ + decomposeNestingPath: function(nPath) { │ │ │ │ + var a = []; │ │ │ │ + if (OpenLayers.Util.isArray(nPath)) { │ │ │ │ + var path = nPath.slice(); │ │ │ │ + while (path.length > 0) { │ │ │ │ + a.push(path.slice()); │ │ │ │ + path.pop(); │ │ │ │ + } │ │ │ │ + a.reverse(); │ │ │ │ + } │ │ │ │ + return a; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read OWS context data from a string or DOMElement, and return a list │ │ │ │ + * of layers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} The context object with a flat layer list as a property named │ │ │ │ + * layersContext. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ + } │ │ │ │ + var context = {}; │ │ │ │ + this.readNode(data, context); │ │ │ │ + // since an OWSContext can be nested we need to go through this │ │ │ │ + // structure recursively │ │ │ │ + this.setNestingPath({ │ │ │ │ + layersContext: context.layersContext │ │ │ │ + }); │ │ │ │ + // after nesting path has been set, create a flat list of layers │ │ │ │ + var layers = []; │ │ │ │ + this.processLayer(layers, context); │ │ │ │ + delete context.layersContext; │ │ │ │ + context.layersContext = layers; │ │ │ │ + return context; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: processLayer │ │ │ │ + * Recursive function to get back a flat list of layers from the hierarchic │ │ │ │ + * layer structure. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layerArray - {Array({Object})} Array of layerContext objects │ │ │ │ + * layer - {Object} layerContext object │ │ │ │ + */ │ │ │ │ + processLayer: function(layerArray, layer) { │ │ │ │ + if (layer.layersContext) { │ │ │ │ + for (var i = 0, len = layer.layersContext.length; i < len; i++) { │ │ │ │ + var l = layer.layersContext[i]; │ │ │ │ + layerArray.push(l); │ │ │ │ + if (l.layersContext) { │ │ │ │ + this.processLayer(layerArray, l); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: write │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * context - {Object} An object representing the map context. │ │ │ │ + * options - {Object} Optional object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} An OWS Context document string. │ │ │ │ + */ │ │ │ │ + write: function(context, options) { │ │ │ │ + var name = "OWSContext"; │ │ │ │ + this.nestingLayerLookup = {}; //start with empty lookup │ │ │ │ + options = options || {}; │ │ │ │ + OpenLayers.Util.applyDefaults(options, context); │ │ │ │ + var root = this.writeNode(name, options); │ │ │ │ + this.nestingLayerLookup = null; //clear lookup │ │ │ │ + this.setAttributeNS( │ │ │ │ + root, this.namespaces["xsi"], │ │ │ │ + "xsi:schemaLocation", this.schemaLocation │ │ │ │ + ); │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [root]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "kml": { │ │ │ │ + "Document": function(node, obj) { │ │ │ │ + obj.features = new OpenLayers.Format.KML({ │ │ │ │ + kmlns: this.namespaces.kml, │ │ │ │ + extractStyles: true │ │ │ │ + }).read(node); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "owc": { │ │ │ │ + "OWSContext": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "General": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "ResourceList": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "Layer": function(node, obj) { │ │ │ │ + var layerContext = { │ │ │ │ + metadata: {}, │ │ │ │ + visibility: (node.getAttribute("hidden") != "1"), │ │ │ │ + queryable: (node.getAttribute("queryable") == "1"), │ │ │ │ + opacity: ((node.getAttribute("opacity") != null) ? │ │ │ │ + parseFloat(node.getAttribute("opacity")) : null), │ │ │ │ + name: node.getAttribute("name"), │ │ │ │ + /* A category layer is a dummy layer meant for creating │ │ │ │ + hierarchies. It is not a physical layer in the │ │ │ │ + OpenLayers sense. The assumption we make here is that │ │ │ │ + category layers do not have a name attribute */ │ │ │ │ + categoryLayer: (node.getAttribute("name") == null), │ │ │ │ + formats: [], │ │ │ │ + styles: [] │ │ │ │ + }; │ │ │ │ + if (!obj.layersContext) { │ │ │ │ + obj.layersContext = []; │ │ │ │ + } │ │ │ │ + obj.layersContext.push(layerContext); │ │ │ │ + this.readChildNodes(node, layerContext); │ │ │ │ + }, │ │ │ │ + "InlineGeometry": function(node, obj) { │ │ │ │ + obj.features = []; │ │ │ │ + var elements = this.getElementsByTagNameNS(node, │ │ │ │ + this.namespaces.gml, "featureMember"); │ │ │ │ + var el; │ │ │ │ + if (elements.length >= 1) { │ │ │ │ + el = elements[0]; │ │ │ │ + } │ │ │ │ + if (el && el.firstChild) { │ │ │ │ + var featurenode = (el.firstChild.nextSibling) ? │ │ │ │ + el.firstChild.nextSibling : el.firstChild; │ │ │ │ + this.setNamespace("feature", featurenode.namespaceURI); │ │ │ │ + this.featureType = featurenode.localName || │ │ │ │ + featurenode.nodeName.split(":").pop(); │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Server": function(node, obj) { │ │ │ │ + // when having multiple Server types, we prefer WMS │ │ │ │ + if ((!obj.service && !obj.version) || │ │ │ │ + (obj.service != │ │ │ │ + OpenLayers.Format.Context.serviceTypes.WMS)) { │ │ │ │ + obj.service = node.getAttribute("service"); │ │ │ │ + obj.version = node.getAttribute("version"); │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Name": function(node, obj) { │ │ │ │ + obj.name = this.getChildValue(node); │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "Title": function(node, obj) { │ │ │ │ + obj.title = this.getChildValue(node); │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "StyleList": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj.styles); │ │ │ │ + }, │ │ │ │ + "Style": function(node, obj) { │ │ │ │ + var style = {}; │ │ │ │ + obj.push(style); │ │ │ │ + this.readChildNodes(node, style); │ │ │ │ + }, │ │ │ │ + "LegendURL": function(node, obj) { │ │ │ │ + var legend = {}; │ │ │ │ + obj.legend = legend; │ │ │ │ + this.readChildNodes(node, legend); │ │ │ │ + }, │ │ │ │ + "OnlineResource": function(node, obj) { │ │ │ │ + obj.url = this.getAttributeNS(node, this.namespaces.xlink, │ │ │ │ + "href"); │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers.ows, │ │ │ │ + "gml": OpenLayers.Format.GML.v2.prototype.readers.gml, │ │ │ │ + "sld": OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld, │ │ │ │ + "feature": OpenLayers.Format.GML.v2.prototype.readers.feature │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: writers │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ + * node names they produce. │ │ │ │ + */ │ │ │ │ + writers: { │ │ │ │ + "owc": { │ │ │ │ + "OWSContext": function(options) { │ │ │ │ + var node = this.createElementNSPlus("OWSContext", { │ │ │ │ + attributes: { │ │ │ │ + version: this.VERSION, │ │ │ │ + id: options.id || OpenLayers.Util.createUniqueID("OpenLayers_OWSContext_") │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + this.writeNode("General", options, node); │ │ │ │ + this.writeNode("ResourceList", options, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "General": function(options) { │ │ │ │ + var node = this.createElementNSPlus("General"); │ │ │ │ + this.writeNode("ows:BoundingBox", options, node); │ │ │ │ + this.writeNode("ows:Title", options.title || 'OpenLayers OWSContext', node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "ResourceList": function(options) { │ │ │ │ + var node = this.createElementNSPlus("ResourceList"); │ │ │ │ + for (var i = 0, len = options.layers.length; i < len; i++) { │ │ │ │ + var layer = options.layers[i]; │ │ │ │ + var decomposedPath = this.decomposeNestingPath(layer.metadata.nestingPath); │ │ │ │ + this.writeNode("_Layer", { │ │ │ │ + layer: layer, │ │ │ │ + subPaths: decomposedPath │ │ │ │ + }, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Server": function(options) { │ │ │ │ + var node = this.createElementNSPlus("Server", { │ │ │ │ + attributes: { │ │ │ │ + version: options.version, │ │ │ │ + service: options.service │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + this.writeNode("OnlineResource", options, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "OnlineResource": function(options) { │ │ │ │ + var node = this.createElementNSPlus("OnlineResource", { │ │ │ │ + attributes: { │ │ │ │ + "xlink:href": options.url │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "InlineGeometry": function(layer) { │ │ │ │ + var node = this.createElementNSPlus("InlineGeometry"), │ │ │ │ + dataExtent = layer.getDataExtent(); │ │ │ │ + if (dataExtent !== null) { │ │ │ │ + this.writeNode("gml:boundedBy", dataExtent, node); │ │ │ │ + } │ │ │ │ + for (var i = 0, len = layer.features.length; i < len; i++) { │ │ │ │ + this.writeNode("gml:featureMember", layer.features[i], node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "StyleList": function(styles) { │ │ │ │ + var node = this.createElementNSPlus("StyleList"); │ │ │ │ + for (var i = 0, len = styles.length; i < len; i++) { │ │ │ │ + this.writeNode("Style", styles[i], node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Style": function(style) { │ │ │ │ + var node = this.createElementNSPlus("Style"); │ │ │ │ + this.writeNode("Name", style, node); │ │ │ │ + this.writeNode("Title", style, node); │ │ │ │ + if (style.legend) { │ │ │ │ + this.writeNode("LegendURL", style, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Name": function(obj) { │ │ │ │ + var node = this.createElementNSPlus("Name", { │ │ │ │ + value: obj.name │ │ │ │ + }); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Title": function(obj) { │ │ │ │ + var node = this.createElementNSPlus("Title", { │ │ │ │ + value: obj.title │ │ │ │ + }); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "LegendURL": function(style) { │ │ │ │ + var node = this.createElementNSPlus("LegendURL"); │ │ │ │ + this.writeNode("OnlineResource", style.legend, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "_WMS": function(layer) { │ │ │ │ + var node = this.createElementNSPlus("Layer", { │ │ │ │ + attributes: { │ │ │ │ + name: layer.params.LAYERS, │ │ │ │ + queryable: layer.queryable ? "1" : "0", │ │ │ │ + hidden: layer.visibility ? "0" : "1", │ │ │ │ + opacity: layer.hasOwnProperty("opacity") ? layer.opacity : null │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + this.writeNode("ows:Title", layer.name, node); │ │ │ │ + this.writeNode("ows:OutputFormat", layer.params.FORMAT, node); │ │ │ │ + this.writeNode("Server", { │ │ │ │ + service: OpenLayers.Format.Context.serviceTypes.WMS, │ │ │ │ + version: layer.params.VERSION, │ │ │ │ + url: layer.url │ │ │ │ + }, node); │ │ │ │ + if (layer.metadata.styles && layer.metadata.styles.length > 0) { │ │ │ │ + this.writeNode("StyleList", layer.metadata.styles, node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "_Layer": function(options) { │ │ │ │ + var layer, subPaths, node, title; │ │ │ │ + layer = options.layer; │ │ │ │ + subPaths = options.subPaths; │ │ │ │ + node = null; │ │ │ │ + title = null; │ │ │ │ + // subPaths is an array of an array │ │ │ │ + // recursively calling _Layer writer eats up subPaths, until a │ │ │ │ + // real writer is called and nodes are returned. │ │ │ │ + if (subPaths.length > 0) { │ │ │ │ + var path = subPaths[0].join("/"); │ │ │ │ + var index = path.lastIndexOf("/"); │ │ │ │ + node = this.nestingLayerLookup[path]; │ │ │ │ + title = (index > 0) ? path.substring(index + 1, path.length) : path; │ │ │ │ + if (!node) { │ │ │ │ + // category layer │ │ │ │ + node = this.createElementNSPlus("Layer"); │ │ │ │ + this.writeNode("ows:Title", title, node); │ │ │ │ + this.nestingLayerLookup[path] = node; │ │ │ │ + } │ │ │ │ + options.subPaths.shift(); //remove a path after each call │ │ │ │ + this.writeNode("_Layer", options, node); │ │ │ │ + return node; │ │ │ │ + } else { │ │ │ │ + // write out the actual layer │ │ │ │ + if (layer instanceof OpenLayers.Layer.WMS) { │ │ │ │ + node = this.writeNode("_WMS", layer); │ │ │ │ + } else if (layer instanceof OpenLayers.Layer.Vector) { │ │ │ │ + if (layer.protocol instanceof OpenLayers.Protocol.WFS.v1) { │ │ │ │ + node = this.writeNode("_WFS", layer); │ │ │ │ + } else if (layer.protocol instanceof OpenLayers.Protocol.HTTP) { │ │ │ │ + if (layer.protocol.format instanceof OpenLayers.Format.GML) { │ │ │ │ + layer.protocol.format.version = "2.1.2"; │ │ │ │ + node = this.writeNode("_GML", layer); │ │ │ │ + } else if (layer.protocol.format instanceof OpenLayers.Format.KML) { │ │ │ │ + layer.protocol.format.version = "2.2"; │ │ │ │ + node = this.writeNode("_KML", layer); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + // write out as inline GML since we have no idea │ │ │ │ + // about the original Format │ │ │ │ + this.setNamespace("feature", this.featureNS); │ │ │ │ + node = this.writeNode("_InlineGeometry", layer); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (layer.options.maxScale) { │ │ │ │ + this.writeNode("sld:MinScaleDenominator", │ │ │ │ + layer.options.maxScale, node); │ │ │ │ + } │ │ │ │ + if (layer.options.minScale) { │ │ │ │ + this.writeNode("sld:MaxScaleDenominator", │ │ │ │ + layer.options.minScale, node); │ │ │ │ + } │ │ │ │ + this.nestingLayerLookup[layer.name] = node; │ │ │ │ + return node; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "_WFS": function(layer) { │ │ │ │ + var node = this.createElementNSPlus("Layer", { │ │ │ │ + attributes: { │ │ │ │ + name: layer.protocol.featurePrefix + ":" + layer.protocol.featureType, │ │ │ │ + hidden: layer.visibility ? "0" : "1" │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + this.writeNode("ows:Title", layer.name, node); │ │ │ │ + this.writeNode("Server", { │ │ │ │ + service: OpenLayers.Format.Context.serviceTypes.WFS, │ │ │ │ + version: layer.protocol.version, │ │ │ │ + url: layer.protocol.url │ │ │ │ + }, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "_InlineGeometry": function(layer) { │ │ │ │ + var node = this.createElementNSPlus("Layer", { │ │ │ │ + attributes: { │ │ │ │ + name: this.featureType, │ │ │ │ + hidden: layer.visibility ? "0" : "1" │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + this.writeNode("ows:Title", layer.name, node); │ │ │ │ + this.writeNode("InlineGeometry", layer, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "_GML": function(layer) { │ │ │ │ + var node = this.createElementNSPlus("Layer"); │ │ │ │ + this.writeNode("ows:Title", layer.name, node); │ │ │ │ + this.writeNode("Server", { │ │ │ │ + service: OpenLayers.Format.Context.serviceTypes.GML, │ │ │ │ + url: layer.protocol.url, │ │ │ │ + version: layer.protocol.format.version │ │ │ │ + }, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "_KML": function(layer) { │ │ │ │ + var node = this.createElementNSPlus("Layer"); │ │ │ │ + this.writeNode("ows:Title", layer.name, node); │ │ │ │ + this.writeNode("Server", { │ │ │ │ + service: OpenLayers.Format.Context.serviceTypes.KML, │ │ │ │ + version: layer.protocol.format.version, │ │ │ │ + url: layer.protocol.url │ │ │ │ + }, node); │ │ │ │ + return node; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ + "boundedBy": function(bounds) { │ │ │ │ + var node = this.createElementNSPlus("gml:boundedBy"); │ │ │ │ + this.writeNode("gml:Box", bounds, node); │ │ │ │ + return node; │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.GML.v2.prototype.writers.gml), │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.writers.ows, │ │ │ │ + "sld": OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld, │ │ │ │ + "feature": OpenLayers.Format.GML.v2.prototype.writers.feature │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.OWSContext.v0_3_1" │ │ │ │ + │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/CSWGetDomain/v2_0_2.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Format/CSWGetDomain.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.CSWGetDomain.v2_0_2 │ │ │ │ + * A format for creating CSWGetDomain v2.0.2 transactions. │ │ │ │ + * Create a new instance with the │ │ │ │ + * <OpenLayers.Format.CSWGetDomain.v2_0_2> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.CSWGetDomain.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + */ │ │ │ │ + namespaces: { │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance", │ │ │ │ + csw: "http://www.opengis.net/cat/csw/2.0.2" │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: defaultPrefix │ │ │ │ + * {String} The default prefix (used by Format.XML). │ │ │ │ + */ │ │ │ │ + defaultPrefix: "csw", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: version │ │ │ │ + * {String} CSW version number. │ │ │ │ + */ │ │ │ │ + version: "2.0.2", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} http://www.opengis.net/cat/csw/2.0.2 │ │ │ │ + * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd │ │ │ │ + */ │ │ │ │ + schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: PropertyName │ │ │ │ + * {String} Value of the csw:PropertyName element, used when │ │ │ │ + * writing a GetDomain document. │ │ │ │ + */ │ │ │ │ + PropertyName: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: ParameterName │ │ │ │ + * {String} Value of the csw:ParameterName element, used when │ │ │ │ + * writing a GetDomain document. │ │ │ │ + */ │ │ │ │ + ParameterName: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.CSWGetDomain.v2_0_2 │ │ │ │ + * A class for parsing and generating CSWGetDomain v2.0.2 transactions. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ + * │ │ │ │ + * Valid options properties: │ │ │ │ + * - PropertyName │ │ │ │ + * - ParameterName │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Parse the response from a GetDomain request. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ + } │ │ │ │ + var obj = {}; │ │ │ │ + this.readNode(data, obj); │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "csw": { │ │ │ │ + "GetDomainResponse": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "DomainValues": function(node, obj) { │ │ │ │ + if (!(OpenLayers.Util.isArray(obj.DomainValues))) { │ │ │ │ + obj.DomainValues = []; │ │ │ │ + } │ │ │ │ + var attrs = node.attributes; │ │ │ │ + var domainValue = {}; │ │ │ │ + for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ + domainValue[attrs[i].name] = attrs[i].nodeValue; │ │ │ │ + } │ │ │ │ + this.readChildNodes(node, domainValue); │ │ │ │ + obj.DomainValues.push(domainValue); │ │ │ │ + }, │ │ │ │ + "PropertyName": function(node, obj) { │ │ │ │ + obj.PropertyName = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "ParameterName": function(node, obj) { │ │ │ │ + obj.ParameterName = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "ListOfValues": function(node, obj) { │ │ │ │ + if (!(OpenLayers.Util.isArray(obj.ListOfValues))) { │ │ │ │ + obj.ListOfValues = []; │ │ │ │ + } │ │ │ │ + this.readChildNodes(node, obj.ListOfValues); │ │ │ │ + }, │ │ │ │ + "Value": function(node, obj) { │ │ │ │ + var attrs = node.attributes; │ │ │ │ + var value = {}; │ │ │ │ + for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ + value[attrs[i].name] = attrs[i].nodeValue; │ │ │ │ + } │ │ │ │ + value.value = this.getChildValue(node); │ │ │ │ + obj.push({ │ │ │ │ + Value: value │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "ConceptualScheme": function(node, obj) { │ │ │ │ + obj.ConceptualScheme = {}; │ │ │ │ + this.readChildNodes(node, obj.ConceptualScheme); │ │ │ │ + }, │ │ │ │ + "Name": function(node, obj) { │ │ │ │ + obj.Name = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Document": function(node, obj) { │ │ │ │ + obj.Document = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Authority": function(node, obj) { │ │ │ │ + obj.Authority = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "RangeOfValues": function(node, obj) { │ │ │ │ + obj.RangeOfValues = {}; │ │ │ │ + this.readChildNodes(node, obj.RangeOfValues); │ │ │ │ + }, │ │ │ │ + "MinValue": function(node, obj) { │ │ │ │ + var attrs = node.attributes; │ │ │ │ + var value = {}; │ │ │ │ + for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ + value[attrs[i].name] = attrs[i].nodeValue; │ │ │ │ + } │ │ │ │ + value.value = this.getChildValue(node); │ │ │ │ + obj.MinValue = value; │ │ │ │ + }, │ │ │ │ + "MaxValue": function(node, obj) { │ │ │ │ + var attrs = node.attributes; │ │ │ │ + var value = {}; │ │ │ │ + for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ + value[attrs[i].name] = attrs[i].nodeValue; │ │ │ │ + } │ │ │ │ + value.value = this.getChildValue(node); │ │ │ │ + obj.MaxValue = value; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: write │ │ │ │ + * Given an configuration js object, write a CSWGetDomain request. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} A object mapping the request. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A serialized CSWGetDomain request. │ │ │ │ + */ │ │ │ │ + write: function(options) { │ │ │ │ + var node = this.writeNode("csw:GetDomain", options); │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: writers │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ + * node names they produce. │ │ │ │ + */ │ │ │ │ + writers: { │ │ │ │ + "csw": { │ │ │ │ + "GetDomain": function(options) { │ │ │ │ + var node = this.createElementNSPlus("csw:GetDomain", { │ │ │ │ + attributes: { │ │ │ │ + service: "CSW", │ │ │ │ + version: this.version │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + if (options.PropertyName || this.PropertyName) { │ │ │ │ + this.writeNode( │ │ │ │ + "csw:PropertyName", │ │ │ │ + options.PropertyName || this.PropertyName, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } else if (options.ParameterName || this.ParameterName) { │ │ │ │ + this.writeNode( │ │ │ │ + "csw:ParameterName", │ │ │ │ + options.ParameterName || this.ParameterName, │ │ │ │ + node │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + this.readChildNodes(node, options); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "PropertyName": function(value) { │ │ │ │ + var node = this.createElementNSPlus("csw:PropertyName", { │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "ParameterName": function(value) { │ │ │ │ + var node = this.createElementNSPlus("csw:ParameterName", { │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + return node; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.CSWGetDomain.v2_0_2" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WCSCapabilities/v1.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WCSCapabilities.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WCSCapabilities.v1 │ │ │ │ + * Abstract class not to be instantiated directly. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WCSCapabilities.v1 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.XML, { │ │ │ │ + │ │ │ │ + regExes: { │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ + splitSpace: (/\s+/) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: defaultPrefix │ │ │ │ + */ │ │ │ │ + defaultPrefix: "wcs", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read capabilities data from a string, and return a list of coverages. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} List of named coverages. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + var raw = data; │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ + } │ │ │ │ + var capabilities = {}; │ │ │ │ + this.readNode(data, capabilities); │ │ │ │ + return capabilities; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WCSCapabilities/v1_0_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WCSCapabilities/v1.js │ │ │ │ + * @requires OpenLayers/Format/GML/v3.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WCSCapabilities/v1_0_0 │ │ │ │ + * Read WCS Capabilities version 1.0.0. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.WCSCapabilities.v1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WCSCapabilities.v1_0_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WCSCapabilities.v1, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WCSCapabilities.v1_0_0 │ │ │ │ + * Create a new parser for WCS capabilities version 1.0.0. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + */ │ │ │ │ + namespaces: { │ │ │ │ + wcs: "http://www.opengis.net/wcs", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance", │ │ │ │ + ows: "http://www.opengis.net/ows" │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: errorProperty │ │ │ │ + * {String} Which property of the returned object to check for in order to │ │ │ │ + * determine whether or not parsing has failed. In the case that the │ │ │ │ + * errorProperty is undefined on the returned object, the document will be │ │ │ │ + * run through an OGCExceptionReport parser. │ │ │ │ + */ │ │ │ │ + errorProperty: "service", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wcs": { │ │ │ │ + "WCS_Capabilities": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "Service": function(node, obj) { │ │ │ │ + obj.service = {}; │ │ │ │ + this.readChildNodes(node, obj.service); │ │ │ │ + }, │ │ │ │ + "name": function(node, service) { │ │ │ │ + service.name = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "label": function(node, service) { │ │ │ │ + service.label = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "keywords": function(node, service) { │ │ │ │ + service.keywords = []; │ │ │ │ + this.readChildNodes(node, service.keywords); │ │ │ │ + }, │ │ │ │ + "keyword": function(node, keywords) { │ │ │ │ + // Append the keyword to the keywords list │ │ │ │ + keywords.push(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "responsibleParty": function(node, service) { │ │ │ │ + service.responsibleParty = {}; │ │ │ │ + this.readChildNodes(node, service.responsibleParty); │ │ │ │ + }, │ │ │ │ + "individualName": function(node, responsibleParty) { │ │ │ │ + responsibleParty.individualName = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "organisationName": function(node, responsibleParty) { │ │ │ │ + responsibleParty.organisationName = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "positionName": function(node, responsibleParty) { │ │ │ │ + responsibleParty.positionName = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "contactInfo": function(node, responsibleParty) { │ │ │ │ + responsibleParty.contactInfo = {}; │ │ │ │ + this.readChildNodes(node, responsibleParty.contactInfo); │ │ │ │ + }, │ │ │ │ + "phone": function(node, contactInfo) { │ │ │ │ + contactInfo.phone = {}; │ │ │ │ + this.readChildNodes(node, contactInfo.phone); │ │ │ │ + }, │ │ │ │ + "voice": function(node, phone) { │ │ │ │ + phone.voice = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "facsimile": function(node, phone) { │ │ │ │ + phone.facsimile = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "address": function(node, contactInfo) { │ │ │ │ + contactInfo.address = {}; │ │ │ │ + this.readChildNodes(node, contactInfo.address); │ │ │ │ + }, │ │ │ │ + "deliveryPoint": function(node, address) { │ │ │ │ + address.deliveryPoint = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "city": function(node, address) { │ │ │ │ + address.city = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "postalCode": function(node, address) { │ │ │ │ + address.postalCode = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "country": function(node, address) { │ │ │ │ + address.country = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "electronicMailAddress": function(node, address) { │ │ │ │ + address.electronicMailAddress = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "fees": function(node, service) { │ │ │ │ + service.fees = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "accessConstraints": function(node, service) { │ │ │ │ + service.accessConstraints = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "ContentMetadata": function(node, obj) { │ │ │ │ + obj.contentMetadata = []; │ │ │ │ + this.readChildNodes(node, obj.contentMetadata); │ │ │ │ + }, │ │ │ │ + "CoverageOfferingBrief": function(node, contentMetadata) { │ │ │ │ + var coverageOfferingBrief = {}; │ │ │ │ + this.readChildNodes(node, coverageOfferingBrief); │ │ │ │ + contentMetadata.push(coverageOfferingBrief); │ │ │ │ + }, │ │ │ │ + "name": function(node, coverageOfferingBrief) { │ │ │ │ + coverageOfferingBrief.name = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "label": function(node, coverageOfferingBrief) { │ │ │ │ + coverageOfferingBrief.label = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "lonLatEnvelope": function(node, coverageOfferingBrief) { │ │ │ │ + var nodeList = this.getElementsByTagNameNS(node, "http://www.opengis.net/gml", "pos"); │ │ │ │ + │ │ │ │ + // We expect two nodes here, to create the corners of a bounding box │ │ │ │ + if (nodeList.length == 2) { │ │ │ │ + var min = {}; │ │ │ │ + var max = {}; │ │ │ │ + │ │ │ │ + OpenLayers.Format.GML.v3.prototype.readers["gml"].pos.apply(this, [nodeList[0], min]); │ │ │ │ + OpenLayers.Format.GML.v3.prototype.readers["gml"].pos.apply(this, [nodeList[1], max]); │ │ │ │ + │ │ │ │ + coverageOfferingBrief.lonLatEnvelope = {}; │ │ │ │ + coverageOfferingBrief.lonLatEnvelope.srsName = node.getAttribute("srsName"); │ │ │ │ + coverageOfferingBrief.lonLatEnvelope.min = min.points[0]; │ │ │ │ + coverageOfferingBrief.lonLatEnvelope.max = max.points[0]; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1_0_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WCSCapabilities/v1_1_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WCSCapabilities/v1.js │ │ │ │ + * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WCSCapabilities/v1_1_0 │ │ │ │ + * Read WCS Capabilities version 1.1.0. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.WCSCapabilities.v1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WCSCapabilities.v1_1_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WCSCapabilities.v1, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + */ │ │ │ │ + namespaces: { │ │ │ │ + wcs: "http://www.opengis.net/wcs/1.1", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance", │ │ │ │ + ows: "http://www.opengis.net/ows/1.1" │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: errorProperty │ │ │ │ + * {String} Which property of the returned object to check for in order to │ │ │ │ + * determine whether or not parsing has failed. In the case that the │ │ │ │ + * errorProperty is undefined on the returned object, the document will be │ │ │ │ + * run through an OGCExceptionReport parser. │ │ │ │ + */ │ │ │ │ + errorProperty: "operationsMetadata", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WCSCapabilities.v1_1_0 │ │ │ │ + * Create a new parser for WCS capabilities version 1.1.0. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wcs": OpenLayers.Util.applyDefaults({ │ │ │ │ + // In 1.0.0, this was WCS_Capabilties, in 1.1.0, it's Capabilities │ │ │ │ + "Capabilities": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "Contents": function(node, request) { │ │ │ │ + request.contentMetadata = []; │ │ │ │ + this.readChildNodes(node, request.contentMetadata); │ │ │ │ + }, │ │ │ │ + "CoverageSummary": function(node, contentMetadata) { │ │ │ │ + var coverageSummary = {}; │ │ │ │ + // Read the summary: │ │ │ │ + this.readChildNodes(node, coverageSummary); │ │ │ │ + │ │ │ │ + // Add it to the contentMetadata array: │ │ │ │ + contentMetadata.push(coverageSummary); │ │ │ │ + }, │ │ │ │ + "Identifier": function(node, coverageSummary) { │ │ │ │ + coverageSummary.identifier = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Title": function(node, coverageSummary) { │ │ │ │ + coverageSummary.title = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Abstract": function(node, coverageSummary) { │ │ │ │ + coverageSummary["abstract"] = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "SupportedCRS": function(node, coverageSummary) { │ │ │ │ + var crs = this.getChildValue(node); │ │ │ │ + if (crs) { │ │ │ │ + if (!coverageSummary.supportedCRS) { │ │ │ │ + coverageSummary.supportedCRS = []; │ │ │ │ + } │ │ │ │ + coverageSummary.supportedCRS.push(crs); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "SupportedFormat": function(node, coverageSummary) { │ │ │ │ + var format = this.getChildValue(node); │ │ │ │ + if (format) { │ │ │ │ + if (!coverageSummary.supportedFormat) { │ │ │ │ + coverageSummary.supportedFormat = []; │ │ │ │ + } │ │ │ │ + coverageSummary.supportedFormat.push(format); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.WCSCapabilities.v1.prototype.readers["wcs"]), │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1_1_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WPSCapabilities/v1_0_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WPSCapabilities.js │ │ │ │ + * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WPSCapabilities.v1_0_0 │ │ │ │ + * Read WPS Capabilities version 1.0.0. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WPSCapabilities.v1_0_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.XML, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + */ │ │ │ │ + namespaces: { │ │ │ │ + ows: "http://www.opengis.net/ows/1.1", │ │ │ │ + wps: "http://www.opengis.net/wps/1.0.0", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink" │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: regExes │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ + */ │ │ │ │ + regExes: { │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WPSCapabilities.v1_0_0 │ │ │ │ + * Create a new parser for WPS capabilities version 1.0.0. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read capabilities data from a string, and return info about the WPS. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} Information about the WPS service. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ + } │ │ │ │ + var capabilities = {}; │ │ │ │ + this.readNode(data, capabilities); │ │ │ │ + return capabilities; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wps": { │ │ │ │ + "Capabilities": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "ProcessOfferings": function(node, obj) { │ │ │ │ + obj.processOfferings = {}; │ │ │ │ + this.readChildNodes(node, obj.processOfferings); │ │ │ │ + }, │ │ │ │ + "Process": function(node, processOfferings) { │ │ │ │ + var processVersion = this.getAttributeNS(node, this.namespaces.wps, "processVersion"); │ │ │ │ + var process = { │ │ │ │ + processVersion: processVersion │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, process); │ │ │ │ + processOfferings[process.identifier] = process; │ │ │ │ + }, │ │ │ │ + "Languages": function(node, obj) { │ │ │ │ + obj.languages = []; │ │ │ │ + this.readChildNodes(node, obj.languages); │ │ │ │ + }, │ │ │ │ + "Default": function(node, languages) { │ │ │ │ + var language = { │ │ │ │ + isDefault: true │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, language); │ │ │ │ + languages.push(language); │ │ │ │ + }, │ │ │ │ + "Supported": function(node, languages) { │ │ │ │ + var language = {}; │ │ │ │ + this.readChildNodes(node, language); │ │ │ │ + languages.push(language); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WPSCapabilities.v1_0_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WMSDescribeLayer/v1_1.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WMSDescribeLayer.js │ │ │ │ + * @requires OpenLayers/Format/OGCExceptionReport.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WMSDescribeLayer.v1_1_1 │ │ │ │ + * Read SLD WMS DescribeLayer response for WMS 1.1.X │ │ │ │ + * WMS 1.1.X is tightly coupled to SLD 1.0.0 │ │ │ │ + * │ │ │ │ + * Example DescribeLayer request: │ │ │ │ + * http://demo.opengeo.org/geoserver/wms?request=DescribeLayer&version=1.1.1&layers=topp:states │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.WMSDescribeLayer> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WMSDescribeLayer.v1_1_1 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WMSDescribeLayer, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WMSDescribeLayer │ │ │ │ + * Create a new parser for WMS DescribeLayer responses. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.WMSDescribeLayer.prototype.initialize.apply(this, │ │ │ │ + [options]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read DescribeLayer data from a string, and return the response. │ │ │ │ + * The OGC defines 2 formats which are allowed for output, │ │ │ │ + * so we need to parse these 2 types for version 1.1.X │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} Object with a layerDescriptions property, which holds an Array │ │ │ │ + * of {<LayerDescription>} objects which have: │ │ │ │ + * - {String} owsType: WFS/WCS │ │ │ │ + * - {String} owsURL: the online resource │ │ │ │ + * - {String} typeName: the name of the typename on the owsType service │ │ │ │ + * - {String} layerName: the name of the WMS layer we did a lookup for │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + var root = data.documentElement; │ │ │ │ + var children = root.childNodes; │ │ │ │ + var describelayer = { │ │ │ │ + layerDescriptions: [] │ │ │ │ + }; │ │ │ │ + var childNode, nodeName; │ │ │ │ + for (var i = 0; i < children.length; ++i) { │ │ │ │ + childNode = children[i]; │ │ │ │ + nodeName = childNode.nodeName; │ │ │ │ + if (nodeName == 'LayerDescription') { │ │ │ │ + var layerName = childNode.getAttribute('name'); │ │ │ │ + var owsType = ''; │ │ │ │ + var owsURL = ''; │ │ │ │ + var typeName = ''; │ │ │ │ + // check for owsType and owsURL attributes │ │ │ │ + if (childNode.getAttribute('owsType')) { │ │ │ │ + owsType = childNode.getAttribute('owsType'); │ │ │ │ + owsURL = childNode.getAttribute('owsURL'); │ │ │ │ + } else { │ │ │ │ + // look for wfs or wcs attribute │ │ │ │ + if (childNode.getAttribute('wfs') != '') { │ │ │ │ + owsType = 'WFS'; │ │ │ │ + owsURL = childNode.getAttribute('wfs'); │ │ │ │ + } else if (childNode.getAttribute('wcs') != '') { │ │ │ │ + owsType = 'WCS'; │ │ │ │ + owsURL = childNode.getAttribute('wcs'); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + // look for Query child │ │ │ │ + var query = childNode.getElementsByTagName('Query'); │ │ │ │ + if (query.length > 0) { │ │ │ │ + typeName = query[0].getAttribute('typeName'); │ │ │ │ + if (!typeName) { │ │ │ │ + // because of Ionic bug │ │ │ │ + typeName = query[0].getAttribute('typename'); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var layerDescription = { │ │ │ │ + layerName: layerName, │ │ │ │ + owsType: owsType, │ │ │ │ + owsURL: owsURL, │ │ │ │ + typeName: typeName │ │ │ │ + }; │ │ │ │ + describelayer.layerDescriptions.push(layerDescription); │ │ │ │ + │ │ │ │ + //TODO do this in deprecated.js instead: │ │ │ │ + // array style index for backwards compatibility │ │ │ │ + describelayer.length = describelayer.layerDescriptions.length; │ │ │ │ + describelayer[describelayer.length - 1] = layerDescription; │ │ │ │ + │ │ │ │ + } else if (nodeName == 'ServiceException') { │ │ │ │ + // an exception must have occurred, so parse it │ │ │ │ + var parser = new OpenLayers.Format.OGCExceptionReport(); │ │ │ │ + return { │ │ │ │ + error: parser.read(data) │ │ │ │ + }; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return describelayer; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSDescribeLayer.v1_1_1" │ │ │ │ + │ │ │ │ + }); │ │ │ │ + │ │ │ │ +// Version alias - workaround for http://trac.osgeo.org/mapserver/ticket/2257 │ │ │ │ +OpenLayers.Format.WMSDescribeLayer.v1_1_0 = │ │ │ │ + OpenLayers.Format.WMSDescribeLayer.v1_1_1; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/ArcXML/Features.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/ArcXML.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.ArcXML.Features │ │ │ │ + * Read/Write ArcXML features. Create a new instance with the │ │ │ │ + * <OpenLayers.Format.ArcXML.Features> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.ArcXML.Features = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.ArcXML.Features │ │ │ │ + * Create a new parser/writer for ArcXML Features. Create an instance of this class │ │ │ │ + * to get a set of features from an ArcXML response. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read data from a string of ArcXML, and return a set of OpenLayers features. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} A collection of features. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + var axl = new OpenLayers.Format.ArcXML(); │ │ │ │ + var parsed = axl.read(data); │ │ │ │ + │ │ │ │ + return parsed.features.feature; │ │ │ │ + } │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WMSCapabilities/v1.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WMSCapabilities.js │ │ │ │ + * @requires OpenLayers/Format/OGCExceptionReport.js │ │ │ │ + * @requires OpenLayers/Format/XML.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WMSCapabilities.v1 │ │ │ │ + * Abstract class not to be instantiated directly. Creates │ │ │ │ + * the common parts for both WMS 1.1.X and WMS 1.3.X. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WMSCapabilities.v1 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.XML, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + */ │ │ │ │ + namespaces: { │ │ │ │ + wms: "http://www.opengis.net/wms", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: defaultPrefix │ │ │ │ + */ │ │ │ │ + defaultPrefix: "wms", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WMSCapabilities.v1 │ │ │ │ + * Create an instance of one of the subclasses. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read capabilities data from a string, and return a list of layers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} List of named layers. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + var raw = data; │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ + } │ │ │ │ + var capabilities = {}; │ │ │ │ + this.readNode(data, capabilities); │ │ │ │ + if (capabilities.service === undefined) { │ │ │ │ + // an exception must have occurred, so parse it │ │ │ │ + var parser = new OpenLayers.Format.OGCExceptionReport(); │ │ │ │ + capabilities.error = parser.read(raw); │ │ │ │ + } │ │ │ │ + return capabilities; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wms": { │ │ │ │ + "Service": function(node, obj) { │ │ │ │ + obj.service = {}; │ │ │ │ + this.readChildNodes(node, obj.service); │ │ │ │ + }, │ │ │ │ + "Name": function(node, obj) { │ │ │ │ + obj.name = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Title": function(node, obj) { │ │ │ │ + obj.title = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Abstract": function(node, obj) { │ │ │ │ + obj["abstract"] = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "BoundingBox": function(node, obj) { │ │ │ │ + var bbox = {}; │ │ │ │ + bbox.bbox = [ │ │ │ │ + parseFloat(node.getAttribute("minx")), │ │ │ │ + parseFloat(node.getAttribute("miny")), │ │ │ │ + parseFloat(node.getAttribute("maxx")), │ │ │ │ + parseFloat(node.getAttribute("maxy")) │ │ │ │ + ]; │ │ │ │ + var res = { │ │ │ │ + x: parseFloat(node.getAttribute("resx")), │ │ │ │ + y: parseFloat(node.getAttribute("resy")) │ │ │ │ + }; │ │ │ │ + │ │ │ │ + if (!(isNaN(res.x) && isNaN(res.y))) { │ │ │ │ + bbox.res = res; │ │ │ │ + } │ │ │ │ + // return the bbox so that descendant classes can set the │ │ │ │ + // CRS and SRS and add it to the obj │ │ │ │ + return bbox; │ │ │ │ + }, │ │ │ │ + "OnlineResource": function(node, obj) { │ │ │ │ + obj.href = this.getAttributeNS(node, this.namespaces.xlink, │ │ │ │ + "href"); │ │ │ │ + }, │ │ │ │ + "ContactInformation": function(node, obj) { │ │ │ │ + obj.contactInformation = {}; │ │ │ │ + this.readChildNodes(node, obj.contactInformation); │ │ │ │ + }, │ │ │ │ + "ContactPersonPrimary": function(node, obj) { │ │ │ │ + obj.personPrimary = {}; │ │ │ │ + this.readChildNodes(node, obj.personPrimary); │ │ │ │ + }, │ │ │ │ + "ContactPerson": function(node, obj) { │ │ │ │ + obj.person = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "ContactOrganization": function(node, obj) { │ │ │ │ + obj.organization = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "ContactPosition": function(node, obj) { │ │ │ │ + obj.position = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "ContactAddress": function(node, obj) { │ │ │ │ + obj.contactAddress = {}; │ │ │ │ + this.readChildNodes(node, obj.contactAddress); │ │ │ │ + }, │ │ │ │ + "AddressType": function(node, obj) { │ │ │ │ + obj.type = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Address": function(node, obj) { │ │ │ │ + obj.address = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "City": function(node, obj) { │ │ │ │ + obj.city = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "StateOrProvince": function(node, obj) { │ │ │ │ + obj.stateOrProvince = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "PostCode": function(node, obj) { │ │ │ │ + obj.postcode = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Country": function(node, obj) { │ │ │ │ + obj.country = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "ContactVoiceTelephone": function(node, obj) { │ │ │ │ + obj.phone = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "ContactFacsimileTelephone": function(node, obj) { │ │ │ │ + obj.fax = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "ContactElectronicMailAddress": function(node, obj) { │ │ │ │ + obj.email = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Fees": function(node, obj) { │ │ │ │ + var fees = this.getChildValue(node); │ │ │ │ + if (fees && fees.toLowerCase() != "none") { │ │ │ │ + obj.fees = fees; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "AccessConstraints": function(node, obj) { │ │ │ │ + var constraints = this.getChildValue(node); │ │ │ │ + if (constraints && constraints.toLowerCase() != "none") { │ │ │ │ + obj.accessConstraints = constraints; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Capability": function(node, obj) { │ │ │ │ + obj.capability = { │ │ │ │ + nestedLayers: [], │ │ │ │ + layers: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj.capability); │ │ │ │ + }, │ │ │ │ + "Request": function(node, obj) { │ │ │ │ + obj.request = {}; │ │ │ │ + this.readChildNodes(node, obj.request); │ │ │ │ + }, │ │ │ │ + "GetCapabilities": function(node, obj) { │ │ │ │ + obj.getcapabilities = { │ │ │ │ + formats: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj.getcapabilities); │ │ │ │ + }, │ │ │ │ + "Format": function(node, obj) { │ │ │ │ + if (OpenLayers.Util.isArray(obj.formats)) { │ │ │ │ + obj.formats.push(this.getChildValue(node)); │ │ │ │ + } else { │ │ │ │ + obj.format = this.getChildValue(node); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "DCPType": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "HTTP": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "Get": function(node, obj) { │ │ │ │ + obj.get = {}; │ │ │ │ + this.readChildNodes(node, obj.get); │ │ │ │ + // backwards compatibility │ │ │ │ + if (!obj.href) { │ │ │ │ + obj.href = obj.get.href; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Post": function(node, obj) { │ │ │ │ + obj.post = {}; │ │ │ │ + this.readChildNodes(node, obj.post); │ │ │ │ + // backwards compatibility │ │ │ │ + if (!obj.href) { │ │ │ │ + obj.href = obj.get.href; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "GetMap": function(node, obj) { │ │ │ │ + obj.getmap = { │ │ │ │ + formats: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj.getmap); │ │ │ │ + }, │ │ │ │ + "GetFeatureInfo": function(node, obj) { │ │ │ │ + obj.getfeatureinfo = { │ │ │ │ + formats: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj.getfeatureinfo); │ │ │ │ + }, │ │ │ │ + "Exception": function(node, obj) { │ │ │ │ + obj.exception = { │ │ │ │ + formats: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj.exception); │ │ │ │ + }, │ │ │ │ + "Layer": function(node, obj) { │ │ │ │ + var parentLayer, capability; │ │ │ │ + if (obj.capability) { │ │ │ │ + capability = obj.capability; │ │ │ │ + parentLayer = obj; │ │ │ │ + } else { │ │ │ │ + capability = obj; │ │ │ │ + } │ │ │ │ + var attrNode = node.getAttributeNode("queryable"); │ │ │ │ + var queryable = (attrNode && attrNode.specified) ? │ │ │ │ + node.getAttribute("queryable") : null; │ │ │ │ + attrNode = node.getAttributeNode("cascaded"); │ │ │ │ + var cascaded = (attrNode && attrNode.specified) ? │ │ │ │ + node.getAttribute("cascaded") : null; │ │ │ │ + attrNode = node.getAttributeNode("opaque"); │ │ │ │ + var opaque = (attrNode && attrNode.specified) ? │ │ │ │ + node.getAttribute('opaque') : null; │ │ │ │ + var noSubsets = node.getAttribute('noSubsets'); │ │ │ │ + var fixedWidth = node.getAttribute('fixedWidth'); │ │ │ │ + var fixedHeight = node.getAttribute('fixedHeight'); │ │ │ │ + var parent = parentLayer || {}, │ │ │ │ + extend = OpenLayers.Util.extend; │ │ │ │ + var layer = { │ │ │ │ + nestedLayers: [], │ │ │ │ + styles: parentLayer ? [].concat(parentLayer.styles) : [], │ │ │ │ + srs: parentLayer ? extend({}, parent.srs) : {}, │ │ │ │ + metadataURLs: [], │ │ │ │ + bbox: parentLayer ? extend({}, parent.bbox) : {}, │ │ │ │ + llbbox: parent.llbbox, │ │ │ │ + dimensions: parentLayer ? extend({}, parent.dimensions) : {}, │ │ │ │ + authorityURLs: parentLayer ? extend({}, parent.authorityURLs) : {}, │ │ │ │ + identifiers: {}, │ │ │ │ + keywords: [], │ │ │ │ + queryable: (queryable && queryable !== "") ? │ │ │ │ + (queryable === "1" || queryable === "true") : (parent.queryable || false), │ │ │ │ + cascaded: (cascaded !== null) ? parseInt(cascaded) : (parent.cascaded || 0), │ │ │ │ + opaque: opaque ? │ │ │ │ + (opaque === "1" || opaque === "true") : (parent.opaque || false), │ │ │ │ + noSubsets: (noSubsets !== null) ? │ │ │ │ + (noSubsets === "1" || noSubsets === "true") : (parent.noSubsets || false), │ │ │ │ + fixedWidth: (fixedWidth != null) ? │ │ │ │ + parseInt(fixedWidth) : (parent.fixedWidth || 0), │ │ │ │ + fixedHeight: (fixedHeight != null) ? │ │ │ │ + parseInt(fixedHeight) : (parent.fixedHeight || 0), │ │ │ │ + minScale: parent.minScale, │ │ │ │ + maxScale: parent.maxScale, │ │ │ │ + attribution: parent.attribution │ │ │ │ + }; │ │ │ │ + obj.nestedLayers.push(layer); │ │ │ │ + layer.capability = capability; │ │ │ │ + this.readChildNodes(node, layer); │ │ │ │ + delete layer.capability; │ │ │ │ + if (layer.name) { │ │ │ │ + var parts = layer.name.split(":"), │ │ │ │ + request = capability.request, │ │ │ │ + gfi = request.getfeatureinfo; │ │ │ │ + if (parts.length > 0) { │ │ │ │ + layer.prefix = parts[0]; │ │ │ │ + } │ │ │ │ + capability.layers.push(layer); │ │ │ │ + if (layer.formats === undefined) { │ │ │ │ + layer.formats = request.getmap.formats; │ │ │ │ + } │ │ │ │ + if (layer.infoFormats === undefined && gfi) { │ │ │ │ + layer.infoFormats = gfi.formats; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Attribution": function(node, obj) { │ │ │ │ + obj.attribution = {}; │ │ │ │ + this.readChildNodes(node, obj.attribution); │ │ │ │ + }, │ │ │ │ + "LogoURL": function(node, obj) { │ │ │ │ + obj.logo = { │ │ │ │ + width: node.getAttribute("width"), │ │ │ │ + height: node.getAttribute("height") │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj.logo); │ │ │ │ + }, │ │ │ │ + "Style": function(node, obj) { │ │ │ │ + var style = {}; │ │ │ │ + obj.styles.push(style); │ │ │ │ + this.readChildNodes(node, style); │ │ │ │ + }, │ │ │ │ + "LegendURL": function(node, obj) { │ │ │ │ + var legend = { │ │ │ │ + width: node.getAttribute("width"), │ │ │ │ + height: node.getAttribute("height") │ │ │ │ + }; │ │ │ │ + obj.legend = legend; │ │ │ │ + this.readChildNodes(node, legend); │ │ │ │ + }, │ │ │ │ + "MetadataURL": function(node, obj) { │ │ │ │ + var metadataURL = { │ │ │ │ + type: node.getAttribute("type") │ │ │ │ + }; │ │ │ │ + obj.metadataURLs.push(metadataURL); │ │ │ │ + this.readChildNodes(node, metadataURL); │ │ │ │ + }, │ │ │ │ + "DataURL": function(node, obj) { │ │ │ │ + obj.dataURL = {}; │ │ │ │ + this.readChildNodes(node, obj.dataURL); │ │ │ │ + }, │ │ │ │ + "FeatureListURL": function(node, obj) { │ │ │ │ + obj.featureListURL = {}; │ │ │ │ + this.readChildNodes(node, obj.featureListURL); │ │ │ │ + }, │ │ │ │ + "AuthorityURL": function(node, obj) { │ │ │ │ + var name = node.getAttribute("name"); │ │ │ │ + var authority = {}; │ │ │ │ + this.readChildNodes(node, authority); │ │ │ │ + obj.authorityURLs[name] = authority.href; │ │ │ │ + }, │ │ │ │ + "Identifier": function(node, obj) { │ │ │ │ + var authority = node.getAttribute("authority"); │ │ │ │ + obj.identifiers[authority] = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "KeywordList": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "SRS": function(node, obj) { │ │ │ │ + obj.srs[this.getChildValue(node)] = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WMSCapabilities/v1_1.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WMSCapabilities/v1.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WMSCapabilities.v1_1 │ │ │ │ + * Abstract class not to be instantiated directly. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.WMSCapabilities.v1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WMSCapabilities.v1_1 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WMSCapabilities.v1, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wms": OpenLayers.Util.applyDefaults({ │ │ │ │ + "WMT_MS_Capabilities": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "Keyword": function(node, obj) { │ │ │ │ + if (obj.keywords) { │ │ │ │ + obj.keywords.push(this.getChildValue(node)); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "DescribeLayer": function(node, obj) { │ │ │ │ + obj.describelayer = { │ │ │ │ + formats: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj.describelayer); │ │ │ │ + }, │ │ │ │ + "GetLegendGraphic": function(node, obj) { │ │ │ │ + obj.getlegendgraphic = { │ │ │ │ + formats: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj.getlegendgraphic); │ │ │ │ + }, │ │ │ │ + "GetStyles": function(node, obj) { │ │ │ │ + obj.getstyles = { │ │ │ │ + formats: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj.getstyles); │ │ │ │ + }, │ │ │ │ + "PutStyles": function(node, obj) { │ │ │ │ + obj.putstyles = { │ │ │ │ + formats: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj.putstyles); │ │ │ │ + }, │ │ │ │ + "UserDefinedSymbolization": function(node, obj) { │ │ │ │ + var userSymbols = { │ │ │ │ + supportSLD: parseInt(node.getAttribute("SupportSLD")) == 1, │ │ │ │ + userLayer: parseInt(node.getAttribute("UserLayer")) == 1, │ │ │ │ + userStyle: parseInt(node.getAttribute("UserStyle")) == 1, │ │ │ │ + remoteWFS: parseInt(node.getAttribute("RemoteWFS")) == 1 │ │ │ │ + }; │ │ │ │ + obj.userSymbols = userSymbols; │ │ │ │ + }, │ │ │ │ + "LatLonBoundingBox": function(node, obj) { │ │ │ │ + obj.llbbox = [ │ │ │ │ + parseFloat(node.getAttribute("minx")), │ │ │ │ + parseFloat(node.getAttribute("miny")), │ │ │ │ + parseFloat(node.getAttribute("maxx")), │ │ │ │ + parseFloat(node.getAttribute("maxy")) │ │ │ │ + ]; │ │ │ │ + }, │ │ │ │ + "BoundingBox": function(node, obj) { │ │ │ │ + var bbox = OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"].BoundingBox.apply(this, [node, obj]); │ │ │ │ + bbox.srs = node.getAttribute("SRS"); │ │ │ │ + obj.bbox[bbox.srs] = bbox; │ │ │ │ + }, │ │ │ │ + "ScaleHint": function(node, obj) { │ │ │ │ + var min = node.getAttribute("min"); │ │ │ │ + var max = node.getAttribute("max"); │ │ │ │ + var rad2 = Math.pow(2, 0.5); │ │ │ │ + var ipm = OpenLayers.INCHES_PER_UNIT["m"]; │ │ │ │ + if (min != 0) { │ │ │ │ + obj.maxScale = parseFloat( │ │ │ │ + ((min / rad2) * ipm * │ │ │ │ + OpenLayers.DOTS_PER_INCH).toPrecision(13) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (max != Number.POSITIVE_INFINITY) { │ │ │ │ + obj.minScale = parseFloat( │ │ │ │ + ((max / rad2) * ipm * │ │ │ │ + OpenLayers.DOTS_PER_INCH).toPrecision(13) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Dimension": function(node, obj) { │ │ │ │ + var name = node.getAttribute("name").toLowerCase(); │ │ │ │ + var dim = { │ │ │ │ + name: name, │ │ │ │ + units: node.getAttribute("units"), │ │ │ │ + unitsymbol: node.getAttribute("unitSymbol") │ │ │ │ + }; │ │ │ │ + obj.dimensions[dim.name] = dim; │ │ │ │ + }, │ │ │ │ + "Extent": function(node, obj) { │ │ │ │ + var name = node.getAttribute("name").toLowerCase(); │ │ │ │ + if (name in obj["dimensions"]) { │ │ │ │ + var extent = obj.dimensions[name]; │ │ │ │ + extent.nearestVal = │ │ │ │ + node.getAttribute("nearestValue") === "1"; │ │ │ │ + extent.multipleVal = │ │ │ │ + node.getAttribute("multipleValues") === "1"; │ │ │ │ + extent.current = node.getAttribute("current") === "1"; │ │ │ │ + extent["default"] = node.getAttribute("default") || ""; │ │ │ │ + var values = this.getChildValue(node); │ │ │ │ + extent.values = values.split(","); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"]) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WMSCapabilities/v1_1_1.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WMSCapabilities/v1_1.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WMSCapabilities/v1_1_1 │ │ │ │ + * Read WMS Capabilities version 1.1.1. │ │ │ │ + * │ │ │ │ + * Note on <ScaleHint> parsing: If the 'min' attribute is set to "0", no │ │ │ │ + * maxScale will be set on the layer object. If the 'max' attribute is set to │ │ │ │ + * "Infinity", no minScale will be set. This makes it easy to create proper │ │ │ │ + * {<OpenLayers.Layer.WMS>} configurations directly from the layer object │ │ │ │ + * literals returned by this format, because no minScale/maxScale modifications │ │ │ │ + * need to be made. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.WMSCapabilities.v1_1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WMSCapabilities.v1_1_1 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WMSCapabilities.v1_1, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: version │ │ │ │ + * {String} The specific parser version. │ │ │ │ + */ │ │ │ │ + version: "1.1.1", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_1 │ │ │ │ + * Create a new parser for WMS capabilities version 1.1.1. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wms": OpenLayers.Util.applyDefaults({ │ │ │ │ + "SRS": function(node, obj) { │ │ │ │ + obj.srs[this.getChildValue(node)] = true; │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers["wms"]) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_1" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WMSCapabilities/v1_3.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WMSCapabilities/v1.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WMSCapabilities/v1_3 │ │ │ │ + * Abstract base class for WMS Capabilities version 1.3.X. │ │ │ │ + * SLD 1.1.0 adds in the extra operations DescribeLayer and GetLegendGraphic, │ │ │ │ + * see: http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.WMSCapabilities.v1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WMSCapabilities.v1_3 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WMSCapabilities.v1, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wms": OpenLayers.Util.applyDefaults({ │ │ │ │ + "WMS_Capabilities": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "LayerLimit": function(node, obj) { │ │ │ │ + obj.layerLimit = parseInt(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "MaxWidth": function(node, obj) { │ │ │ │ + obj.maxWidth = parseInt(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "MaxHeight": function(node, obj) { │ │ │ │ + obj.maxHeight = parseInt(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "BoundingBox": function(node, obj) { │ │ │ │ + var bbox = OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"].BoundingBox.apply(this, [node, obj]); │ │ │ │ + bbox.srs = node.getAttribute("CRS"); │ │ │ │ + obj.bbox[bbox.srs] = bbox; │ │ │ │ + }, │ │ │ │ + "CRS": function(node, obj) { │ │ │ │ + // CRS is the synonym of SRS │ │ │ │ + this.readers.wms.SRS.apply(this, [node, obj]); │ │ │ │ + }, │ │ │ │ + "EX_GeographicBoundingBox": function(node, obj) { │ │ │ │ + // replacement of LatLonBoundingBox │ │ │ │ + obj.llbbox = []; │ │ │ │ + this.readChildNodes(node, obj.llbbox); │ │ │ │ + │ │ │ │ + }, │ │ │ │ + "westBoundLongitude": function(node, obj) { │ │ │ │ + obj[0] = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "eastBoundLongitude": function(node, obj) { │ │ │ │ + obj[2] = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "southBoundLatitude": function(node, obj) { │ │ │ │ + obj[1] = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "northBoundLatitude": function(node, obj) { │ │ │ │ + obj[3] = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "MinScaleDenominator": function(node, obj) { │ │ │ │ + obj.maxScale = parseFloat(this.getChildValue(node)).toPrecision(16); │ │ │ │ + }, │ │ │ │ + "MaxScaleDenominator": function(node, obj) { │ │ │ │ + obj.minScale = parseFloat(this.getChildValue(node)).toPrecision(16); │ │ │ │ + }, │ │ │ │ + "Dimension": function(node, obj) { │ │ │ │ + // dimension has extra attributes: default, multipleValues, │ │ │ │ + // nearestValue, current which used to be part of Extent. It now │ │ │ │ + // also contains the values. │ │ │ │ + var name = node.getAttribute("name").toLowerCase(); │ │ │ │ + var dim = { │ │ │ │ + name: name, │ │ │ │ + units: node.getAttribute("units"), │ │ │ │ + unitsymbol: node.getAttribute("unitSymbol"), │ │ │ │ + nearestVal: node.getAttribute("nearestValue") === "1", │ │ │ │ + multipleVal: node.getAttribute("multipleValues") === "1", │ │ │ │ + "default": node.getAttribute("default") || "", │ │ │ │ + current: node.getAttribute("current") === "1", │ │ │ │ + values: this.getChildValue(node).split(",") │ │ │ │ + │ │ │ │ + }; │ │ │ │ + // Theoretically there can be more dimensions with the same │ │ │ │ + // name, but with a different unit. Until we meet such a case, │ │ │ │ + // let's just keep the same structure as the WMS 1.1 │ │ │ │ + // GetCapabilities parser uses. We will store the last │ │ │ │ + // one encountered. │ │ │ │ + obj.dimensions[dim.name] = dim; │ │ │ │ + }, │ │ │ │ + "Keyword": function(node, obj) { │ │ │ │ + // TODO: should we change the structure of keyword in v1.js? │ │ │ │ + // Make it an object with a value instead of a string? │ │ │ │ + var keyword = { │ │ │ │ + value: this.getChildValue(node), │ │ │ │ + vocabulary: node.getAttribute("vocabulary") │ │ │ │ + }; │ │ │ │ + if (obj.keywords) { │ │ │ │ + obj.keywords.push(keyword); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"]), │ │ │ │ + "sld": { │ │ │ │ + "UserDefinedSymbolization": function(node, obj) { │ │ │ │ + this.readers.wms.UserDefinedSymbolization.apply(this, [node, obj]); │ │ │ │ + // add the two extra attributes │ │ │ │ + obj.userSymbols.inlineFeature = parseInt(node.getAttribute("InlineFeature")) == 1; │ │ │ │ + obj.userSymbols.remoteWCS = parseInt(node.getAttribute("RemoteWCS")) == 1; │ │ │ │ + }, │ │ │ │ + "DescribeLayer": function(node, obj) { │ │ │ │ + this.readers.wms.DescribeLayer.apply(this, [node, obj]); │ │ │ │ + }, │ │ │ │ + "GetLegendGraphic": function(node, obj) { │ │ │ │ + this.readers.wms.GetLegendGraphic.apply(this, [node, obj]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_3" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WMSCapabilities/v1_1_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WMSCapabilities/v1_1.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WMSCapabilities/v1_1_0 │ │ │ │ + * Read WMS Capabilities version 1.1.0. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.WMSCapabilities.v1_1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WMSCapabilities.v1_1_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WMSCapabilities.v1_1, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: version │ │ │ │ + * {String} The specific parser version. │ │ │ │ + */ │ │ │ │ + version: "1.1.0", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_0 │ │ │ │ + * Create a new parser for WMS capabilities version 1.1.0. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wms": OpenLayers.Util.applyDefaults({ │ │ │ │ + "SRS": function(node, obj) { │ │ │ │ + var srs = this.getChildValue(node); │ │ │ │ + var values = srs.split(/ +/); │ │ │ │ + for (var i = 0, len = values.length; i < len; i++) { │ │ │ │ + obj.srs[values[i]] = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers["wms"]) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WMSCapabilities/v1_1_1.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WMSCapabilities/v1_1_1_WMSC │ │ │ │ + * Read WMS-C Capabilities version 1.1.1. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.WMSCapabilities.v1_1_1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WMSCapabilities.v1_1_1, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: version │ │ │ │ + * {String} The specific parser version. │ │ │ │ + */ │ │ │ │ + version: "1.1.1", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: profile │ │ │ │ + * {String} The specific profile │ │ │ │ + */ │ │ │ │ + profile: "WMSC", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_1 │ │ │ │ + * Create a new parser for WMS-C capabilities version 1.1.1. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wms": OpenLayers.Util.applyDefaults({ │ │ │ │ + "VendorSpecificCapabilities": function(node, obj) { │ │ │ │ + obj.vendorSpecific = { │ │ │ │ + tileSets: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, obj.vendorSpecific); │ │ │ │ + }, │ │ │ │ + "TileSet": function(node, vendorSpecific) { │ │ │ │ + var tileset = { │ │ │ │ + srs: {}, │ │ │ │ + bbox: {}, │ │ │ │ + resolutions: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, tileset); │ │ │ │ + vendorSpecific.tileSets.push(tileset); │ │ │ │ + }, │ │ │ │ + "Resolutions": function(node, tileset) { │ │ │ │ + var res = this.getChildValue(node).split(" "); │ │ │ │ + for (var i = 0, len = res.length; i < len; i++) { │ │ │ │ + if (res[i] != "") { │ │ │ │ + tileset.resolutions.push(parseFloat(res[i])); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Width": function(node, tileset) { │ │ │ │ + tileset.width = parseInt(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "Height": function(node, tileset) { │ │ │ │ + tileset.height = parseInt(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "Layers": function(node, tileset) { │ │ │ │ + tileset.layers = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Styles": function(node, tileset) { │ │ │ │ + tileset.styles = this.getChildValue(node); │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.readers["wms"]) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WMSCapabilities/v1_3_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WMSCapabilities/v1_3.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WMSCapabilities/v1_3_0 │ │ │ │ + * Read WMS Capabilities version 1.3.0. │ │ │ │ + * SLD 1.1.0 adds in the extra operations DescribeLayer and GetLegendGraphic, │ │ │ │ + * see: http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.WMSCapabilities.v1_3> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WMSCapabilities.v1_3_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WMSCapabilities.v1_3, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: version │ │ │ │ + * {String} The specific parser version. │ │ │ │ + */ │ │ │ │ + version: "1.3.0", │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_3_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/SLD/v1_0_0_GeoServer.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/SLD/v1_0_0.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.SLD/v1_0_0_GeoServer │ │ │ │ + * Read and write SLD version 1.0.0 with GeoServer-specific enhanced options. │ │ │ │ + * See http://svn.osgeo.org/geotools/trunk/modules/extension/xsd/xsd-sld/src/main/resources/org/geotools/sld/bindings/StyledLayerDescriptor.xsd │ │ │ │ + * for more information. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.SLD.v1_0_0> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.SLD.v1_0_0_GeoServer = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.SLD.v1_0_0, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: version │ │ │ │ + * {String} The specific parser version. │ │ │ │ + */ │ │ │ │ + version: "1.0.0", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: profile │ │ │ │ + * {String} The specific profile │ │ │ │ + */ │ │ │ │ + profile: "GeoServer", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.SLD.v1_0_0_GeoServer │ │ │ │ + * Create a new parser for GeoServer-enhanced SLD version 1.0.0. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: OpenLayers.Util.applyDefaults({ │ │ │ │ + "sld": OpenLayers.Util.applyDefaults({ │ │ │ │ + "Priority": function(node, obj) { │ │ │ │ + var value = this.readers.ogc._expression.call(this, node); │ │ │ │ + if (value) { │ │ │ │ + obj.priority = value; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "VendorOption": function(node, obj) { │ │ │ │ + if (!obj.vendorOptions) { │ │ │ │ + obj.vendorOptions = {}; │ │ │ │ + } │ │ │ │ + obj.vendorOptions[node.getAttribute("name")] = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "TextSymbolizer": function(node, rule) { │ │ │ │ + OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld.TextSymbolizer.apply(this, arguments); │ │ │ │ + var symbolizer = this.multipleSymbolizers ? rule.symbolizers[rule.symbolizers.length - 1] : rule.symbolizer["Text"]; │ │ │ │ + if (symbolizer.graphic === undefined) { │ │ │ │ + symbolizer.graphic = false; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.SLD.v1_0_0.prototype.readers["sld"]) │ │ │ │ + }, OpenLayers.Format.SLD.v1_0_0.prototype.readers), │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: writers │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ + * node names they produce. │ │ │ │ + */ │ │ │ │ + writers: OpenLayers.Util.applyDefaults({ │ │ │ │ + "sld": OpenLayers.Util.applyDefaults({ │ │ │ │ + "Priority": function(priority) { │ │ │ │ + return this.writers.sld._OGCExpression.call( │ │ │ │ + this, "sld:Priority", priority │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + "VendorOption": function(option) { │ │ │ │ + return this.createElementNSPlus("sld:VendorOption", { │ │ │ │ + attributes: { │ │ │ │ + name: option.name │ │ │ │ + }, │ │ │ │ + value: option.value │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "TextSymbolizer": function(symbolizer) { │ │ │ │ + var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers; │ │ │ │ + var node = writers["sld"]["TextSymbolizer"].apply(this, arguments); │ │ │ │ + if (symbolizer.graphic !== false && (symbolizer.externalGraphic || symbolizer.graphicName)) { │ │ │ │ + this.writeNode("Graphic", symbolizer, node); │ │ │ │ + } │ │ │ │ + if ("priority" in symbolizer) { │ │ │ │ + this.writeNode("Priority", symbolizer.priority, node); │ │ │ │ + } │ │ │ │ + return this.addVendorOptions(node, symbolizer); │ │ │ │ + }, │ │ │ │ + "PointSymbolizer": function(symbolizer) { │ │ │ │ + var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers; │ │ │ │ + var node = writers["sld"]["PointSymbolizer"].apply(this, arguments); │ │ │ │ + return this.addVendorOptions(node, symbolizer); │ │ │ │ + }, │ │ │ │ + "LineSymbolizer": function(symbolizer) { │ │ │ │ + var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers; │ │ │ │ + var node = writers["sld"]["LineSymbolizer"].apply(this, arguments); │ │ │ │ + return this.addVendorOptions(node, symbolizer); │ │ │ │ + }, │ │ │ │ + "PolygonSymbolizer": function(symbolizer) { │ │ │ │ + var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers; │ │ │ │ + var node = writers["sld"]["PolygonSymbolizer"].apply(this, arguments); │ │ │ │ + return this.addVendorOptions(node, symbolizer); │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.SLD.v1_0_0.prototype.writers["sld"]) │ │ │ │ + }, OpenLayers.Format.SLD.v1_0_0.prototype.writers), │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: addVendorOptions │ │ │ │ + * Add in the VendorOption tags and return the node again. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} A DOM node. │ │ │ │ + * symbolizer - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A DOM node. │ │ │ │ + */ │ │ │ │ + addVendorOptions: function(node, symbolizer) { │ │ │ │ + var options = symbolizer.vendorOptions; │ │ │ │ + if (options) { │ │ │ │ + for (var key in symbolizer.vendorOptions) { │ │ │ │ + this.writeNode("VendorOption", { │ │ │ │ + name: key, │ │ │ │ + value: symbolizer.vendorOptions[key] │ │ │ │ + }, node); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.SLD.v1_0_0_GeoServer" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/XLS/v1.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/XLS.js │ │ │ │ + * @requires OpenLayers/Format/GML/v3.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.XLS.v1 │ │ │ │ + * Superclass for XLS version 1 parsers. Only supports GeocodeRequest for now. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.XLS.v1 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + */ │ │ │ │ + namespaces: { │ │ │ │ + xls: "http://www.opengis.net/xls", │ │ │ │ + gml: "http://www.opengis.net/gml", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: regExes │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ + */ │ │ │ │ + regExes: { │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: xy │ │ │ │ + * {Boolean} Order of the GML coordinate true:(x,y) or false:(y,x) │ │ │ │ + * Changing is not recommended, a new Format should be instantiated. │ │ │ │ + */ │ │ │ │ + xy: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: defaultPrefix │ │ │ │ + */ │ │ │ │ + defaultPrefix: "xls", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} Schema location for a particular minor version. │ │ │ │ + */ │ │ │ │ + schemaLocation: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.XLS.v1 │ │ │ │ + * Instances of this class are not created directly. Use the │ │ │ │ + * <OpenLayers.Format.XLS> constructor instead. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: read │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {DOMElement} An XLS document element. │ │ │ │ + * options - {Object} Options for the reader. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An object representing the XLSResponse. │ │ │ │ + */ │ │ │ │ + read: function(data, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var xls = {}; │ │ │ │ + this.readChildNodes(data, xls); │ │ │ │ + return xls; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "xls": { │ │ │ │ + "XLS": function(node, xls) { │ │ │ │ + xls.version = node.getAttribute("version"); │ │ │ │ + this.readChildNodes(node, xls); │ │ │ │ + }, │ │ │ │ + "Response": function(node, xls) { │ │ │ │ + this.readChildNodes(node, xls); │ │ │ │ + }, │ │ │ │ + "GeocodeResponse": function(node, xls) { │ │ │ │ + xls.responseLists = []; │ │ │ │ + this.readChildNodes(node, xls); │ │ │ │ + }, │ │ │ │ + "GeocodeResponseList": function(node, xls) { │ │ │ │ + var responseList = { │ │ │ │ + features: [], │ │ │ │ + numberOfGeocodedAddresses: parseInt(node.getAttribute("numberOfGeocodedAddresses")) │ │ │ │ + }; │ │ │ │ + xls.responseLists.push(responseList); │ │ │ │ + this.readChildNodes(node, responseList); │ │ │ │ + }, │ │ │ │ + "GeocodedAddress": function(node, responseList) { │ │ │ │ + var feature = new OpenLayers.Feature.Vector(); │ │ │ │ + responseList.features.push(feature); │ │ │ │ + this.readChildNodes(node, feature); │ │ │ │ + // post-process geometry │ │ │ │ + feature.geometry = feature.components[0]; │ │ │ │ + }, │ │ │ │ + "GeocodeMatchCode": function(node, feature) { │ │ │ │ + feature.attributes.matchCode = { │ │ │ │ + accuracy: parseFloat(node.getAttribute("accuracy")), │ │ │ │ + matchType: node.getAttribute("matchType") │ │ │ │ + }; │ │ │ │ + }, │ │ │ │ + "Address": function(node, feature) { │ │ │ │ + var address = { │ │ │ │ + countryCode: node.getAttribute("countryCode"), │ │ │ │ + addressee: node.getAttribute("addressee"), │ │ │ │ + street: [], │ │ │ │ + place: [] │ │ │ │ + }; │ │ │ │ + feature.attributes.address = address; │ │ │ │ + this.readChildNodes(node, address); │ │ │ │ + }, │ │ │ │ + "freeFormAddress": function(node, address) { │ │ │ │ + address.freeFormAddress = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "StreetAddress": function(node, address) { │ │ │ │ + this.readChildNodes(node, address); │ │ │ │ + }, │ │ │ │ + "Building": function(node, address) { │ │ │ │ + address.building = { │ │ │ │ + 'number': node.getAttribute("number"), │ │ │ │ + subdivision: node.getAttribute("subdivision"), │ │ │ │ + buildingName: node.getAttribute("buildingName") │ │ │ │ + }; │ │ │ │ + }, │ │ │ │ + "Street": function(node, address) { │ │ │ │ + // only support the built-in primitive type for now │ │ │ │ + address.street.push(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "Place": function(node, address) { │ │ │ │ + // type is one of CountrySubdivision, │ │ │ │ + // CountrySecondarySubdivision, Municipality or │ │ │ │ + // MunicipalitySubdivision │ │ │ │ + address.place[node.getAttribute("type")] = │ │ │ │ + this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "PostalCode": function(node, address) { │ │ │ │ + address.postalCode = this.getChildValue(node); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "gml": OpenLayers.Format.GML.v3.prototype.readers.gml │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: write │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * request - {Object} An object representing the geocode request. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The root of an XLS document. │ │ │ │ + */ │ │ │ │ + write: function(request) { │ │ │ │ + return this.writers.xls.XLS.apply(this, [request]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: writers │ │ │ │ + * As a compliment to the readers property, this structure contains public │ │ │ │ + * writing functions grouped by namespace alias and named like the │ │ │ │ + * node names they produce. │ │ │ │ + */ │ │ │ │ + writers: { │ │ │ │ + "xls": { │ │ │ │ + "XLS": function(request) { │ │ │ │ + var root = this.createElementNSPlus( │ │ │ │ + "xls:XLS", { │ │ │ │ + attributes: { │ │ │ │ + "version": this.VERSION, │ │ │ │ + "xsi:schemaLocation": this.schemaLocation │ │ │ │ + } │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + this.writeNode("RequestHeader", request.header, root); │ │ │ │ + this.writeNode("Request", request, root); │ │ │ │ + return root; │ │ │ │ + }, │ │ │ │ + "RequestHeader": function(header) { │ │ │ │ + return this.createElementNSPlus("xls:RequestHeader"); │ │ │ │ + }, │ │ │ │ + "Request": function(request) { │ │ │ │ + var node = this.createElementNSPlus("xls:Request", { │ │ │ │ + attributes: { │ │ │ │ + methodName: "GeocodeRequest", │ │ │ │ + requestID: request.requestID || "", │ │ │ │ + version: this.VERSION │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + this.writeNode("GeocodeRequest", request.addresses, node); │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "GeocodeRequest": function(addresses) { │ │ │ │ + var node = this.createElementNSPlus("xls:GeocodeRequest"); │ │ │ │ + for (var i = 0, len = addresses.length; i < len; i++) { │ │ │ │ + this.writeNode("Address", addresses[i], node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Address": function(address) { │ │ │ │ + var node = this.createElementNSPlus("xls:Address", { │ │ │ │ + attributes: { │ │ │ │ + countryCode: address.countryCode │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + if (address.freeFormAddress) { │ │ │ │ + this.writeNode("freeFormAddress", address.freeFormAddress, node); │ │ │ │ + } else { │ │ │ │ + if (address.street) { │ │ │ │ + this.writeNode("StreetAddress", address, node); │ │ │ │ + } │ │ │ │ + if (address.municipality) { │ │ │ │ + this.writeNode("Municipality", address.municipality, node); │ │ │ │ + } │ │ │ │ + if (address.countrySubdivision) { │ │ │ │ + this.writeNode("CountrySubdivision", address.countrySubdivision, node); │ │ │ │ + } │ │ │ │ + if (address.postalCode) { │ │ │ │ + this.writeNode("PostalCode", address.postalCode, node); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "freeFormAddress": function(freeFormAddress) { │ │ │ │ + return this.createElementNSPlus("freeFormAddress", { │ │ │ │ + value: freeFormAddress │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "StreetAddress": function(address) { │ │ │ │ + var node = this.createElementNSPlus("xls:StreetAddress"); │ │ │ │ + if (address.building) { │ │ │ │ + this.writeNode(node, "Building", address.building); │ │ │ │ + } │ │ │ │ + var street = address.street; │ │ │ │ + if (!(OpenLayers.Util.isArray(street))) { │ │ │ │ + street = [street]; │ │ │ │ + } │ │ │ │ + for (var i = 0, len = street.length; i < len; i++) { │ │ │ │ + this.writeNode("Street", street[i], node); │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ + "Building": function(building) { │ │ │ │ + return this.createElementNSPlus("xls:Building", { │ │ │ │ + attributes: { │ │ │ │ + "number": building["number"], │ │ │ │ + "subdivision": building.subdivision, │ │ │ │ + "buildingName": building.buildingName │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "Street": function(street) { │ │ │ │ + return this.createElementNSPlus("xls:Street", { │ │ │ │ + value: street │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "Municipality": function(municipality) { │ │ │ │ + return this.createElementNSPlus("xls:Place", { │ │ │ │ + attributes: { │ │ │ │ + type: "Municipality" │ │ │ │ + }, │ │ │ │ + value: municipality │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "CountrySubdivision": function(countrySubdivision) { │ │ │ │ + return this.createElementNSPlus("xls:Place", { │ │ │ │ + attributes: { │ │ │ │ + type: "CountrySubdivision" │ │ │ │ + }, │ │ │ │ + value: countrySubdivision │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + "PostalCode": function(postalCode) { │ │ │ │ + return this.createElementNSPlus("xls:PostalCode", { │ │ │ │ + value: postalCode │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.XLS.v1" │ │ │ │ + │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/XLS/v1_1_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/XLS/v1.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.XLS.v1_1_0 │ │ │ │ + * Read / write XLS version 1.1.0. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XLS.v1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.XLS.v1_1_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.XLS.v1, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: VERSION │ │ │ │ + * {String} 1.1 │ │ │ │ + */ │ │ │ │ + VERSION: "1.1", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: schemaLocation │ │ │ │ + * {String} http://www.opengis.net/xls │ │ │ │ + * http://schemas.opengis.net/ols/1.1.0/LocationUtilityService.xsd │ │ │ │ + */ │ │ │ │ + schemaLocation: "http://www.opengis.net/xls http://schemas.opengis.net/ols/1.1.0/LocationUtilityService.xsd", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.XLS.v1_1_0 │ │ │ │ + * Instances of this class are not created directly. Use the │ │ │ │ + * <OpenLayers.Format.XLS> constructor instead. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.XLS.v1_1_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ + │ │ │ │ +// Support non standard implementation │ │ │ │ +OpenLayers.Format.XLS.v1_1 = OpenLayers.Format.XLS.v1_1_0; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/SOSCapabilities/v1_0_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/SOSCapabilities.js │ │ │ │ + * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ + * @requires OpenLayers/Format/GML/v3.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.SOSCapabilities.v1_0_0 │ │ │ │ + * Read SOS Capabilities version 1.0.0. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.SOSCapabilities> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.SOSCapabilities.v1_0_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.SOSCapabilities, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + */ │ │ │ │ + namespaces: { │ │ │ │ + ows: "http://www.opengis.net/ows/1.1", │ │ │ │ + sos: "http://www.opengis.net/sos/1.0", │ │ │ │ + gml: "http://www.opengis.net/gml", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink" │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: regExes │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ + */ │ │ │ │ + regExes: { │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.SOSCapabilities.v1_0_0 │ │ │ │ + * Create a new parser for SOS capabilities version 1.0.0. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ + this.options = options; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read capabilities data from a string, and return info about the SOS. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} Information about the SOS service. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ + } │ │ │ │ + var capabilities = {}; │ │ │ │ + this.readNode(data, capabilities); │ │ │ │ + return capabilities; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ + "name": function(node, obj) { │ │ │ │ + obj.name = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "TimePeriod": function(node, obj) { │ │ │ │ + obj.timePeriod = {}; │ │ │ │ + this.readChildNodes(node, obj.timePeriod); │ │ │ │ + }, │ │ │ │ + "beginPosition": function(node, timePeriod) { │ │ │ │ + timePeriod.beginPosition = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "endPosition": function(node, timePeriod) { │ │ │ │ + timePeriod.endPosition = this.getChildValue(node); │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.GML.v3.prototype.readers["gml"]), │ │ │ │ + "sos": { │ │ │ │ + "Capabilities": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "Contents": function(node, obj) { │ │ │ │ + obj.contents = {}; │ │ │ │ + this.readChildNodes(node, obj.contents); │ │ │ │ + }, │ │ │ │ + "ObservationOfferingList": function(node, contents) { │ │ │ │ + contents.offeringList = {}; │ │ │ │ + this.readChildNodes(node, contents.offeringList); │ │ │ │ + }, │ │ │ │ + "ObservationOffering": function(node, offeringList) { │ │ │ │ + var id = this.getAttributeNS(node, this.namespaces.gml, "id"); │ │ │ │ + offeringList[id] = { │ │ │ │ + procedures: [], │ │ │ │ + observedProperties: [], │ │ │ │ + featureOfInterestIds: [], │ │ │ │ + responseFormats: [], │ │ │ │ + resultModels: [], │ │ │ │ + responseModes: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, offeringList[id]); │ │ │ │ + }, │ │ │ │ + "time": function(node, offering) { │ │ │ │ + offering.time = {}; │ │ │ │ + this.readChildNodes(node, offering.time); │ │ │ │ + }, │ │ │ │ + "procedure": function(node, offering) { │ │ │ │ + offering.procedures.push(this.getAttributeNS(node, │ │ │ │ + this.namespaces.xlink, "href")); │ │ │ │ + }, │ │ │ │ + "observedProperty": function(node, offering) { │ │ │ │ + offering.observedProperties.push(this.getAttributeNS(node, │ │ │ │ + this.namespaces.xlink, "href")); │ │ │ │ + }, │ │ │ │ + "featureOfInterest": function(node, offering) { │ │ │ │ + offering.featureOfInterestIds.push(this.getAttributeNS(node, │ │ │ │ + this.namespaces.xlink, "href")); │ │ │ │ + }, │ │ │ │ + "responseFormat": function(node, offering) { │ │ │ │ + offering.responseFormats.push(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "resultModel": function(node, offering) { │ │ │ │ + offering.resultModels.push(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "responseMode": function(node, offering) { │ │ │ │ + offering.responseModes.push(this.getChildValue(node)); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.SOSCapabilities.v1_0_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WFSCapabilities/v1.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WFSCapabilities.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WFSCapabilities.v1 │ │ │ │ + * Abstract class not to be instantiated directly. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.XML> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WFSCapabilities.v1 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.XML, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + */ │ │ │ │ + namespaces: { │ │ │ │ + wfs: "http://www.opengis.net/wfs", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance", │ │ │ │ + ows: "http://www.opengis.net/ows" │ │ │ │ + }, │ │ │ │ + │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: errorProperty │ │ │ │ + * {String} Which property of the returned object to check for in order to │ │ │ │ + * determine whether or not parsing has failed. In the case that the │ │ │ │ + * errorProperty is undefined on the returned object, the document will be │ │ │ │ + * run through an OGCExceptionReport parser. │ │ │ │ + */ │ │ │ │ + errorProperty: "featureTypeList", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: defaultPrefix │ │ │ │ + */ │ │ │ │ + defaultPrefix: "wfs", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WFSCapabilities.v1_1 │ │ │ │ + * Create an instance of one of the subclasses. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read capabilities data from a string, and return a list of layers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} List of named layers. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + var raw = data; │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ + } │ │ │ │ + var capabilities = {}; │ │ │ │ + this.readNode(data, capabilities); │ │ │ │ + return capabilities; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wfs": { │ │ │ │ + "WFS_Capabilities": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "FeatureTypeList": function(node, request) { │ │ │ │ + request.featureTypeList = { │ │ │ │ + featureTypes: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, request.featureTypeList); │ │ │ │ + }, │ │ │ │ + "FeatureType": function(node, featureTypeList) { │ │ │ │ + var featureType = {}; │ │ │ │ + this.readChildNodes(node, featureType); │ │ │ │ + featureTypeList.featureTypes.push(featureType); │ │ │ │ + }, │ │ │ │ + "Name": function(node, obj) { │ │ │ │ + var name = this.getChildValue(node); │ │ │ │ + if (name) { │ │ │ │ + var parts = name.split(":"); │ │ │ │ + obj.name = parts.pop(); │ │ │ │ + if (parts.length > 0) { │ │ │ │ + obj.featureNS = this.lookupNamespaceURI(node, parts[0]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Title": function(node, obj) { │ │ │ │ + var title = this.getChildValue(node); │ │ │ │ + if (title) { │ │ │ │ + obj.title = title; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Abstract": function(node, obj) { │ │ │ │ + var abst = this.getChildValue(node); │ │ │ │ + if (abst) { │ │ │ │ + obj["abstract"] = abst; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WFSCapabilities/v1_0_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WFSCapabilities/v1.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WFSCapabilities/v1_0_0 │ │ │ │ + * Read WFS Capabilities version 1.0.0. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.WFSCapabilities.v1> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WFSCapabilities.v1_0_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WFSCapabilities.v1, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WFSCapabilities.v1_0_0 │ │ │ │ + * Create a new parser for WFS capabilities version 1.0.0. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wfs": OpenLayers.Util.applyDefaults({ │ │ │ │ + "Service": function(node, capabilities) { │ │ │ │ + capabilities.service = {}; │ │ │ │ + this.readChildNodes(node, capabilities.service); │ │ │ │ + }, │ │ │ │ + "Fees": function(node, service) { │ │ │ │ + var fees = this.getChildValue(node); │ │ │ │ + if (fees && fees.toLowerCase() != "none") { │ │ │ │ + service.fees = fees; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "AccessConstraints": function(node, service) { │ │ │ │ + var constraints = this.getChildValue(node); │ │ │ │ + if (constraints && constraints.toLowerCase() != "none") { │ │ │ │ + service.accessConstraints = constraints; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "OnlineResource": function(node, service) { │ │ │ │ + var onlineResource = this.getChildValue(node); │ │ │ │ + if (onlineResource && onlineResource.toLowerCase() != "none") { │ │ │ │ + service.onlineResource = onlineResource; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Keywords": function(node, service) { │ │ │ │ + var keywords = this.getChildValue(node); │ │ │ │ + if (keywords && keywords.toLowerCase() != "none") { │ │ │ │ + service.keywords = keywords.split(', '); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "Capability": function(node, capabilities) { │ │ │ │ + capabilities.capability = {}; │ │ │ │ + this.readChildNodes(node, capabilities.capability); │ │ │ │ + }, │ │ │ │ + "Request": function(node, obj) { │ │ │ │ + obj.request = {}; │ │ │ │ + this.readChildNodes(node, obj.request); │ │ │ │ + }, │ │ │ │ + "GetFeature": function(node, request) { │ │ │ │ + request.getfeature = { │ │ │ │ + href: {}, // DCPType │ │ │ │ + formats: [] // ResultFormat │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, request.getfeature); │ │ │ │ + }, │ │ │ │ + "ResultFormat": function(node, obj) { │ │ │ │ + var children = node.childNodes; │ │ │ │ + var childNode; │ │ │ │ + for (var i = 0; i < children.length; i++) { │ │ │ │ + childNode = children[i]; │ │ │ │ + if (childNode.nodeType == 1) { │ │ │ │ + obj.formats.push(childNode.nodeName); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "DCPType": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "HTTP": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj.href); │ │ │ │ + }, │ │ │ │ + "Get": function(node, obj) { │ │ │ │ + obj.get = node.getAttribute("onlineResource"); │ │ │ │ + }, │ │ │ │ + "Post": function(node, obj) { │ │ │ │ + obj.post = node.getAttribute("onlineResource"); │ │ │ │ + }, │ │ │ │ + "SRS": function(node, obj) { │ │ │ │ + var srs = this.getChildValue(node); │ │ │ │ + if (srs) { │ │ │ │ + obj.srs = srs; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers["wfs"]) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_0_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WFSCapabilities/v1_1_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WFSCapabilities/v1.js │ │ │ │ + * @requires OpenLayers/Format/OWSCommon/v1.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WFSCapabilities/v1_1_0 │ │ │ │ + * Read WFS Capabilities version 1.1.0. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.WFSCapabilities> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WFSCapabilities.v1_1_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.WFSCapabilities.v1, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: regExes │ │ │ │ + * Compiled regular expressions for manipulating strings. │ │ │ │ + */ │ │ │ │ + regExes: { │ │ │ │ + trimSpace: (/^\s*|\s*$/g), │ │ │ │ + removeSpace: (/\s*/g), │ │ │ │ + splitSpace: (/\s+/), │ │ │ │ + trimComma: (/\s*,\s*/g) │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WFSCapabilities.v1_1_0 │ │ │ │ + * Create a new parser for WFS capabilities version 1.1.0. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wfs": OpenLayers.Util.applyDefaults({ │ │ │ │ + "DefaultSRS": function(node, obj) { │ │ │ │ + var defaultSRS = this.getChildValue(node); │ │ │ │ + if (defaultSRS) { │ │ │ │ + obj.srs = defaultSRS; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers["wfs"]), │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1.prototype.readers.ows │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_1_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Format/WMTSCapabilities/v1_0_0.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Format/WMTSCapabilities.js │ │ │ │ + * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.WMTSCapabilities.v1_0_0 │ │ │ │ + * Read WMTS Capabilities version 1.0.0. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Format.WMTSCapabilities> │ │ │ │ + */ │ │ │ │ +OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class( │ │ │ │ + OpenLayers.Format.OWSCommon.v1_1_0, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: version │ │ │ │ + * {String} The parser version ("1.0.0"). │ │ │ │ + */ │ │ │ │ + version: "1.0.0", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: namespaces │ │ │ │ + * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + */ │ │ │ │ + namespaces: { │ │ │ │ + ows: "http://www.opengis.net/ows/1.1", │ │ │ │ + wmts: "http://www.opengis.net/wmts/1.0", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink" │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: yx │ │ │ │ + * {Object} Members in the yx object are used to determine if a CRS URN │ │ │ │ + * corresponds to a CRS with y,x axis order. Member names are CRS URNs │ │ │ │ + * and values are boolean. Defaults come from the │ │ │ │ + * <OpenLayers.Format.WMTSCapabilities> prototype. │ │ │ │ + */ │ │ │ │ + yx: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: defaultPrefix │ │ │ │ + * {String} The default namespace alias for creating element nodes. │ │ │ │ + */ │ │ │ │ + defaultPrefix: "wmts", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Format.WMTSCapabilities.v1_0_0 │ │ │ │ + * Create a new parser for WMTS capabilities version 1.0.0. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ + this.options = options; │ │ │ │ + var yx = OpenLayers.Util.extend({}, OpenLayers.Format.WMTSCapabilities.prototype.yx); │ │ │ │ + this.yx = OpenLayers.Util.extend(yx, this.yx); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Read capabilities data from a string, and return info about the WMTS. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} Information about the SOS service. │ │ │ │ + */ │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + } │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement; │ │ │ │ + } │ │ │ │ + var capabilities = {}; │ │ │ │ + this.readNode(data, capabilities); │ │ │ │ + capabilities.version = this.version; │ │ │ │ + return capabilities; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: readers │ │ │ │ + * Contains public functions, grouped by namespace prefix, that will │ │ │ │ + * be applied when a namespaced node is found matching the function │ │ │ │ + * name. The function will be applied in the scope of this parser │ │ │ │ + * with two arguments: the node being read and a context object passed │ │ │ │ + * from the parent. │ │ │ │ + */ │ │ │ │ + readers: { │ │ │ │ + "wmts": { │ │ │ │ + "Capabilities": function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + }, │ │ │ │ + "Contents": function(node, obj) { │ │ │ │ + obj.contents = {}; │ │ │ │ + obj.contents.layers = []; │ │ │ │ + obj.contents.tileMatrixSets = {}; │ │ │ │ + this.readChildNodes(node, obj.contents); │ │ │ │ + }, │ │ │ │ + "Layer": function(node, obj) { │ │ │ │ + var layer = { │ │ │ │ + styles: [], │ │ │ │ + formats: [], │ │ │ │ + dimensions: [], │ │ │ │ + tileMatrixSetLinks: [] │ │ │ │ + }; │ │ │ │ + layer.layers = []; │ │ │ │ + this.readChildNodes(node, layer); │ │ │ │ + obj.layers.push(layer); │ │ │ │ + }, │ │ │ │ + "Style": function(node, obj) { │ │ │ │ + var style = {}; │ │ │ │ + style.isDefault = (node.getAttribute("isDefault") === "true"); │ │ │ │ + this.readChildNodes(node, style); │ │ │ │ + obj.styles.push(style); │ │ │ │ + }, │ │ │ │ + "Format": function(node, obj) { │ │ │ │ + obj.formats.push(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "TileMatrixSetLink": function(node, obj) { │ │ │ │ + var tileMatrixSetLink = {}; │ │ │ │ + this.readChildNodes(node, tileMatrixSetLink); │ │ │ │ + obj.tileMatrixSetLinks.push(tileMatrixSetLink); │ │ │ │ + }, │ │ │ │ + "TileMatrixSet": function(node, obj) { │ │ │ │ + // node could be child of wmts:Contents or wmts:TileMatrixSetLink │ │ │ │ + // duck type wmts:Contents by looking for layers │ │ │ │ + if (obj.layers) { │ │ │ │ + // TileMatrixSet as object type in schema │ │ │ │ + var tileMatrixSet = { │ │ │ │ + matrixIds: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, tileMatrixSet); │ │ │ │ + obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet; │ │ │ │ + } else { │ │ │ │ + // TileMatrixSet as string type in schema │ │ │ │ + obj.tileMatrixSet = this.getChildValue(node); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "TileMatrix": function(node, obj) { │ │ │ │ + var tileMatrix = { │ │ │ │ + supportedCRS: obj.supportedCRS │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, tileMatrix); │ │ │ │ + obj.matrixIds.push(tileMatrix); │ │ │ │ + }, │ │ │ │ + "ScaleDenominator": function(node, obj) { │ │ │ │ + obj.scaleDenominator = parseFloat(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "TopLeftCorner": function(node, obj) { │ │ │ │ + var topLeftCorner = this.getChildValue(node); │ │ │ │ + var coords = topLeftCorner.split(" "); │ │ │ │ + // decide on axis order for the given CRS │ │ │ │ + var yx; │ │ │ │ + if (obj.supportedCRS) { │ │ │ │ + // extract out version from URN │ │ │ │ + var crs = obj.supportedCRS.replace( │ │ │ │ + /urn:ogc:def:crs:(\w+):.+:(\w+)$/, │ │ │ │ + "urn:ogc:def:crs:$1::$2" │ │ │ │ + ); │ │ │ │ + yx = !!this.yx[crs]; │ │ │ │ + } │ │ │ │ + if (yx) { │ │ │ │ + obj.topLeftCorner = new OpenLayers.LonLat( │ │ │ │ + coords[1], coords[0] │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + obj.topLeftCorner = new OpenLayers.LonLat( │ │ │ │ + coords[0], coords[1] │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "TileWidth": function(node, obj) { │ │ │ │ + obj.tileWidth = parseInt(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "TileHeight": function(node, obj) { │ │ │ │ + obj.tileHeight = parseInt(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "MatrixWidth": function(node, obj) { │ │ │ │ + obj.matrixWidth = parseInt(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "MatrixHeight": function(node, obj) { │ │ │ │ + obj.matrixHeight = parseInt(this.getChildValue(node)); │ │ │ │ + }, │ │ │ │ + "ResourceURL": function(node, obj) { │ │ │ │ + obj.resourceUrl = obj.resourceUrl || {}; │ │ │ │ + var resourceType = node.getAttribute("resourceType"); │ │ │ │ + if (!obj.resourceUrls) { │ │ │ │ + obj.resourceUrls = []; │ │ │ │ + } │ │ │ │ + var resourceUrl = obj.resourceUrl[resourceType] = { │ │ │ │ + format: node.getAttribute("format"), │ │ │ │ + template: node.getAttribute("template"), │ │ │ │ + resourceType: resourceType │ │ │ │ + }; │ │ │ │ + obj.resourceUrls.push(resourceUrl); │ │ │ │ + }, │ │ │ │ + // not used for now, can be added in the future though │ │ │ │ + /*"Themes": function(node, obj) { │ │ │ │ + obj.themes = []; │ │ │ │ + this.readChildNodes(node, obj.themes); │ │ │ │ + }, │ │ │ │ + "Theme": function(node, obj) { │ │ │ │ + var theme = {}; │ │ │ │ + this.readChildNodes(node, theme); │ │ │ │ + obj.push(theme); │ │ │ │ + },*/ │ │ │ │ + "WSDL": function(node, obj) { │ │ │ │ + obj.wsdl = {}; │ │ │ │ + obj.wsdl.href = node.getAttribute("xlink:href"); │ │ │ │ + // TODO: other attributes of <WSDL> element │ │ │ │ + }, │ │ │ │ + "ServiceMetadataURL": function(node, obj) { │ │ │ │ + obj.serviceMetadataUrl = {}; │ │ │ │ + obj.serviceMetadataUrl.href = node.getAttribute("xlink:href"); │ │ │ │ + // TODO: other attributes of <ServiceMetadataURL> element │ │ │ │ + }, │ │ │ │ + "LegendURL": function(node, obj) { │ │ │ │ + obj.legend = {}; │ │ │ │ + obj.legend.href = node.getAttribute("xlink:href"); │ │ │ │ + obj.legend.format = node.getAttribute("format"); │ │ │ │ + }, │ │ │ │ + "Dimension": function(node, obj) { │ │ │ │ + var dimension = { │ │ │ │ + values: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, dimension); │ │ │ │ + obj.dimensions.push(dimension); │ │ │ │ + }, │ │ │ │ + "Default": function(node, obj) { │ │ │ │ + obj["default"] = this.getChildValue(node); │ │ │ │ + }, │ │ │ │ + "Value": function(node, obj) { │ │ │ │ + obj.values.push(this.getChildValue(node)); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMTSCapabilities.v1_0_0" │ │ │ │ + │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/Markers.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.Markers │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} Markers layer is never a base layer. │ │ │ │ + */ │ │ │ │ + isBaseLayer: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: markers │ │ │ │ + * {Array(<OpenLayers.Marker>)} internal marker list │ │ │ │ + */ │ │ │ │ + markers: null, │ │ │ │ + │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: drawn │ │ │ │ + * {Boolean} internal state of drawing. This is a workaround for the fact │ │ │ │ + * that the map does not call moveTo with a zoomChanged when the map is │ │ │ │ + * first starting up. This lets us catch the case where we have *never* │ │ │ │ + * drawn the layer, and draw it even if the zoom hasn't changed. │ │ │ │ + */ │ │ │ │ + drawn: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Markers │ │ │ │ + * Create a Markers layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + */ │ │ │ │ + initialize: function(name, options) { │ │ │ │ + OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ + this.markers = []; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.clearMarkers(); │ │ │ │ + this.markers = null; │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: setOpacity │ │ │ │ + * Sets the opacity for all the markers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * opacity - {Float} │ │ │ │ + */ │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ + if (opacity != this.opacity) { │ │ │ │ + this.opacity = opacity; │ │ │ │ + for (var i = 0, len = this.markers.length; i < len; i++) { │ │ │ │ + this.markers[i].setOpacity(this.opacity); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveTo │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * zoomChanged - {Boolean} │ │ │ │ + * dragging - {Boolean} │ │ │ │ + */ │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ + │ │ │ │ + if (zoomChanged || !this.drawn) { │ │ │ │ + for (var i = 0, len = this.markers.length; i < len; i++) { │ │ │ │ + this.drawMarker(this.markers[i]); │ │ │ │ + } │ │ │ │ + this.drawn = true; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: addMarker │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * marker - {<OpenLayers.Marker>} │ │ │ │ + */ │ │ │ │ + addMarker: function(marker) { │ │ │ │ + this.markers.push(marker); │ │ │ │ + │ │ │ │ + if (this.opacity < 1) { │ │ │ │ + marker.setOpacity(this.opacity); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.map && this.map.getExtent()) { │ │ │ │ + marker.map = this.map; │ │ │ │ + this.drawMarker(marker); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: removeMarker │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * marker - {<OpenLayers.Marker>} │ │ │ │ + */ │ │ │ │ + removeMarker: function(marker) { │ │ │ │ + if (this.markers && this.markers.length) { │ │ │ │ + OpenLayers.Util.removeItem(this.markers, marker); │ │ │ │ + marker.erase(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: clearMarkers │ │ │ │ + * This method removes all markers from a layer. The markers are not │ │ │ │ + * destroyed by this function, but are removed from the list of markers. │ │ │ │ + */ │ │ │ │ + clearMarkers: function() { │ │ │ │ + if (this.markers != null) { │ │ │ │ + while (this.markers.length > 0) { │ │ │ │ + this.removeMarker(this.markers[0]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: drawMarker │ │ │ │ + * Calculate the pixel location for the marker, create it, and │ │ │ │ + * add it to the layer's div │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * marker - {<OpenLayers.Marker>} │ │ │ │ + */ │ │ │ │ + drawMarker: function(marker) { │ │ │ │ + var px = this.map.getLayerPxFromLonLat(marker.lonlat); │ │ │ │ + if (px == null) { │ │ │ │ + marker.display(false); │ │ │ │ + } else { │ │ │ │ + if (!marker.isDrawn()) { │ │ │ │ + var markerImg = marker.draw(px); │ │ │ │ + this.div.appendChild(markerImg); │ │ │ │ + } else if (marker.icon) { │ │ │ │ + marker.icon.moveTo(px); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getDataExtent │ │ │ │ + * Calculates the max extent which includes all of the markers. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} │ │ │ │ + */ │ │ │ │ + getDataExtent: function() { │ │ │ │ + var maxExtent = null; │ │ │ │ + │ │ │ │ + if (this.markers && (this.markers.length > 0)) { │ │ │ │ + var maxExtent = new OpenLayers.Bounds(); │ │ │ │ + for (var i = 0, len = this.markers.length; i < len; i++) { │ │ │ │ + var marker = this.markers[i]; │ │ │ │ + maxExtent.extend(marker.lonlat); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + return maxExtent; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Markers" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/Text.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Markers.js │ │ │ │ + * @requires OpenLayers/Format/Text.js │ │ │ │ + * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.Text │ │ │ │ + * This layer creates markers given data in a text file. The <location> │ │ │ │ + * property of the layer (specified as a property of the options argument │ │ │ │ + * in the <OpenLayers.Layer.Text> constructor) points to a tab delimited │ │ │ │ + * file with data used to create markers. │ │ │ │ + * │ │ │ │ + * The first row of the data file should be a header line with the column names │ │ │ │ + * of the data. Each column should be delimited by a tab space. The │ │ │ │ + * possible columns are: │ │ │ │ + * - *point* lat,lon of the point where a marker is to be placed │ │ │ │ + * - *lat* Latitude of the point where a marker is to be placed │ │ │ │ + * - *lon* Longitude of the point where a marker is to be placed │ │ │ │ + * - *icon* or *image* URL of marker icon to use. │ │ │ │ + * - *iconSize* Size of Icon to use. │ │ │ │ + * - *iconOffset* Where the top-left corner of the icon is to be placed │ │ │ │ + * relative to the latitude and longitude of the point. │ │ │ │ + * - *title* The text of the 'title' is placed inside an 'h2' marker │ │ │ │ + * inside a popup, which opens when the marker is clicked. │ │ │ │ + * - *description* The text of the 'description' is placed below the h2 │ │ │ │ + * in the popup. this can be plain text or HTML. │ │ │ │ + * │ │ │ │ + * Example text file: │ │ │ │ + * (code) │ │ │ │ + * lat lon title description iconSize iconOffset icon │ │ │ │ + * 10 20 title description 21,25 -10,-25 http://www.openlayers.org/dev/img/marker.png │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Markers> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: location │ │ │ │ + * {String} URL of text file. Must be specified in the "options" argument │ │ │ │ + * of the constructor. Can not be changed once passed in. │ │ │ │ + */ │ │ │ │ + location: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: features │ │ │ │ + * {Array(<OpenLayers.Feature>)} │ │ │ │ + */ │ │ │ │ + features: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: formatOptions │ │ │ │ + * {Object} Hash of options which should be passed to the format when it is │ │ │ │ + * created. Must be passed in the constructor. │ │ │ │ + */ │ │ │ │ + formatOptions: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: selectedFeature │ │ │ │ + * {<OpenLayers.Feature>} │ │ │ │ + */ │ │ │ │ + selectedFeature: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Text │ │ │ │ + * Create a text layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} │ │ │ │ + * options - {Object} Object with properties to be set on the layer. │ │ │ │ + * Must include <location> property. │ │ │ │ + */ │ │ │ │ + initialize: function(name, options) { │ │ │ │ + OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments); │ │ │ │ + this.features = []; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + // Warning: Layer.Markers.destroy() must be called prior to calling │ │ │ │ + // clearFeatures() here, otherwise we leak memory. Indeed, if │ │ │ │ + // Layer.Markers.destroy() is called after clearFeatures(), it won't be │ │ │ │ + // able to remove the marker image elements from the layer's div since │ │ │ │ + // the markers will have been destroyed by clearFeatures(). │ │ │ │ + OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments); │ │ │ │ + this.clearFeatures(); │ │ │ │ + this.features = null; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: loadText │ │ │ │ + * Start the load of the Text data. Don't do this when we first add the layer, │ │ │ │ + * since we may not be visible at any point, and it would therefore be a waste. │ │ │ │ + */ │ │ │ │ + loadText: function() { │ │ │ │ + if (!this.loaded) { │ │ │ │ + if (this.location != null) { │ │ │ │ + │ │ │ │ + var onFail = function(e) { │ │ │ │ + this.events.triggerEvent("loadend"); │ │ │ │ + }; │ │ │ │ + │ │ │ │ + this.events.triggerEvent("loadstart"); │ │ │ │ + OpenLayers.Request.GET({ │ │ │ │ + url: this.location, │ │ │ │ + success: this.parseData, │ │ │ │ + failure: onFail, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.loaded = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveTo │ │ │ │ + * If layer is visible and Text has not been loaded, load Text. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {Object} │ │ │ │ + * zoomChanged - {Object} │ │ │ │ + * minor - {Object} │ │ │ │ + */ │ │ │ │ + moveTo: function(bounds, zoomChanged, minor) { │ │ │ │ + OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments); │ │ │ │ + if (this.visibility && !this.loaded) { │ │ │ │ + this.loadText(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: parseData │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>} │ │ │ │ + */ │ │ │ │ + parseData: function(ajaxRequest) { │ │ │ │ + var text = ajaxRequest.responseText; │ │ │ │ + │ │ │ │ + var options = {}; │ │ │ │ + │ │ │ │ + OpenLayers.Util.extend(options, this.formatOptions); │ │ │ │ + │ │ │ │ + if (this.map && !this.projection.equals(this.map.getProjectionObject())) { │ │ │ │ + options.externalProjection = this.projection; │ │ │ │ + options.internalProjection = this.map.getProjectionObject(); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var parser = new OpenLayers.Format.Text(options); │ │ │ │ + var features = parser.read(text); │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ + var data = {}; │ │ │ │ + var feature = features[i]; │ │ │ │ + var location; │ │ │ │ + var iconSize, iconOffset; │ │ │ │ + │ │ │ │ + location = new OpenLayers.LonLat(feature.geometry.x, │ │ │ │ + feature.geometry.y); │ │ │ │ + │ │ │ │ + if (feature.style.graphicWidth && │ │ │ │ + feature.style.graphicHeight) { │ │ │ │ + iconSize = new OpenLayers.Size( │ │ │ │ + feature.style.graphicWidth, │ │ │ │ + feature.style.graphicHeight); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // FIXME: At the moment, we only use this if we have an │ │ │ │ + // externalGraphic, because icon has no setOffset API Method. │ │ │ │ + /** │ │ │ │ + * FIXME FIRST!! │ │ │ │ + * The Text format does all sorts of parseFloating │ │ │ │ + * The result of a parseFloat for a bogus string is NaN. That │ │ │ │ + * means the three possible values here are undefined, NaN, or a │ │ │ │ + * number. The previous check was an identity check for null. This │ │ │ │ + * means it was failing for all undefined or NaN. A slightly better │ │ │ │ + * check is for undefined. An even better check is to see if the │ │ │ │ + * value is a number (see #1441). │ │ │ │ + */ │ │ │ │ + if (feature.style.graphicXOffset !== undefined && │ │ │ │ + feature.style.graphicYOffset !== undefined) { │ │ │ │ + iconOffset = new OpenLayers.Pixel( │ │ │ │ + feature.style.graphicXOffset, │ │ │ │ + feature.style.graphicYOffset); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (feature.style.externalGraphic != null) { │ │ │ │ + data.icon = new OpenLayers.Icon(feature.style.externalGraphic, │ │ │ │ + iconSize, │ │ │ │ + iconOffset); │ │ │ │ + } else { │ │ │ │ + data.icon = OpenLayers.Marker.defaultIcon(); │ │ │ │ + │ │ │ │ + //allows for the case where the image url is not │ │ │ │ + // specified but the size is. use a default icon │ │ │ │ + // but change the size │ │ │ │ + if (iconSize != null) { │ │ │ │ + data.icon.setSize(iconSize); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + if ((feature.attributes.title != null) && │ │ │ │ + (feature.attributes.description != null)) { │ │ │ │ + data['popupContentHTML'] = │ │ │ │ + '<h2>' + feature.attributes.title + '</h2>' + │ │ │ │ + '<p>' + feature.attributes.description + '</p>'; │ │ │ │ + } │ │ │ │ + │ │ │ │ + data['overflow'] = feature.attributes.overflow || "auto"; │ │ │ │ + │ │ │ │ + var markerFeature = new OpenLayers.Feature(this, location, data); │ │ │ │ + this.features.push(markerFeature); │ │ │ │ + var marker = markerFeature.createMarker(); │ │ │ │ + if ((feature.attributes.title != null) && │ │ │ │ + (feature.attributes.description != null)) { │ │ │ │ + marker.events.register('click', markerFeature, this.markerClick); │ │ │ │ + } │ │ │ │ + this.addMarker(marker); │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("loadend"); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: markerClick │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + * │ │ │ │ + * Context: │ │ │ │ + * - {<OpenLayers.Feature>} │ │ │ │ + */ │ │ │ │ + markerClick: function(evt) { │ │ │ │ + var sameMarkerClicked = (this == this.layer.selectedFeature); │ │ │ │ + this.layer.selectedFeature = (!sameMarkerClicked) ? this : null; │ │ │ │ + for (var i = 0, len = this.layer.map.popups.length; i < len; i++) { │ │ │ │ + this.layer.map.removePopup(this.layer.map.popups[i]); │ │ │ │ + } │ │ │ │ + if (!sameMarkerClicked) { │ │ │ │ + this.layer.map.addPopup(this.createPopup()); │ │ │ │ + } │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: clearFeatures │ │ │ │ + */ │ │ │ │ + clearFeatures: function() { │ │ │ │ + if (this.features != null) { │ │ │ │ + while (this.features.length > 0) { │ │ │ │ + var feature = this.features[0]; │ │ │ │ + OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ + feature.destroy(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Text" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/Vector.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ + * @requires OpenLayers/Renderer.js │ │ │ │ + * @requires OpenLayers/StyleMap.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Console.js │ │ │ │ + * @requires OpenLayers/Lang.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.Vector │ │ │ │ + * Instances of OpenLayers.Layer.Vector are used to render vector data from │ │ │ │ + * a variety of sources. Create a new vector layer with the │ │ │ │ + * <OpenLayers.Layer.Vector> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * layer.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Listeners will be called with a reference to an event object. The │ │ │ │ + * properties of this event depends on exactly what happened. │ │ │ │ + * │ │ │ │ + * All event objects have at least the following properties: │ │ │ │ + * object - {Object} A reference to layer.events.object. │ │ │ │ + * element - {DOMElement} A reference to layer.events.element. │ │ │ │ + * │ │ │ │ + * Supported map event types (in addition to those from <OpenLayers.Layer.events>): │ │ │ │ + * beforefeatureadded - Triggered before a feature is added. Listeners │ │ │ │ + * will receive an object with a *feature* property referencing the │ │ │ │ + * feature to be added. To stop the feature from being added, a │ │ │ │ + * listener should return false. │ │ │ │ + * beforefeaturesadded - Triggered before an array of features is added. │ │ │ │ + * Listeners will receive an object with a *features* property │ │ │ │ + * referencing the feature to be added. To stop the features from │ │ │ │ + * being added, a listener should return false. │ │ │ │ + * featureadded - Triggered after a feature is added. The event │ │ │ │ + * object passed to listeners will have a *feature* property with a │ │ │ │ + * reference to the added feature. │ │ │ │ + * featuresadded - Triggered after features are added. The event │ │ │ │ + * object passed to listeners will have a *features* property with a │ │ │ │ + * reference to an array of added features. │ │ │ │ + * beforefeatureremoved - Triggered before a feature is removed. Listeners │ │ │ │ + * will receive an object with a *feature* property referencing the │ │ │ │ + * feature to be removed. │ │ │ │ + * beforefeaturesremoved - Triggered before multiple features are removed. │ │ │ │ + * Listeners will receive an object with a *features* property │ │ │ │ + * referencing the features to be removed. │ │ │ │ + * featureremoved - Triggerd after a feature is removed. The event │ │ │ │ + * object passed to listeners will have a *feature* property with a │ │ │ │ + * reference to the removed feature. │ │ │ │ + * featuresremoved - Triggered after features are removed. The event │ │ │ │ + * object passed to listeners will have a *features* property with a │ │ │ │ + * reference to an array of removed features. │ │ │ │ + * beforefeatureselected - Triggered before a feature is selected. Listeners │ │ │ │ + * will receive an object with a *feature* property referencing the │ │ │ │ + * feature to be selected. To stop the feature from being selectd, a │ │ │ │ + * listener should return false. │ │ │ │ + * featureselected - Triggered after a feature is selected. Listeners │ │ │ │ + * will receive an object with a *feature* property referencing the │ │ │ │ + * selected feature. │ │ │ │ + * featureunselected - Triggered after a feature is unselected. │ │ │ │ + * Listeners will receive an object with a *feature* property │ │ │ │ + * referencing the unselected feature. │ │ │ │ + * beforefeaturemodified - Triggered when a feature is selected to │ │ │ │ + * be modified. Listeners will receive an object with a *feature* │ │ │ │ + * property referencing the selected feature. │ │ │ │ + * featuremodified - Triggered when a feature has been modified. │ │ │ │ + * Listeners will receive an object with a *feature* property referencing │ │ │ │ + * the modified feature. │ │ │ │ + * afterfeaturemodified - Triggered when a feature is finished being modified. │ │ │ │ + * Listeners will receive an object with a *feature* property referencing │ │ │ │ + * the modified feature. │ │ │ │ + * vertexmodified - Triggered when a vertex within any feature geometry │ │ │ │ + * has been modified. Listeners will receive an object with a │ │ │ │ + * *feature* property referencing the modified feature, a *vertex* │ │ │ │ + * property referencing the vertex modified (always a point geometry), │ │ │ │ + * and a *pixel* property referencing the pixel location of the │ │ │ │ + * modification. │ │ │ │ + * vertexremoved - Triggered when a vertex within any feature geometry │ │ │ │ + * has been deleted. Listeners will receive an object with a │ │ │ │ + * *feature* property referencing the modified feature, a *vertex* │ │ │ │ + * property referencing the vertex modified (always a point geometry), │ │ │ │ + * and a *pixel* property referencing the pixel location of the │ │ │ │ + * removal. │ │ │ │ + * sketchstarted - Triggered when a feature sketch bound for this layer │ │ │ │ + * is started. Listeners will receive an object with a *feature* │ │ │ │ + * property referencing the new sketch feature and a *vertex* property │ │ │ │ + * referencing the creation point. │ │ │ │ + * sketchmodified - Triggered when a feature sketch bound for this layer │ │ │ │ + * is modified. Listeners will receive an object with a *vertex* │ │ │ │ + * property referencing the modified vertex and a *feature* property │ │ │ │ + * referencing the sketch feature. │ │ │ │ + * sketchcomplete - Triggered when a feature sketch bound for this layer │ │ │ │ + * is complete. Listeners will receive an object with a *feature* │ │ │ │ + * property referencing the sketch feature. By returning false, a │ │ │ │ + * listener can stop the sketch feature from being added to the layer. │ │ │ │ + * refresh - Triggered when something wants a strategy to ask the protocol │ │ │ │ + * for a new set of features. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} The layer is a base layer. Default is false. Set this property │ │ │ │ + * in the layer options. │ │ │ │ + */ │ │ │ │ + isBaseLayer: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isFixed │ │ │ │ + * {Boolean} Whether the layer remains in one place while dragging the │ │ │ │ + * map. Note that setting this to true will move the layer to the bottom │ │ │ │ + * of the layer stack. │ │ │ │ + */ │ │ │ │ + isFixed: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: features │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + */ │ │ │ │ + features: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: filter │ │ │ │ + * {<OpenLayers.Filter>} The filter set in this layer, │ │ │ │ + * a strategy launching read requests can combined │ │ │ │ + * this filter with its own filter. │ │ │ │ + */ │ │ │ │ + filter: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: selectedFeatures │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + */ │ │ │ │ + selectedFeatures: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: unrenderedFeatures │ │ │ │ + * {Object} hash of features, keyed by feature.id, that the renderer │ │ │ │ + * failed to draw │ │ │ │ + */ │ │ │ │ + unrenderedFeatures: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: reportError │ │ │ │ + * {Boolean} report friendly error message when loading of renderer │ │ │ │ + * fails. │ │ │ │ + */ │ │ │ │ + reportError: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: style │ │ │ │ + * {Object} Default style for the layer │ │ │ │ + */ │ │ │ │ + style: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: styleMap │ │ │ │ + * {<OpenLayers.StyleMap>} │ │ │ │ + */ │ │ │ │ + styleMap: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: strategies │ │ │ │ + * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer. │ │ │ │ + */ │ │ │ │ + strategies: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: protocol │ │ │ │ + * {<OpenLayers.Protocol>} Optional protocol for the layer. │ │ │ │ + */ │ │ │ │ + protocol: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: renderers │ │ │ │ + * {Array(String)} List of supported Renderer classes. Add to this list to │ │ │ │ + * add support for additional renderers. This list is ordered: │ │ │ │ + * the first renderer which returns true for the 'supported()' │ │ │ │ + * method will be used, if not defined in the 'renderer' option. │ │ │ │ + */ │ │ │ │ + renderers: ['SVG', 'VML', 'Canvas'], │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: renderer │ │ │ │ + * {<OpenLayers.Renderer>} │ │ │ │ + */ │ │ │ │ + renderer: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: rendererOptions │ │ │ │ + * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for │ │ │ │ + * supported options. │ │ │ │ + */ │ │ │ │ + rendererOptions: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: geometryType │ │ │ │ + * {String} geometryType allows you to limit the types of geometries this │ │ │ │ + * layer supports. This should be set to something like │ │ │ │ + * "OpenLayers.Geometry.Point" to limit types. │ │ │ │ + */ │ │ │ │ + geometryType: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: drawn │ │ │ │ + * {Boolean} Whether the Vector Layer features have been drawn yet. │ │ │ │ + */ │ │ │ │ + drawn: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: ratio │ │ │ │ + * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map. │ │ │ │ + */ │ │ │ │ + ratio: 1, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Vector │ │ │ │ + * Create a new vector layer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} A name for the layer │ │ │ │ + * options - {Object} Optional object with non-default properties to set on │ │ │ │ + * the layer. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.Vector>} A new vector layer │ │ │ │ + */ │ │ │ │ + initialize: function(name, options) { │ │ │ │ + OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ + │ │ │ │ + // allow user-set renderer, otherwise assign one │ │ │ │ + if (!this.renderer || !this.renderer.supported()) { │ │ │ │ + this.assignRenderer(); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // if no valid renderer found, display error │ │ │ │ + if (!this.renderer || !this.renderer.supported()) { │ │ │ │ + this.renderer = null; │ │ │ │ + this.displayError(); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (!this.styleMap) { │ │ │ │ + this.styleMap = new OpenLayers.StyleMap(); │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.features = []; │ │ │ │ + this.selectedFeatures = []; │ │ │ │ + this.unrenderedFeatures = {}; │ │ │ │ + │ │ │ │ + // Allow for custom layer behavior │ │ │ │ + if (this.strategies) { │ │ │ │ + for (var i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ + this.strategies[i].setLayer(this); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Destroy this layer │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + if (this.strategies) { │ │ │ │ + var strategy, i, len; │ │ │ │ + for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ + strategy = this.strategies[i]; │ │ │ │ + if (strategy.autoDestroy) { │ │ │ │ + strategy.destroy(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.strategies = null; │ │ │ │ + } │ │ │ │ + if (this.protocol) { │ │ │ │ + if (this.protocol.autoDestroy) { │ │ │ │ + this.protocol.destroy(); │ │ │ │ + } │ │ │ │ + this.protocol = null; │ │ │ │ + } │ │ │ │ + this.destroyFeatures(); │ │ │ │ + this.features = null; │ │ │ │ + this.selectedFeatures = null; │ │ │ │ + this.unrenderedFeatures = null; │ │ │ │ + if (this.renderer) { │ │ │ │ + this.renderer.destroy(); │ │ │ │ + } │ │ │ │ + this.renderer = null; │ │ │ │ + this.geometryType = null; │ │ │ │ + this.drawn = null; │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: clone │ │ │ │ + * Create a clone of this layer. │ │ │ │ + * │ │ │ │ + * Note: Features of the layer are also cloned. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.Vector>} An exact clone of this layer │ │ │ │ + */ │ │ │ │ + clone: function(obj) { │ │ │ │ + │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.Vector(this.name, this.getOptions()); │ │ │ │ + } │ │ │ │ + │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + var features = this.features; │ │ │ │ + var len = features.length; │ │ │ │ + var clonedFeatures = new Array(len); │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ + clonedFeatures[i] = features[i].clone(); │ │ │ │ + } │ │ │ │ + obj.features = clonedFeatures; │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: refresh │ │ │ │ + * Ask the layer to request features again and redraw them. Triggers │ │ │ │ + * the refresh event if the layer is in range and visible. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * obj - {Object} Optional object with properties for any listener of │ │ │ │ + * the refresh event. │ │ │ │ + */ │ │ │ │ + refresh: function(obj) { │ │ │ │ + if (this.calculateInRange() && this.visibility) { │ │ │ │ + this.events.triggerEvent("refresh", obj); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: assignRenderer │ │ │ │ + * Iterates through the available renderer implementations and selects │ │ │ │ + * and assigns the first one whose "supported()" function returns true. │ │ │ │ + */ │ │ │ │ + assignRenderer: function() { │ │ │ │ + for (var i = 0, len = this.renderers.length; i < len; i++) { │ │ │ │ + var rendererClass = this.renderers[i]; │ │ │ │ + var renderer = (typeof rendererClass == "function") ? │ │ │ │ + rendererClass : │ │ │ │ + OpenLayers.Renderer[rendererClass]; │ │ │ │ + if (renderer && renderer.prototype.supported()) { │ │ │ │ + this.renderer = new renderer(this.div, this.rendererOptions); │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: displayError │ │ │ │ + * Let the user know their browser isn't supported. │ │ │ │ + */ │ │ │ │ + displayError: function() { │ │ │ │ + if (this.reportError) { │ │ │ │ + OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", { │ │ │ │ + renderers: this.renderers.join('\n') │ │ │ │ + })); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * The layer has been added to the map. │ │ │ │ + * │ │ │ │ + * If there is no renderer set, the layer can't be used. Remove it. │ │ │ │ + * Otherwise, give the renderer a reference to the map and set its size. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ + │ │ │ │ + if (!this.renderer) { │ │ │ │ + this.map.removeLayer(this); │ │ │ │ + } else { │ │ │ │ + this.renderer.map = this.map; │ │ │ │ + │ │ │ │ + var newSize = this.map.getSize(); │ │ │ │ + newSize.w = newSize.w * this.ratio; │ │ │ │ + newSize.h = newSize.h * this.ratio; │ │ │ │ + this.renderer.setSize(newSize); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: afterAdd │ │ │ │ + * Called at the end of the map.addLayer sequence. At this point, the map │ │ │ │ + * will have a base layer. Any autoActivate strategies will be │ │ │ │ + * activated here. │ │ │ │ + */ │ │ │ │ + afterAdd: function() { │ │ │ │ + if (this.strategies) { │ │ │ │ + var strategy, i, len; │ │ │ │ + for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ + strategy = this.strategies[i]; │ │ │ │ + if (strategy.autoActivate) { │ │ │ │ + strategy.activate(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: removeMap │ │ │ │ + * The layer has been removed from the map. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + removeMap: function(map) { │ │ │ │ + this.drawn = false; │ │ │ │ + if (this.strategies) { │ │ │ │ + var strategy, i, len; │ │ │ │ + for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ + strategy = this.strategies[i]; │ │ │ │ + if (strategy.autoActivate) { │ │ │ │ + strategy.deactivate(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: onMapResize │ │ │ │ + * Notify the renderer of the change in size. │ │ │ │ + * │ │ │ │ + */ │ │ │ │ + onMapResize: function() { │ │ │ │ + OpenLayers.Layer.prototype.onMapResize.apply(this, arguments); │ │ │ │ + │ │ │ │ + var newSize = this.map.getSize(); │ │ │ │ + newSize.w = newSize.w * this.ratio; │ │ │ │ + newSize.h = newSize.h * this.ratio; │ │ │ │ + this.renderer.setSize(newSize); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveTo │ │ │ │ + * Reset the vector layer's div so that it once again is lined up with │ │ │ │ + * the map. Notify the renderer of the change of extent, and in the │ │ │ │ + * case of a change of zoom level (resolution), have the │ │ │ │ + * renderer redraw features. │ │ │ │ + * │ │ │ │ + * If the layer has not yet been drawn, cycle through the layer's │ │ │ │ + * features and draw each one. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * zoomChanged - {Boolean} │ │ │ │ + * dragging - {Boolean} │ │ │ │ + */ │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ + │ │ │ │ + var coordSysUnchanged = true; │ │ │ │ + if (!dragging) { │ │ │ │ + this.renderer.root.style.visibility = 'hidden'; │ │ │ │ + │ │ │ │ + var viewSize = this.map.getSize(), │ │ │ │ + viewWidth = viewSize.w, │ │ │ │ + viewHeight = viewSize.h, │ │ │ │ + offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2, │ │ │ │ + offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2; │ │ │ │ + offsetLeft += this.map.layerContainerOriginPx.x; │ │ │ │ + offsetLeft = -Math.round(offsetLeft); │ │ │ │ + offsetTop += this.map.layerContainerOriginPx.y; │ │ │ │ + offsetTop = -Math.round(offsetTop); │ │ │ │ + │ │ │ │ + this.div.style.left = offsetLeft + 'px'; │ │ │ │ + this.div.style.top = offsetTop + 'px'; │ │ │ │ + │ │ │ │ + var extent = this.map.getExtent().scale(this.ratio); │ │ │ │ + coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged); │ │ │ │ + │ │ │ │ + this.renderer.root.style.visibility = 'visible'; │ │ │ │ + │ │ │ │ + // Force a reflow on gecko based browsers to prevent jump/flicker. │ │ │ │ + // This seems to happen on only certain configurations; it was originally │ │ │ │ + // noticed in FF 2.0 and Linux. │ │ │ │ + if (OpenLayers.IS_GECKO === true) { │ │ │ │ + this.div.scrollLeft = this.div.scrollLeft; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (!zoomChanged && coordSysUnchanged) { │ │ │ │ + for (var i in this.unrenderedFeatures) { │ │ │ │ + var feature = this.unrenderedFeatures[i]; │ │ │ │ + this.drawFeature(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (!this.drawn || zoomChanged || !coordSysUnchanged) { │ │ │ │ + this.drawn = true; │ │ │ │ + var feature; │ │ │ │ + for (var i = 0, len = this.features.length; i < len; i++) { │ │ │ │ + this.renderer.locked = (i !== (len - 1)); │ │ │ │ + feature = this.features[i]; │ │ │ │ + this.drawFeature(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: display │ │ │ │ + * Hide or show the Layer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * display - {Boolean} │ │ │ │ + */ │ │ │ │ + display: function(display) { │ │ │ │ + OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ + // we need to set the display style of the root in case it is attached │ │ │ │ + // to a foreign layer │ │ │ │ + var currentDisplay = this.div.style.display; │ │ │ │ + if (currentDisplay != this.renderer.root.style.display) { │ │ │ │ + this.renderer.root.style.display = currentDisplay; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: addFeatures │ │ │ │ + * Add Features to the layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + * options - {Object} │ │ │ │ + */ │ │ │ │ + addFeatures: function(features, options) { │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ + features = [features]; │ │ │ │ + } │ │ │ │ + │ │ │ │ + var notify = !options || !options.silent; │ │ │ │ + if (notify) { │ │ │ │ + var event = { │ │ │ │ + features: features │ │ │ │ + }; │ │ │ │ + var ret = this.events.triggerEvent("beforefeaturesadded", event); │ │ │ │ + if (ret === false) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + features = event.features; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // Track successfully added features for featuresadded event, since │ │ │ │ + // beforefeatureadded can veto single features. │ │ │ │ + var featuresAdded = []; │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ + if (i != (features.length - 1)) { │ │ │ │ + this.renderer.locked = true; │ │ │ │ + } else { │ │ │ │ + this.renderer.locked = false; │ │ │ │ + } │ │ │ │ + var feature = features[i]; │ │ │ │ + │ │ │ │ + if (this.geometryType && │ │ │ │ + !(feature.geometry instanceof this.geometryType)) { │ │ │ │ + throw new TypeError('addFeatures: component should be an ' + │ │ │ │ + this.geometryType.prototype.CLASS_NAME); │ │ │ │ + } │ │ │ │ + │ │ │ │ + //give feature reference to its layer │ │ │ │ + feature.layer = this; │ │ │ │ + │ │ │ │ + if (!feature.style && this.style) { │ │ │ │ + feature.style = OpenLayers.Util.extend({}, this.style); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (notify) { │ │ │ │ + if (this.events.triggerEvent("beforefeatureadded", { │ │ │ │ + feature: feature │ │ │ │ + }) === false) { │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ + this.preFeatureInsert(feature); │ │ │ │ + } │ │ │ │ + │ │ │ │ + featuresAdded.push(feature); │ │ │ │ + this.features.push(feature); │ │ │ │ + this.drawFeature(feature); │ │ │ │ + │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featureadded", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + this.onFeatureInsert(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featuresadded", { │ │ │ │ + features: featuresAdded │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: removeFeatures │ │ │ │ + * Remove features from the layer. This erases any drawn features and │ │ │ │ + * removes them from the layer's control. The beforefeatureremoved │ │ │ │ + * and featureremoved events will be triggered for each feature. The │ │ │ │ + * featuresremoved event will be triggered after all features have │ │ │ │ + * been removed. To supress event triggering, use the silent option. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be │ │ │ │ + * removed. │ │ │ │ + * options - {Object} Optional properties for changing behavior of the │ │ │ │ + * removal. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * silent - {Boolean} Supress event triggering. Default is false. │ │ │ │ + */ │ │ │ │ + removeFeatures: function(features, options) { │ │ │ │ + if (!features || features.length === 0) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (features === this.features) { │ │ │ │ + return this.removeAllFeatures(options); │ │ │ │ + } │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ + features = [features]; │ │ │ │ + } │ │ │ │ + if (features === this.selectedFeatures) { │ │ │ │ + features = features.slice(); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var notify = !options || !options.silent; │ │ │ │ + │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent( │ │ │ │ + "beforefeaturesremoved", { │ │ │ │ + features: features │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + │ │ │ │ + for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ + // We remain locked so long as we're not at 0 │ │ │ │ + // and the 'next' feature has a geometry. We do the geometry check │ │ │ │ + // because if all the features after the current one are 'null', we │ │ │ │ + // won't call eraseGeometry, so we break the 'renderer functions │ │ │ │ + // will always be called with locked=false *last*' rule. The end result │ │ │ │ + // is a possible gratiutious unlocking to save a loop through the rest │ │ │ │ + // of the list checking the remaining features every time. So long as │ │ │ │ + // null geoms are rare, this is probably okay. │ │ │ │ + if (i != 0 && features[i - 1].geometry) { │ │ │ │ + this.renderer.locked = true; │ │ │ │ + } else { │ │ │ │ + this.renderer.locked = false; │ │ │ │ + } │ │ │ │ + │ │ │ │ + var feature = features[i]; │ │ │ │ + delete this.unrenderedFeatures[feature.id]; │ │ │ │ + │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.features = OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ + // feature has no layer at this point │ │ │ │ + feature.layer = null; │ │ │ │ + │ │ │ │ + if (feature.geometry) { │ │ │ │ + this.renderer.eraseFeatures(feature); │ │ │ │ + } │ │ │ │ + │ │ │ │ + //in the case that this feature is one of the selected features, │ │ │ │ + // remove it from that array as well. │ │ │ │ + if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) { │ │ │ │ + OpenLayers.Util.removeItem(this.selectedFeatures, feature); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featureremoved", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featuresremoved", { │ │ │ │ + features: features │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: removeAllFeatures │ │ │ │ + * Remove all features from the layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional properties for changing behavior of the │ │ │ │ + * removal. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * silent - {Boolean} Supress event triggering. Default is false. │ │ │ │ + */ │ │ │ │ + removeAllFeatures: function(options) { │ │ │ │ + var notify = !options || !options.silent; │ │ │ │ + var features = this.features; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent( │ │ │ │ + "beforefeaturesremoved", { │ │ │ │ + features: features │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + var feature; │ │ │ │ + for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ + feature = features[i]; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + feature.layer = null; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featureremoved", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.renderer.clear(); │ │ │ │ + this.features = []; │ │ │ │ + this.unrenderedFeatures = {}; │ │ │ │ + this.selectedFeatures = []; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featuresremoved", { │ │ │ │ + features: features │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroyFeatures │ │ │ │ + * Erase and destroy features on the layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of │ │ │ │ + * features to destroy. If not supplied, all features on the layer │ │ │ │ + * will be destroyed. │ │ │ │ + * options - {Object} │ │ │ │ + */ │ │ │ │ + destroyFeatures: function(features, options) { │ │ │ │ + var all = (features == undefined); // evaluates to true if │ │ │ │ + // features is null │ │ │ │ + if (all) { │ │ │ │ + features = this.features; │ │ │ │ + } │ │ │ │ + if (features) { │ │ │ │ + this.removeFeatures(features, options); │ │ │ │ + for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ + features[i].destroy(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: drawFeature │ │ │ │ + * Draw (or redraw) a feature on the layer. If the optional style argument │ │ │ │ + * is included, this style will be used. If no style is included, the │ │ │ │ + * feature's style will be used. If the feature doesn't have a style, │ │ │ │ + * the layer's style will be used. │ │ │ │ + * │ │ │ │ + * This function is not designed to be used when adding features to │ │ │ │ + * the layer (use addFeatures instead). It is meant to be used when │ │ │ │ + * the style of a feature has changed, or in some other way needs to │ │ │ │ + * visually updated *after* it has already been added to a layer. You │ │ │ │ + * must add the feature to the layer for most layer-related events to │ │ │ │ + * happen. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * style - {String | Object} Named render intent or full symbolizer object. │ │ │ │ + */ │ │ │ │ + drawFeature: function(feature, style) { │ │ │ │ + // don't try to draw the feature with the renderer if the layer is not │ │ │ │ + // drawn itself │ │ │ │ + if (!this.drawn) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (typeof style != "object") { │ │ │ │ + if (!style && feature.state === OpenLayers.State.DELETE) { │ │ │ │ + style = "delete"; │ │ │ │ + } │ │ │ │ + var renderIntent = style || feature.renderIntent; │ │ │ │ + style = feature.style || this.style; │ │ │ │ + if (!style) { │ │ │ │ + style = this.styleMap.createSymbolizer(feature, renderIntent); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + var drawn = this.renderer.drawFeature(feature, style); │ │ │ │ + //TODO remove the check for null when we get rid of Renderer.SVG │ │ │ │ + if (drawn === false || drawn === null) { │ │ │ │ + this.unrenderedFeatures[feature.id] = feature; │ │ │ │ + } else { │ │ │ │ + delete this.unrenderedFeatures[feature.id]; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: eraseFeatures │ │ │ │ + * Erase features from the layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + */ │ │ │ │ + eraseFeatures: function(features) { │ │ │ │ + this.renderer.eraseFeatures(features); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getFeatureFromEvent │ │ │ │ + * Given an event, return a feature if the event occurred over one. │ │ │ │ + * Otherwise, return null. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Feature.Vector>} A feature if one was under the event. │ │ │ │ + */ │ │ │ │ + getFeatureFromEvent: function(evt) { │ │ │ │ + if (!this.renderer) { │ │ │ │ + throw new Error('getFeatureFromEvent called on layer with no ' + │ │ │ │ + 'renderer. This usually means you destroyed a ' + │ │ │ │ + 'layer, but not some handler which is associated ' + │ │ │ │ + 'with it.'); │ │ │ │ + } │ │ │ │ + var feature = null; │ │ │ │ + var featureId = this.renderer.getFeatureIdFromEvent(evt); │ │ │ │ + if (featureId) { │ │ │ │ + if (typeof featureId === "string") { │ │ │ │ + feature = this.getFeatureById(featureId); │ │ │ │ + } else { │ │ │ │ + feature = featureId; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return feature; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getFeatureBy │ │ │ │ + * Given a property value, return the feature if it exists in the features array │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * property - {String} │ │ │ │ + * value - {String} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Feature.Vector>} A feature corresponding to the given │ │ │ │ + * property value or null if there is no such feature. │ │ │ │ + */ │ │ │ │ + getFeatureBy: function(property, value) { │ │ │ │ + //TBD - would it be more efficient to use a hash for this.features? │ │ │ │ + var feature = null; │ │ │ │ + for (var i = 0, len = this.features.length; i < len; ++i) { │ │ │ │ + if (this.features[i][property] == value) { │ │ │ │ + feature = this.features[i]; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return feature; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getFeatureById │ │ │ │ + * Given a feature id, return the feature if it exists in the features array │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * featureId - {String} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Feature.Vector>} A feature corresponding to the given │ │ │ │ + * featureId or null if there is no such feature. │ │ │ │ + */ │ │ │ │ + getFeatureById: function(featureId) { │ │ │ │ + return this.getFeatureBy('id', featureId); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getFeatureByFid │ │ │ │ + * Given a feature fid, return the feature if it exists in the features array │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * featureFid - {String} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Feature.Vector>} A feature corresponding to the given │ │ │ │ + * featureFid or null if there is no such feature. │ │ │ │ + */ │ │ │ │ + getFeatureByFid: function(featureFid) { │ │ │ │ + return this.getFeatureBy('fid', featureFid); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getFeaturesByAttribute │ │ │ │ + * Returns an array of features that have the given attribute key set to the │ │ │ │ + * given value. Comparison of attribute values takes care of datatypes, e.g. │ │ │ │ + * the string '1234' is not equal to the number 1234. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * attrName - {String} │ │ │ │ + * attrValue - {Mixed} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * Array({<OpenLayers.Feature.Vector>}) An array of features that have the │ │ │ │ + * passed named attribute set to the given value. │ │ │ │ + */ │ │ │ │ + getFeaturesByAttribute: function(attrName, attrValue) { │ │ │ │ + var i, │ │ │ │ + feature, │ │ │ │ + len = this.features.length, │ │ │ │ + foundFeatures = []; │ │ │ │ + for (i = 0; i < len; i++) { │ │ │ │ + feature = this.features[i]; │ │ │ │ + if (feature && feature.attributes) { │ │ │ │ + if (feature.attributes[attrName] === attrValue) { │ │ │ │ + foundFeatures.push(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return foundFeatures; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Unselect the selected features │ │ │ │ + * i.e. clears the featureSelection array │ │ │ │ + * change the style back │ │ │ │ + clearSelection: function() { │ │ │ │ + │ │ │ │ + var vectorLayer = this.map.vectorLayer; │ │ │ │ + for (var i = 0; i < this.map.featureSelection.length; i++) { │ │ │ │ + var featureSelection = this.map.featureSelection[i]; │ │ │ │ + vectorLayer.drawFeature(featureSelection, vectorLayer.style); │ │ │ │ + } │ │ │ │ + this.map.featureSelection = []; │ │ │ │ + }, │ │ │ │ + */ │ │ │ │ + │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: onFeatureInsert │ │ │ │ + * method called after a feature is inserted. │ │ │ │ + * Does nothing by default. Override this if you │ │ │ │ + * need to do something on feature updates. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + */ │ │ │ │ + onFeatureInsert: function(feature) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: preFeatureInsert │ │ │ │ + * method called before a feature is inserted. │ │ │ │ + * Does nothing by default. Override this if you │ │ │ │ + * need to do something when features are first added to the │ │ │ │ + * layer, but before they are drawn, such as adjust the style. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + */ │ │ │ │ + preFeatureInsert: function(feature) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getDataExtent │ │ │ │ + * Calculates the max extent which includes all of the features. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} or null if the layer has no features with │ │ │ │ + * geometries. │ │ │ │ + */ │ │ │ │ + getDataExtent: function() { │ │ │ │ + var maxExtent = null; │ │ │ │ + var features = this.features; │ │ │ │ + if (features && (features.length > 0)) { │ │ │ │ + var geometry = null; │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ + geometry = features[i].geometry; │ │ │ │ + if (geometry) { │ │ │ │ + if (maxExtent === null) { │ │ │ │ + maxExtent = new OpenLayers.Bounds(); │ │ │ │ + } │ │ │ │ + maxExtent.extend(geometry.getBounds()); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return maxExtent; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Vector" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/PointTrack.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Vector.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.PointTrack │ │ │ │ + * Vector layer to display ordered point features as a line, creating one │ │ │ │ + * LineString feature for each pair of two points. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Vector> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: dataFrom │ │ │ │ + * {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or │ │ │ │ + * {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines │ │ │ │ + * should get the data/attributes from one of the two points it is │ │ │ │ + * composed of, which one should it be? │ │ │ │ + */ │ │ │ │ + dataFrom: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: styleFrom │ │ │ │ + * {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or │ │ │ │ + * {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines │ │ │ │ + * should get the style from one of the two points it is composed of, │ │ │ │ + * which one should it be? │ │ │ │ + */ │ │ │ │ + styleFrom: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.PointTrack │ │ │ │ + * Constructor for a new OpenLayers.PointTrack instance. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} name of the layer │ │ │ │ + * options - {Object} Optional object with properties to tag onto the │ │ │ │ + * instance. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: addNodes │ │ │ │ + * Adds point features that will be used to create lines from, using point │ │ │ │ + * pairs. The first point of a pair will be the source node, the second │ │ │ │ + * will be the target node. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * pointFeatures - {Array(<OpenLayers.Feature>)} │ │ │ │ + * options - {Object} │ │ │ │ + * │ │ │ │ + * Supported options: │ │ │ │ + * silent - {Boolean} true to suppress (before)feature(s)added events │ │ │ │ + */ │ │ │ │ + addNodes: function(pointFeatures, options) { │ │ │ │ + if (pointFeatures.length < 2) { │ │ │ │ + throw new Error("At least two point features have to be added to " + │ │ │ │ + "create a line from"); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var lines = new Array(pointFeatures.length - 1); │ │ │ │ + │ │ │ │ + var pointFeature, startPoint, endPoint; │ │ │ │ + for (var i = 0, len = pointFeatures.length; i < len; i++) { │ │ │ │ + pointFeature = pointFeatures[i]; │ │ │ │ + endPoint = pointFeature.geometry; │ │ │ │ + │ │ │ │ + if (!endPoint) { │ │ │ │ + var lonlat = pointFeature.lonlat; │ │ │ │ + endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat); │ │ │ │ + } else if (endPoint.CLASS_NAME != "OpenLayers.Geometry.Point") { │ │ │ │ + throw new TypeError("Only features with point geometries are supported."); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (i > 0) { │ │ │ │ + var attributes = (this.dataFrom != null) ? │ │ │ │ + (pointFeatures[i + this.dataFrom].data || │ │ │ │ + pointFeatures[i + this.dataFrom].attributes) : │ │ │ │ + null; │ │ │ │ + var style = (this.styleFrom != null) ? │ │ │ │ + (pointFeatures[i + this.styleFrom].style) : │ │ │ │ + null; │ │ │ │ + var line = new OpenLayers.Geometry.LineString([startPoint, │ │ │ │ + endPoint │ │ │ │ + ]); │ │ │ │ + │ │ │ │ + lines[i - 1] = new OpenLayers.Feature.Vector(line, attributes, │ │ │ │ + style); │ │ │ │ + } │ │ │ │ + │ │ │ │ + startPoint = endPoint; │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.addFeatures(lines, options); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.PointTrack" │ │ │ │ +}); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Layer.PointTrack.SOURCE_NODE │ │ │ │ + * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and │ │ │ │ + * <OpenLayers.Layer.PointTrack.styleFrom> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.PointTrack.SOURCE_NODE = -1; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Layer.PointTrack.TARGET_NODE │ │ │ │ + * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and │ │ │ │ + * <OpenLayers.Layer.PointTrack.styleFrom> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.PointTrack.TARGET_NODE = 0; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Layer.PointTrack.dataFrom │ │ │ │ + * {Object} with the following keys - *deprecated* │ │ │ │ + * - SOURCE_NODE: take data/attributes from the source node of the line │ │ │ │ + * - TARGET_NODE: take data/attributes from the target node of the line │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.PointTrack.dataFrom = { │ │ │ │ + 'SOURCE_NODE': -1, │ │ │ │ + 'TARGET_NODE': 0 │ │ │ │ +}; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/WorldWind.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.WorldWind │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + │ │ │ │ + DEFAULT_PARAMS: {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} WorldWind layer is a base layer by default. │ │ │ │ + */ │ │ │ │ + isBaseLayer: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: lzd │ │ │ │ + * {Float} LevelZeroTileSizeDegrees │ │ │ │ + */ │ │ │ │ + lzd: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomLevels │ │ │ │ + * {Integer} Number of zoom levels. │ │ │ │ + */ │ │ │ │ + zoomLevels: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.WorldWind │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} Name of Layer │ │ │ │ + * url - {String} Base URL │ │ │ │ + * lzd - {Float} Level zero tile size degrees │ │ │ │ + * zoomLevels - {Integer} number of zoom levels │ │ │ │ + * params - {Object} additional parameters │ │ │ │ + * options - {Object} additional options │ │ │ │ + */ │ │ │ │ + initialize: function(name, url, lzd, zoomLevels, params, options) { │ │ │ │ + this.lzd = lzd; │ │ │ │ + this.zoomLevels = zoomLevels; │ │ │ │ + var newArguments = []; │ │ │ │ + newArguments.push(name, url, params, options); │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ + this.params = OpenLayers.Util.applyDefaults( │ │ │ │ + this.params, this.DEFAULT_PARAMS │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getZoom │ │ │ │ + * Convert map zoom to WW zoom. │ │ │ │ + */ │ │ │ │ + getZoom: function() { │ │ │ │ + var zoom = this.map.getZoom(); │ │ │ │ + var extent = this.map.getMaxExtent(); │ │ │ │ + zoom = zoom - Math.log(this.maxResolution / (this.lzd / 512)) / Math.log(2); │ │ │ │ + return zoom; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getURL │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A string with the layer's url and parameters and also the │ │ │ │ + * passed-in bounds and appropriate tile size specified as │ │ │ │ + * parameters │ │ │ │ + */ │ │ │ │ + getURL: function(bounds) { │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ + var zoom = this.getZoom(); │ │ │ │ + var extent = this.map.getMaxExtent(); │ │ │ │ + var deg = this.lzd / Math.pow(2, this.getZoom()); │ │ │ │ + var x = Math.floor((bounds.left - extent.left) / deg); │ │ │ │ + var y = Math.floor((bounds.bottom - extent.bottom) / deg); │ │ │ │ + if (this.map.getResolution() <= (this.lzd / 512) && │ │ │ │ + this.getZoom() <= this.zoomLevels) { │ │ │ │ + return this.getFullRequestString({ │ │ │ │ + L: zoom, │ │ │ │ + X: x, │ │ │ │ + Y: y │ │ │ │ + }); │ │ │ │ + } else { │ │ │ │ + return OpenLayers.Util.getImageLocation("blank.gif"); │ │ │ │ + } │ │ │ │ + │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.WorldWind" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/TileCache.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.TileCache │ │ │ │ + * A read only TileCache layer. Used to requests tiles cached by TileCache in │ │ │ │ + * a web accessible cache. This means that you have to pre-populate your │ │ │ │ + * cache before this layer can be used. It is meant only to read tiles │ │ │ │ + * created by TileCache, and not to make calls to TileCache for tile │ │ │ │ + * creation. Create a new instance with the │ │ │ │ + * <OpenLayers.Layer.TileCache> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} Treat this layer as a base layer. Default is true. │ │ │ │ + */ │ │ │ │ + isBaseLayer: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: format │ │ │ │ + * {String} Mime type of the images returned. Default is image/png. │ │ │ │ + */ │ │ │ │ + format: 'image/png', │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: serverResolutions │ │ │ │ + * {Array} A list of all resolutions available on the server. Only set this │ │ │ │ + * property if the map resolutions differ from the server. This │ │ │ │ + * property serves two purposes. (a) <serverResolutions> can include │ │ │ │ + * resolutions that the server supports and that you don't want to │ │ │ │ + * provide with this layer. (b) The map can work with resolutions │ │ │ │ + * that aren't supported by the server, i.e. that aren't in │ │ │ │ + * <serverResolutions>. When the map is displayed in such a resolution │ │ │ │ + * data for the closest server-supported resolution is loaded and the │ │ │ │ + * layer div is stretched as necessary. │ │ │ │ + */ │ │ │ │ + serverResolutions: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.TileCache │ │ │ │ + * Create a new read only TileCache layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} Name of the layer displayed in the interface │ │ │ │ + * url - {String} Location of the web accessible cache (not the location of │ │ │ │ + * your tilecache script!) │ │ │ │ + * layername - {String} Layer name as defined in the TileCache │ │ │ │ + * configuration │ │ │ │ + * options - {Object} Optional object with properties to be set on the │ │ │ │ + * layer. Note that you should speficy your resolutions to match │ │ │ │ + * your TileCache configuration. This can be done by setting │ │ │ │ + * the resolutions array directly (here or on the map), by setting │ │ │ │ + * maxResolution and numZoomLevels, or by using scale based properties. │ │ │ │ + */ │ │ │ │ + initialize: function(name, url, layername, options) { │ │ │ │ + this.layername = layername; │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, │ │ │ │ + [name, url, {}, options]); │ │ │ │ + this.extension = this.format.split('/')[1].toLowerCase(); │ │ │ │ + this.extension = (this.extension == 'jpg') ? 'jpeg' : this.extension; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: clone │ │ │ │ + * obj - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.TileCache>} An exact clone of this │ │ │ │ + * <OpenLayers.Layer.TileCache> │ │ │ │ + */ │ │ │ │ + clone: function(obj) { │ │ │ │ + │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.TileCache(this.name, │ │ │ │ + this.url, │ │ │ │ + this.layername, │ │ │ │ + this.getOptions()); │ │ │ │ + } │ │ │ │ + │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getURL │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A string with the layer's url and parameters and also the │ │ │ │ + * passed-in bounds and appropriate tile size specified as parameters. │ │ │ │ + */ │ │ │ │ + getURL: function(bounds) { │ │ │ │ + var res = this.getServerResolution(); │ │ │ │ + var bbox = this.maxExtent; │ │ │ │ + var size = this.tileSize; │ │ │ │ + var tileX = Math.round((bounds.left - bbox.left) / (res * size.w)); │ │ │ │ + var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h)); │ │ │ │ + var tileZ = this.serverResolutions != null ? │ │ │ │ + OpenLayers.Util.indexOf(this.serverResolutions, res) : │ │ │ │ + this.map.getZoom(); │ │ │ │ + │ │ │ │ + var components = [ │ │ │ │ + this.layername, │ │ │ │ + OpenLayers.Number.zeroPad(tileZ, 2), │ │ │ │ + OpenLayers.Number.zeroPad(parseInt(tileX / 1000000), 3), │ │ │ │ + OpenLayers.Number.zeroPad((parseInt(tileX / 1000) % 1000), 3), │ │ │ │ + OpenLayers.Number.zeroPad((parseInt(tileX) % 1000), 3), │ │ │ │ + OpenLayers.Number.zeroPad(parseInt(tileY / 1000000), 3), │ │ │ │ + OpenLayers.Number.zeroPad((parseInt(tileY / 1000) % 1000), 3), │ │ │ │ + OpenLayers.Number.zeroPad((parseInt(tileY) % 1000), 3) + '.' + this.extension │ │ │ │ + ]; │ │ │ │ + var path = components.join('/'); │ │ │ │ + var url = this.url; │ │ │ │ + if (OpenLayers.Util.isArray(url)) { │ │ │ │ + url = this.selectUrl(path, url); │ │ │ │ + } │ │ │ │ + url = (url.charAt(url.length - 1) == '/') ? url : url + '/'; │ │ │ │ + return url + path; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.TileCache" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/SphericalMercator.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ + * @requires OpenLayers/Projection.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.SphericalMercator │ │ │ │ + * A mixin for layers that wraps up the pieces neccesary to have a coordinate │ │ │ │ + * conversion for working with commercial APIs which use a spherical │ │ │ │ + * mercator projection. Using this layer as a base layer, additional │ │ │ │ + * layers can be used as overlays if they are in the same projection. │ │ │ │ + * │ │ │ │ + * A layer is given properties of this object by setting the sphericalMercator │ │ │ │ + * property to true. │ │ │ │ + * │ │ │ │ + * More projection information: │ │ │ │ + * - http://spatialreference.org/ref/user/google-projection/ │ │ │ │ + * │ │ │ │ + * Proj4 Text: │ │ │ │ + * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 │ │ │ │ + * +k=1.0 +units=m +nadgrids=@null +no_defs │ │ │ │ + * │ │ │ │ + * WKT: │ │ │ │ + * 900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84", │ │ │ │ + * DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]], │ │ │ │ + * PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295], │ │ │ │ + * AXIS["Longitude", EAST], AXIS["Latitude", NORTH]], │ │ │ │ + * PROJECTION["Mercator_1SP_Google"], │ │ │ │ + * PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0], │ │ │ │ + * PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0], │ │ │ │ + * PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST], │ │ │ │ + * AXIS["y", NORTH], AUTHORITY["EPSG","900913"]] │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.SphericalMercator = { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getExtent │ │ │ │ + * Get the map's extent. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} The map extent. │ │ │ │ + */ │ │ │ │ + getExtent: function() { │ │ │ │ + var extent = null; │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + extent = this.map.calculateBounds(); │ │ │ │ + } else { │ │ │ │ + extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this); │ │ │ │ + } │ │ │ │ + return extent; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getLonLatFromViewPortPx │ │ │ │ + * Get a map location from a pixel location │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * viewPortPx - {<OpenLayers.Pixel>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view │ │ │ │ + * port OpenLayers.Pixel, translated into lon/lat by map lib │ │ │ │ + * If the map lib is not loaded or not centered, returns null │ │ │ │ + */ │ │ │ │ + getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ + return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getViewPortPxFromLonLat │ │ │ │ + * Get a pixel location from a map location │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * lonlat - {<OpenLayers.LonLat>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in │ │ │ │ + * OpenLayers.LonLat, translated into view port pixels by map lib │ │ │ │ + * If map lib is not loaded or not centered, returns null │ │ │ │ + */ │ │ │ │ + getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ + return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: initMercatorParameters │ │ │ │ + * Set up the mercator parameters on the layer: resolutions, │ │ │ │ + * projection, units. │ │ │ │ + */ │ │ │ │ + initMercatorParameters: function() { │ │ │ │ + // set up properties for Mercator - assume EPSG:900913 │ │ │ │ + this.RESOLUTIONS = []; │ │ │ │ + var maxResolution = 156543.03390625; │ │ │ │ + for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) { │ │ │ │ + this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom); │ │ │ │ + } │ │ │ │ + this.units = "m"; │ │ │ │ + this.projection = this.projection || "EPSG:900913"; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: forwardMercator │ │ │ │ + * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * lon - {float} │ │ │ │ + * lat - {float} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.LonLat>} The coordinates transformed to Mercator. │ │ │ │ + */ │ │ │ │ + forwardMercator: (function() { │ │ │ │ + var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ + return function(lon, lat) { │ │ │ │ + var point = OpenLayers.Projection.transform({ │ │ │ │ + x: lon, │ │ │ │ + y: lat │ │ │ │ + }, gg, sm); │ │ │ │ + return new OpenLayers.LonLat(point.x, point.y); │ │ │ │ + }; │ │ │ │ + })(), │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: inverseMercator │ │ │ │ + * Given a x,y in Spherical Mercator, return a point in EPSG:4326. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * x - {float} A map x in Spherical Mercator. │ │ │ │ + * y - {float} A map y in Spherical Mercator. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326. │ │ │ │ + */ │ │ │ │ + inverseMercator: (function() { │ │ │ │ + var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ + return function(x, y) { │ │ │ │ + var point = OpenLayers.Projection.transform({ │ │ │ │ + x: x, │ │ │ │ + y: y │ │ │ │ + }, sm, gg); │ │ │ │ + return new OpenLayers.LonLat(point.x, point.y); │ │ │ │ + }; │ │ │ │ + })() │ │ │ │ + │ │ │ │ +}; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/EventPane.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.EventPane │ │ │ │ + * Base class for 3rd party layers, providing a DOM element which isolates │ │ │ │ + * the 3rd-party layer from mouse events. │ │ │ │ + * Only used by Google layers. │ │ │ │ + * │ │ │ │ + * Automatically instantiated by the Google constructor, and not usually instantiated directly. │ │ │ │ + * │ │ │ │ + * Create a new event pane layer with the │ │ │ │ + * <OpenLayers.Layer.EventPane> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: smoothDragPan │ │ │ │ + * {Boolean} smoothDragPan determines whether non-public/internal API │ │ │ │ + * methods are used for better performance while dragging EventPane │ │ │ │ + * layers. When not in sphericalMercator mode, the smoother dragging │ │ │ │ + * doesn't actually move north/south directly with the number of │ │ │ │ + * pixels moved, resulting in a slight offset when you drag your mouse │ │ │ │ + * north south with this option on. If this visual disparity bothers │ │ │ │ + * you, you should turn this option off, or use spherical mercator. │ │ │ │ + * Default is on. │ │ │ │ + */ │ │ │ │ + smoothDragPan: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: isBaseLayer │ │ │ │ + * {Boolean} EventPaned layers are always base layers, by necessity. │ │ │ │ + */ │ │ │ │ + isBaseLayer: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isFixed │ │ │ │ + * {Boolean} EventPaned layers are fixed by default. │ │ │ │ + */ │ │ │ │ + isFixed: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: pane │ │ │ │ + * {DOMElement} A reference to the element that controls the events. │ │ │ │ + */ │ │ │ │ + pane: null, │ │ │ │ + │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: mapObject │ │ │ │ + * {Object} This is the object which will be used to load the 3rd party library │ │ │ │ + * in the case of the google layer, this will be of type GMap, │ │ │ │ + * in the case of the ve layer, this will be of type VEMap │ │ │ │ + */ │ │ │ │ + mapObject: null, │ │ │ │ + │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.EventPane │ │ │ │ + * Create a new event pane layer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + */ │ │ │ │ + initialize: function(name, options) { │ │ │ │ + OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ + if (this.pane == null) { │ │ │ │ + this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane"); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Deconstruct this layer. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.mapObject = null; │ │ │ │ + this.pane = null; │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the layer. This is done through an accessor │ │ │ │ + * so that subclasses can override this and take special action once │ │ │ │ + * they have their map variable set. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ + │ │ │ │ + this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; │ │ │ │ + this.pane.style.display = this.div.style.display; │ │ │ │ + this.pane.style.width = "100%"; │ │ │ │ + this.pane.style.height = "100%"; │ │ │ │ + if (OpenLayers.BROWSER_NAME == "msie") { │ │ │ │ + this.pane.style.background = │ │ │ │ + "url(" + OpenLayers.Util.getImageLocation("blank.gif") + ")"; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.isFixed) { │ │ │ │ + this.map.viewPortDiv.appendChild(this.pane); │ │ │ │ + } else { │ │ │ │ + this.map.layerContainerDiv.appendChild(this.pane); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // once our layer has been added to the map, we can load it │ │ │ │ + this.loadMapObject(); │ │ │ │ + │ │ │ │ + // if map didn't load, display warning │ │ │ │ + if (this.mapObject == null) { │ │ │ │ + this.loadWarningMessage(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: removeMap │ │ │ │ + * On being removed from the map, we'll like to remove the invisible 'pane' │ │ │ │ + * div that we added to it on creation. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + removeMap: function(map) { │ │ │ │ + if (this.pane && this.pane.parentNode) { │ │ │ │ + this.pane.parentNode.removeChild(this.pane); │ │ │ │ + } │ │ │ │ + OpenLayers.Layer.prototype.removeMap.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: loadWarningMessage │ │ │ │ + * If we can't load the map lib, then display an error message to the │ │ │ │ + * user and tell them where to go for help. │ │ │ │ + * │ │ │ │ + * This function sets up the layout for the warning message. Each 3rd │ │ │ │ + * party layer must implement its own getWarningHTML() function to │ │ │ │ + * provide the actual warning message. │ │ │ │ + */ │ │ │ │ + loadWarningMessage: function() { │ │ │ │ + │ │ │ │ + this.div.style.backgroundColor = "darkblue"; │ │ │ │ + │ │ │ │ + var viewSize = this.map.getSize(); │ │ │ │ + │ │ │ │ + var msgW = Math.min(viewSize.w, 300); │ │ │ │ + var msgH = Math.min(viewSize.h, 200); │ │ │ │ + var size = new OpenLayers.Size(msgW, msgH); │ │ │ │ + │ │ │ │ + var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2); │ │ │ │ + │ │ │ │ + var topLeft = centerPx.add(-size.w / 2, -size.h / 2); │ │ │ │ + │ │ │ │ + var div = OpenLayers.Util.createDiv(this.name + "_warning", │ │ │ │ + topLeft, │ │ │ │ + size, │ │ │ │ + null, │ │ │ │ + null, │ │ │ │ + null, │ │ │ │ + "auto"); │ │ │ │ + │ │ │ │ + div.style.padding = "7px"; │ │ │ │ + div.style.backgroundColor = "yellow"; │ │ │ │ + │ │ │ │ + div.innerHTML = this.getWarningHTML(); │ │ │ │ + this.div.appendChild(div); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getWarningHTML │ │ │ │ + * To be implemented by subclasses. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} String with information on why layer is broken, how to get │ │ │ │ + * it working. │ │ │ │ + */ │ │ │ │ + getWarningHTML: function() { │ │ │ │ + //should be implemented by subclasses │ │ │ │ + return ""; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: display │ │ │ │ + * Set the display on the pane │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * display - {Boolean} │ │ │ │ + */ │ │ │ │ + display: function(display) { │ │ │ │ + OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ + this.pane.style.display = this.div.style.display; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setZIndex │ │ │ │ + * Set the z-index order for the pane. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * zIndex - {int} │ │ │ │ + */ │ │ │ │ + setZIndex: function(zIndex) { │ │ │ │ + OpenLayers.Layer.prototype.setZIndex.apply(this, arguments); │ │ │ │ + this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveByPx │ │ │ │ + * Move the layer based on pixel vector. To be implemented by subclasses. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * dx - {Number} The x coord of the displacement vector. │ │ │ │ + * dy - {Number} The y coord of the displacement vector. │ │ │ │ + */ │ │ │ │ + moveByPx: function(dx, dy) { │ │ │ │ + OpenLayers.Layer.prototype.moveByPx.apply(this, arguments); │ │ │ │ + │ │ │ │ + if (this.dragPanMapObject) { │ │ │ │ + this.dragPanMapObject(dx, -dy); │ │ │ │ + } else { │ │ │ │ + this.moveTo(this.map.getCachedCenter()); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveTo │ │ │ │ + * Handle calls to move the layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * zoomChanged - {Boolean} │ │ │ │ + * dragging - {Boolean} │ │ │ │ + */ │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ + │ │ │ │ + if (this.mapObject != null) { │ │ │ │ + │ │ │ │ + var newCenter = this.map.getCenter(); │ │ │ │ + var newZoom = this.map.getZoom(); │ │ │ │ + │ │ │ │ + if (newCenter != null) { │ │ │ │ + │ │ │ │ + var moOldCenter = this.getMapObjectCenter(); │ │ │ │ + var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter); │ │ │ │ + │ │ │ │ + var moOldZoom = this.getMapObjectZoom(); │ │ │ │ + var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom); │ │ │ │ + │ │ │ │ + if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) { │ │ │ │ + │ │ │ │ + if (!zoomChanged && oldCenter && this.dragPanMapObject && │ │ │ │ + this.smoothDragPan) { │ │ │ │ + var oldPx = this.map.getViewPortPxFromLonLat(oldCenter); │ │ │ │ + var newPx = this.map.getViewPortPxFromLonLat(newCenter); │ │ │ │ + this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y); │ │ │ │ + } else { │ │ │ │ + var center = this.getMapObjectLonLatFromOLLonLat(newCenter); │ │ │ │ + var zoom = this.getMapObjectZoomFromOLZoom(newZoom); │ │ │ │ + this.setMapObjectCenter(center, zoom, dragging); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + │ │ │ │ + /********************************************************/ │ │ │ │ + /* */ │ │ │ │ + /* Baselayer Functions */ │ │ │ │ + /* */ │ │ │ │ + /********************************************************/ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getLonLatFromViewPortPx │ │ │ │ + * Get a map location from a pixel location │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * viewPortPx - {<OpenLayers.Pixel>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view │ │ │ │ + * port OpenLayers.Pixel, translated into lon/lat by map lib │ │ │ │ + * If the map lib is not loaded or not centered, returns null │ │ │ │ + */ │ │ │ │ + getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ + var lonlat = null; │ │ │ │ + if ((this.mapObject != null) && │ │ │ │ + (this.getMapObjectCenter() != null)) { │ │ │ │ + var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx); │ │ │ │ + var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel); │ │ │ │ + lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat); │ │ │ │ + } │ │ │ │ + return lonlat; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getViewPortPxFromLonLat │ │ │ │ + * Get a pixel location from a map location │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * lonlat - {<OpenLayers.LonLat>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in │ │ │ │ + * OpenLayers.LonLat, translated into view port pixels by map lib │ │ │ │ + * If map lib is not loaded or not centered, returns null │ │ │ │ + */ │ │ │ │ + getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ + var viewPortPx = null; │ │ │ │ + if ((this.mapObject != null) && │ │ │ │ + (this.getMapObjectCenter() != null)) { │ │ │ │ + │ │ │ │ + var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat); │ │ │ │ + var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat); │ │ │ │ + │ │ │ │ + viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel); │ │ │ │ + } │ │ │ │ + return viewPortPx; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /********************************************************/ │ │ │ │ + /* */ │ │ │ │ + /* Translation Functions */ │ │ │ │ + /* */ │ │ │ │ + /* The following functions translate Map Object and */ │ │ │ │ + /* OL formats for Pixel, LonLat */ │ │ │ │ + /* */ │ │ │ │ + /********************************************************/ │ │ │ │ + │ │ │ │ + // │ │ │ │ + // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat │ │ │ │ + // │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getOLLonLatFromMapObjectLonLat │ │ │ │ + * Get an OL style map location from a 3rd party style map location │ │ │ │ + * │ │ │ │ + * Parameters │ │ │ │ + * moLonLat - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in │ │ │ │ + * MapObject LonLat │ │ │ │ + * Returns null if null value is passed in │ │ │ │ + */ │ │ │ │ + getOLLonLatFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + var olLonLat = null; │ │ │ │ + if (moLonLat != null) { │ │ │ │ + var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ + var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ + olLonLat = new OpenLayers.LonLat(lon, lat); │ │ │ │ + } │ │ │ │ + return olLonLat; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getMapObjectLonLatFromOLLonLat │ │ │ │ + * Get a 3rd party map location from an OL map location. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * olLonLat - {<OpenLayers.LonLat>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} A MapObject LonLat, translated from the passed in │ │ │ │ + * OpenLayers.LonLat │ │ │ │ + * Returns null if null value is passed in │ │ │ │ + */ │ │ │ │ + getMapObjectLonLatFromOLLonLat: function(olLonLat) { │ │ │ │ + var moLatLng = null; │ │ │ │ + if (olLonLat != null) { │ │ │ │ + moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, │ │ │ │ + olLonLat.lat); │ │ │ │ + } │ │ │ │ + return moLatLng; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + │ │ │ │ + // │ │ │ │ + // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel │ │ │ │ + // │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getOLPixelFromMapObjectPixel │ │ │ │ + * Get an OL pixel location from a 3rd party pixel location. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moPixel - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in │ │ │ │ + * MapObject Pixel │ │ │ │ + * Returns null if null value is passed in │ │ │ │ + */ │ │ │ │ + getOLPixelFromMapObjectPixel: function(moPixel) { │ │ │ │ + var olPixel = null; │ │ │ │ + if (moPixel != null) { │ │ │ │ + var x = this.getXFromMapObjectPixel(moPixel); │ │ │ │ + var y = this.getYFromMapObjectPixel(moPixel); │ │ │ │ + olPixel = new OpenLayers.Pixel(x, y); │ │ │ │ + } │ │ │ │ + return olPixel; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getMapObjectPixelFromOLPixel │ │ │ │ + * Get a 3rd party pixel location from an OL pixel location │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * olPixel - {<OpenLayers.Pixel>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} A MapObject Pixel, translated from the passed in │ │ │ │ + * OpenLayers.Pixel │ │ │ │ + * Returns null if null value is passed in │ │ │ │ + */ │ │ │ │ + getMapObjectPixelFromOLPixel: function(olPixel) { │ │ │ │ + var moPixel = null; │ │ │ │ + if (olPixel != null) { │ │ │ │ + moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y); │ │ │ │ + } │ │ │ │ + return moPixel; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.EventPane" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/FixedZoomLevels.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.FixedZoomLevels │ │ │ │ + * Some Layers will already have established zoom levels (like google │ │ │ │ + * or ve). Instead of trying to determine them and populate a resolutions[] │ │ │ │ + * Array with those values, we will hijack the resolution functionality │ │ │ │ + * here. │ │ │ │ + * │ │ │ │ + * When you subclass FixedZoomLevels: │ │ │ │ + * │ │ │ │ + * The initResolutions() call gets nullified, meaning no resolutions[] array │ │ │ │ + * is set up. Which would be a big problem getResolution() in Layer, since │ │ │ │ + * it merely takes map.zoom and indexes into resolutions[]... but.... │ │ │ │ + * │ │ │ │ + * The getResolution() call is also overridden. Instead of using the │ │ │ │ + * resolutions[] array, we simply calculate the current resolution based │ │ │ │ + * on the current extent and the current map size. But how will we be able │ │ │ │ + * to calculate the current extent without knowing the resolution...? │ │ │ │ + * │ │ │ │ + * The getExtent() function is also overridden. Instead of calculating extent │ │ │ │ + * based on the center point and the current resolution, we instead │ │ │ │ + * calculate the extent by getting the lonlats at the top-left and │ │ │ │ + * bottom-right by using the getLonLatFromViewPortPx() translation function, │ │ │ │ + * taken from the pixel locations (0,0) and the size of the map. But how │ │ │ │ + * will we be able to do lonlat-px translation without resolution....? │ │ │ │ + * │ │ │ │ + * The getZoomForResolution() method is overridden. Instead of indexing into │ │ │ │ + * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in │ │ │ │ + * the desired resolution. With this extent, we then call getZoomForExtent() │ │ │ │ + * │ │ │ │ + * │ │ │ │ + * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels, │ │ │ │ + * it is your responsibility to provide the following three functions: │ │ │ │ + * │ │ │ │ + * - getLonLatFromViewPortPx │ │ │ │ + * - getViewPortPxFromLonLat │ │ │ │ + * - getZoomForExtent │ │ │ │ + * │ │ │ │ + * ...those three functions should generally be provided by any reasonable │ │ │ │ + * API that you might be working from. │ │ │ │ + * │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({ │ │ │ │ + │ │ │ │ + /********************************************************/ │ │ │ │ + /* */ │ │ │ │ + /* Baselayer Functions */ │ │ │ │ + /* */ │ │ │ │ + /* The following functions must all be implemented */ │ │ │ │ + /* by all base layers */ │ │ │ │ + /* */ │ │ │ │ + /********************************************************/ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.FixedZoomLevels │ │ │ │ + * Create a new fixed zoom levels layer. │ │ │ │ + */ │ │ │ │ + initialize: function() { │ │ │ │ + //this class is only just to add the following functions... │ │ │ │ + // nothing to actually do here... but it is probably a good │ │ │ │ + // idea to have layers that use these functions call this │ │ │ │ + // inititalize() anyways, in case at some point we decide we │ │ │ │ + // do want to put some functionality or state in here. │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: initResolutions │ │ │ │ + * Populate the resolutions array │ │ │ │ + */ │ │ │ │ + initResolutions: function() { │ │ │ │ + │ │ │ │ + var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels']; │ │ │ │ + │ │ │ │ + for (var i = 0, len = props.length; i < len; i++) { │ │ │ │ + var property = props[i]; │ │ │ │ + this[property] = (this.options[property] != null) ? │ │ │ │ + this.options[property] : │ │ │ │ + this.map[property]; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if ((this.minZoomLevel == null) || │ │ │ │ + (this.minZoomLevel < this.MIN_ZOOM_LEVEL)) { │ │ │ │ + this.minZoomLevel = this.MIN_ZOOM_LEVEL; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // │ │ │ │ + // At this point, we know what the minimum desired zoom level is, and │ │ │ │ + // we must calculate the total number of zoom levels. │ │ │ │ + // │ │ │ │ + // Because we allow for the setting of either the 'numZoomLevels' │ │ │ │ + // or the 'maxZoomLevel' properties... on either the layer or the │ │ │ │ + // map, we have to define some rules to see which we take into │ │ │ │ + // account first in this calculation. │ │ │ │ + // │ │ │ │ + // The following is the precedence list for these properties: │ │ │ │ + // │ │ │ │ + // (1) numZoomLevels set on layer │ │ │ │ + // (2) maxZoomLevel set on layer │ │ │ │ + // (3) numZoomLevels set on map │ │ │ │ + // (4) maxZoomLevel set on map* │ │ │ │ + // (5) none of the above* │ │ │ │ + // │ │ │ │ + // *Note that options (4) and (5) are only possible if the user │ │ │ │ + // _explicitly_ sets the 'numZoomLevels' property on the map to │ │ │ │ + // null, since it is set by default to 16. │ │ │ │ + // │ │ │ │ + │ │ │ │ + // │ │ │ │ + // Note to future: In 3.0, I think we should remove the default │ │ │ │ + // value of 16 for map.numZoomLevels. Rather, I think that value │ │ │ │ + // should be set as a default on the Layer.WMS class. If someone │ │ │ │ + // creates a 3rd party layer and does not specify any 'minZoomLevel', │ │ │ │ + // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly │ │ │ │ + // specified any of those on the map object either.. then I think │ │ │ │ + // it is fair to say that s/he wants all the zoom levels available. │ │ │ │ + // │ │ │ │ + // By making map.numZoomLevels *null* by default, that will be the │ │ │ │ + // case. As it is, I don't feel comfortable changing that right now │ │ │ │ + // as it would be a glaring API change and actually would probably │ │ │ │ + // break many peoples' codes. │ │ │ │ + // │ │ │ │ + │ │ │ │ + //the number of zoom levels we'd like to have. │ │ │ │ + var desiredZoomLevels; │ │ │ │ + │ │ │ │ + //this is the maximum number of zoom levels the layer will allow, │ │ │ │ + // given the specified starting minimum zoom level. │ │ │ │ + var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1; │ │ │ │ + │ │ │ │ + if (((this.options.numZoomLevels == null) && │ │ │ │ + (this.options.maxZoomLevel != null)) // (2) │ │ │ │ + || │ │ │ │ + ((this.numZoomLevels == null) && │ │ │ │ + (this.maxZoomLevel != null)) // (4) │ │ │ │ + ) { │ │ │ │ + //calculate based on specified maxZoomLevel (on layer or map) │ │ │ │ + desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1; │ │ │ │ + } else { │ │ │ │ + //calculate based on specified numZoomLevels (on layer or map) │ │ │ │ + // this covers cases (1) and (3) │ │ │ │ + desiredZoomLevels = this.numZoomLevels; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (desiredZoomLevels != null) { │ │ │ │ + //Now that we know what we would *like* the number of zoom levels │ │ │ │ + // to be, based on layer or map options, we have to make sure that │ │ │ │ + // it does not conflict with the actual limit, as specified by │ │ │ │ + // the constants on the layer itself (and calculated into the │ │ │ │ + // 'limitZoomLevels' variable). │ │ │ │ + this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels); │ │ │ │ + } else { │ │ │ │ + // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was │ │ │ │ + // set on either the layer or the map. So we just use the │ │ │ │ + // maximum limit as calculated by the layer's constants. │ │ │ │ + this.numZoomLevels = limitZoomLevels; │ │ │ │ + } │ │ │ │ + │ │ │ │ + //now that the 'numZoomLevels' is appropriately, safely set, │ │ │ │ + // we go back and re-calculate the 'maxZoomLevel'. │ │ │ │ + this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1; │ │ │ │ + │ │ │ │ + if (this.RESOLUTIONS != null) { │ │ │ │ + var resolutionsIndex = 0; │ │ │ │ + this.resolutions = []; │ │ │ │ + for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) { │ │ │ │ + this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i]; │ │ │ │ + } │ │ │ │ + this.maxResolution = this.resolutions[0]; │ │ │ │ + this.minResolution = this.resolutions[this.resolutions.length - 1]; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getResolution │ │ │ │ + * Get the current map resolution │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} Map units per Pixel │ │ │ │ + */ │ │ │ │ + getResolution: function() { │ │ │ │ + │ │ │ │ + if (this.resolutions != null) { │ │ │ │ + return OpenLayers.Layer.prototype.getResolution.apply(this, arguments); │ │ │ │ + } else { │ │ │ │ + var resolution = null; │ │ │ │ + │ │ │ │ + var viewSize = this.map.getSize(); │ │ │ │ + var extent = this.getExtent(); │ │ │ │ + │ │ │ │ + if ((viewSize != null) && (extent != null)) { │ │ │ │ + resolution = Math.max(extent.getWidth() / viewSize.w, │ │ │ │ + extent.getHeight() / viewSize.h); │ │ │ │ + } │ │ │ │ + return resolution; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getExtent │ │ │ │ + * Calculates using px-> lonlat translation functions on tl and br │ │ │ │ + * corners of viewport │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat │ │ │ │ + * bounds of the current viewPort. │ │ │ │ + */ │ │ │ │ + getExtent: function() { │ │ │ │ + var size = this.map.getSize(); │ │ │ │ + var tl = this.getLonLatFromViewPortPx({ │ │ │ │ + x: 0, │ │ │ │ + y: 0 │ │ │ │ + }); │ │ │ │ + var br = this.getLonLatFromViewPortPx({ │ │ │ │ + x: size.w, │ │ │ │ + y: size.h │ │ │ │ + }); │ │ │ │ + │ │ │ │ + if ((tl != null) && (br != null)) { │ │ │ │ + return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat); │ │ │ │ + } else { │ │ │ │ + return null; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getZoomForResolution │ │ │ │ + * Get the zoom level for a given resolution │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * resolution - {Float} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} A suitable zoom level for the specified resolution. │ │ │ │ + * If no baselayer is set, returns null. │ │ │ │ + */ │ │ │ │ + getZoomForResolution: function(resolution) { │ │ │ │ + │ │ │ │ + if (this.resolutions != null) { │ │ │ │ + return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments); │ │ │ │ + } else { │ │ │ │ + var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []); │ │ │ │ + return this.getZoomForExtent(extent); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + /********************************************************/ │ │ │ │ + /* */ │ │ │ │ + /* Translation Functions */ │ │ │ │ + /* */ │ │ │ │ + /* The following functions translate GMaps and OL */ │ │ │ │ + /* formats for Pixel, LonLat, Bounds, and Zoom */ │ │ │ │ + /* */ │ │ │ │ + /********************************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // │ │ │ │ + // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom │ │ │ │ + // │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getOLZoomFromMapObjectZoom │ │ │ │ + * Get the OL zoom index from the map object zoom level │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moZoom - {Integer} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} An OpenLayers Zoom level, translated from the passed in zoom │ │ │ │ + * Returns null if null value is passed in │ │ │ │ + */ │ │ │ │ + getOLZoomFromMapObjectZoom: function(moZoom) { │ │ │ │ + var zoom = null; │ │ │ │ + if (moZoom != null) { │ │ │ │ + zoom = moZoom - this.minZoomLevel; │ │ │ │ + if (this.map.baseLayer !== this) { │ │ │ │ + zoom = this.map.baseLayer.getZoomForResolution( │ │ │ │ + this.getResolutionForZoom(zoom) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return zoom; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getMapObjectZoomFromOLZoom │ │ │ │ + * Get the map object zoom level from the OL zoom level │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * olZoom - {Integer} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} A MapObject level, translated from the passed in olZoom │ │ │ │ + * Returns null if null value is passed in │ │ │ │ + */ │ │ │ │ + getMapObjectZoomFromOLZoom: function(olZoom) { │ │ │ │ + var zoom = null; │ │ │ │ + if (olZoom != null) { │ │ │ │ + zoom = olZoom + this.minZoomLevel; │ │ │ │ + if (this.map.baseLayer !== this) { │ │ │ │ + zoom = this.getZoomForResolution( │ │ │ │ + this.map.baseLayer.getResolutionForZoom(zoom) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return zoom; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels" │ │ │ │ +}); │ │ │ │ + │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/Google.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/SphericalMercator.js │ │ │ │ + * @requires OpenLayers/Layer/EventPane.js │ │ │ │ + * @requires OpenLayers/Layer/FixedZoomLevels.js │ │ │ │ + * @requires OpenLayers/Lang.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.Google │ │ │ │ + * │ │ │ │ + * Provides a wrapper for Google's Maps API │ │ │ │ + * Normally the Terms of Use for this API do not allow wrapping, but Google │ │ │ │ + * have provided written consent to OpenLayers for this - see email in │ │ │ │ + * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.SphericalMercator> │ │ │ │ + * - <OpenLayers.Layer.EventPane> │ │ │ │ + * - <OpenLayers.Layer.FixedZoomLevels> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Google = OpenLayers.Class( │ │ │ │ + OpenLayers.Layer.EventPane, │ │ │ │ + OpenLayers.Layer.FixedZoomLevels, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: MIN_ZOOM_LEVEL │ │ │ │ + * {Integer} 0 │ │ │ │ + */ │ │ │ │ + MIN_ZOOM_LEVEL: 0, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: MAX_ZOOM_LEVEL │ │ │ │ + * {Integer} 21 │ │ │ │ + */ │ │ │ │ + MAX_ZOOM_LEVEL: 21, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: RESOLUTIONS │ │ │ │ + * {Array(Float)} Hardcode these resolutions so that they are more closely │ │ │ │ + * tied with the standard wms projection │ │ │ │ + */ │ │ │ │ + RESOLUTIONS: [ │ │ │ │ + 1.40625, │ │ │ │ + 0.703125, │ │ │ │ + 0.3515625, │ │ │ │ + 0.17578125, │ │ │ │ + 0.087890625, │ │ │ │ + 0.0439453125, │ │ │ │ + 0.02197265625, │ │ │ │ + 0.010986328125, │ │ │ │ + 0.0054931640625, │ │ │ │ + 0.00274658203125, │ │ │ │ + 0.001373291015625, │ │ │ │ + 0.0006866455078125, │ │ │ │ + 0.00034332275390625, │ │ │ │ + 0.000171661376953125, │ │ │ │ + 0.0000858306884765625, │ │ │ │ + 0.00004291534423828125, │ │ │ │ + 0.00002145767211914062, │ │ │ │ + 0.00001072883605957031, │ │ │ │ + 0.00000536441802978515, │ │ │ │ + 0.00000268220901489257, │ │ │ │ + 0.0000013411045074462891, │ │ │ │ + 0.00000067055225372314453 │ │ │ │ + ], │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: type │ │ │ │ + * {GMapType} │ │ │ │ + */ │ │ │ │ + type: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: wrapDateLine │ │ │ │ + * {Boolean} Allow user to pan forever east/west. Default is true. │ │ │ │ + * Setting this to false only restricts panning if │ │ │ │ + * <sphericalMercator> is true. │ │ │ │ + */ │ │ │ │ + wrapDateLine: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: sphericalMercator │ │ │ │ + * {Boolean} Should the map act as a mercator-projected map? This will │ │ │ │ + * cause all interactions with the map to be in the actual map │ │ │ │ + * projection, which allows support for vector drawing, overlaying │ │ │ │ + * other maps, etc. │ │ │ │ + */ │ │ │ │ + sphericalMercator: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: version │ │ │ │ + * {Number} The version of the Google Maps API │ │ │ │ + */ │ │ │ │ + version: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Google │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} A name for the layer. │ │ │ │ + * options - {Object} An optional object whose properties will be set │ │ │ │ + * on the layer. │ │ │ │ + */ │ │ │ │ + initialize: function(name, options) { │ │ │ │ + options = options || {}; │ │ │ │ + if (!options.version) { │ │ │ │ + options.version = typeof GMap2 === "function" ? "2" : "3"; │ │ │ │ + } │ │ │ │ + var mixin = OpenLayers.Layer.Google["v" + │ │ │ │ + options.version.replace(/\./g, "_")]; │ │ │ │ + if (mixin) { │ │ │ │ + OpenLayers.Util.applyDefaults(options, mixin); │ │ │ │ + } else { │ │ │ │ + throw "Unsupported Google Maps API version: " + options.version; │ │ │ │ + } │ │ │ │ + │ │ │ │ + OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS); │ │ │ │ + if (options.maxExtent) { │ │ │ │ + options.maxExtent = options.maxExtent.clone(); │ │ │ │ + } │ │ │ │ + │ │ │ │ + OpenLayers.Layer.EventPane.prototype.initialize.apply(this, │ │ │ │ + [name, options]); │ │ │ │ + OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, │ │ │ │ + [name, options]); │ │ │ │ + │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator); │ │ │ │ + this.initMercatorParameters(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.Google>} An exact clone of this layer │ │ │ │ + */ │ │ │ │ + clone: function() { │ │ │ │ + /** │ │ │ │ + * This method isn't intended to be called by a subclass and it │ │ │ │ + * doesn't call the same method on the superclass. We don't call │ │ │ │ + * the super's clone because we don't want properties that are set │ │ │ │ + * on this layer after initialize (i.e. this.mapObject etc.). │ │ │ │ + */ │ │ │ │ + return new OpenLayers.Layer.Google( │ │ │ │ + this.name, this.getOptions() │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: setVisibility │ │ │ │ + * Set the visibility flag for the layer and hide/show & redraw │ │ │ │ + * accordingly. Fire event unless otherwise specified │ │ │ │ + * │ │ │ │ + * Note that visibility is no longer simply whether or not the layer's │ │ │ │ + * style.display is set to "block". Now we store a 'visibility' state │ │ │ │ + * property on the layer class, this allows us to remember whether or │ │ │ │ + * not we *desire* for a layer to be visible. In the case where the │ │ │ │ + * map's resolution is out of the layer's range, this desire may be │ │ │ │ + * subverted. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * visible - {Boolean} Display the layer (if in range) │ │ │ │ + */ │ │ │ │ + setVisibility: function(visible) { │ │ │ │ + // sharing a map container, opacity has to be set per layer │ │ │ │ + var opacity = this.opacity == null ? 1 : this.opacity; │ │ │ │ + OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments); │ │ │ │ + this.setOpacity(opacity); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: display │ │ │ │ + * Hide or show the Layer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * visible - {Boolean} │ │ │ │ + */ │ │ │ │ + display: function(visible) { │ │ │ │ + if (!this._dragging) { │ │ │ │ + this.setGMapVisibility(visible); │ │ │ │ + } │ │ │ │ + OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveTo │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to │ │ │ │ + * do some init work in that case. │ │ │ │ + * dragging - {Boolean} │ │ │ │ + */ │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + this._dragging = dragging; │ │ │ │ + OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments); │ │ │ │ + delete this._dragging; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: setOpacity │ │ │ │ + * Sets the opacity for the entire layer (all images) │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * opacity - {Float} │ │ │ │ + */ │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ + if (opacity !== this.opacity) { │ │ │ │ + if (this.map != null) { │ │ │ │ + this.map.events.triggerEvent("changelayer", { │ │ │ │ + layer: this, │ │ │ │ + property: "opacity" │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + this.opacity = opacity; │ │ │ │ + } │ │ │ │ + // Though this layer's opacity may not change, we're sharing a container │ │ │ │ + // and need to update the opacity for the entire container. │ │ │ │ + if (this.getVisibility()) { │ │ │ │ + var container = this.getMapContainer(); │ │ │ │ + OpenLayers.Util.modifyDOMElement( │ │ │ │ + container, null, null, null, null, null, null, opacity │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up this layer. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + /** │ │ │ │ + * We have to override this method because the event pane destroy │ │ │ │ + * deletes the mapObject reference before removing this layer from │ │ │ │ + * the map. │ │ │ │ + */ │ │ │ │ + if (this.map) { │ │ │ │ + this.setGMapVisibility(false); │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache && cache.count <= 1) { │ │ │ │ + this.removeGMapElements(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: removeGMapElements │ │ │ │ + * Remove all elements added to the dom. This should only be called if │ │ │ │ + * this is the last of the Google layers for the given map. │ │ │ │ + */ │ │ │ │ + removeGMapElements: function() { │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache) { │ │ │ │ + // remove shared elements from dom │ │ │ │ + var container = this.mapObject && this.getMapContainer(); │ │ │ │ + if (container && container.parentNode) { │ │ │ │ + container.parentNode.removeChild(container); │ │ │ │ + } │ │ │ │ + var termsOfUse = cache.termsOfUse; │ │ │ │ + if (termsOfUse && termsOfUse.parentNode) { │ │ │ │ + termsOfUse.parentNode.removeChild(termsOfUse); │ │ │ │ + } │ │ │ │ + var poweredBy = cache.poweredBy; │ │ │ │ + if (poweredBy && poweredBy.parentNode) { │ │ │ │ + poweredBy.parentNode.removeChild(poweredBy); │ │ │ │ + } │ │ │ │ + if (this.mapObject && window.google && google.maps && │ │ │ │ + google.maps.event && google.maps.event.clearListeners) { │ │ │ │ + google.maps.event.clearListeners(this.mapObject, 'tilesloaded'); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: removeMap │ │ │ │ + * On being removed from the map, also remove termsOfUse and poweredBy divs │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + removeMap: function(map) { │ │ │ │ + // hide layer before removing │ │ │ │ + if (this.visibility && this.mapObject) { │ │ │ │ + this.setGMapVisibility(false); │ │ │ │ + } │ │ │ │ + // check to see if last Google layer in this map │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[map.id]; │ │ │ │ + if (cache) { │ │ │ │ + if (cache.count <= 1) { │ │ │ │ + this.removeGMapElements(); │ │ │ │ + delete OpenLayers.Layer.Google.cache[map.id]; │ │ │ │ + } else { │ │ │ │ + // decrement the layer count │ │ │ │ + --cache.count; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + // remove references to gmap elements │ │ │ │ + delete this.termsOfUse; │ │ │ │ + delete this.poweredBy; │ │ │ │ + delete this.mapObject; │ │ │ │ + delete this.dragObject; │ │ │ │ + OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + // │ │ │ │ + // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds │ │ │ │ + // │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getOLBoundsFromMapObjectBounds │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moBounds - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the │ │ │ │ + * passed-in MapObject Bounds. │ │ │ │ + * Returns null if null value is passed in. │ │ │ │ + */ │ │ │ │ + getOLBoundsFromMapObjectBounds: function(moBounds) { │ │ │ │ + var olBounds = null; │ │ │ │ + if (moBounds != null) { │ │ │ │ + var sw = moBounds.getSouthWest(); │ │ │ │ + var ne = moBounds.getNorthEast(); │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + sw = this.forwardMercator(sw.lng(), sw.lat()); │ │ │ │ + ne = this.forwardMercator(ne.lng(), ne.lat()); │ │ │ │ + } else { │ │ │ │ + sw = new OpenLayers.LonLat(sw.lng(), sw.lat()); │ │ │ │ + ne = new OpenLayers.LonLat(ne.lng(), ne.lat()); │ │ │ │ + } │ │ │ │ + olBounds = new OpenLayers.Bounds(sw.lon, │ │ │ │ + sw.lat, │ │ │ │ + ne.lon, │ │ │ │ + ne.lat); │ │ │ │ + } │ │ │ │ + return olBounds; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getWarningHTML │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} String with information on why layer is broken, how to get │ │ │ │ + * it working. │ │ │ │ + */ │ │ │ │ + getWarningHTML: function() { │ │ │ │ + return OpenLayers.i18n("googleWarning"); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + │ │ │ │ + /************************************ │ │ │ │ + * * │ │ │ │ + * MapObject Interface Controls * │ │ │ │ + * * │ │ │ │ + ************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // Get&Set Center, Zoom │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getMapObjectCenter │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} The mapObject's current center in Map Object format │ │ │ │ + */ │ │ │ │ + getMapObjectCenter: function() { │ │ │ │ + return this.mapObject.getCenter(); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getMapObjectZoom │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} The mapObject's current zoom, in Map Object format │ │ │ │ + */ │ │ │ │ + getMapObjectZoom: function() { │ │ │ │ + return this.mapObject.getZoom(); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + │ │ │ │ + /************************************ │ │ │ │ + * * │ │ │ │ + * MapObject Primitives * │ │ │ │ + * * │ │ │ │ + ************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // LonLat │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getLongitudeFromMapObjectLonLat │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moLonLat - {Object} MapObject LonLat format │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} Longitude of the given MapObject LonLat │ │ │ │ + */ │ │ │ │ + getLongitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + return this.sphericalMercator ? │ │ │ │ + this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : │ │ │ │ + moLonLat.lng(); │ │ │ │ + }, │ │ │ │ │ │ │ │ - while (rightIndex - leftIndex > 1) { │ │ │ │ - middle = parseInt((leftIndex + rightIndex) / 2); │ │ │ │ + /** │ │ │ │ + * APIMethod: getLatitudeFromMapObjectLonLat │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moLonLat - {Object} MapObject LonLat format │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} Latitude of the given MapObject LonLat │ │ │ │ + */ │ │ │ │ + getLatitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + var lat = this.sphericalMercator ? │ │ │ │ + this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : │ │ │ │ + moLonLat.lat(); │ │ │ │ + return lat; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var placement = this.compare(this, newNode, │ │ │ │ - OpenLayers.Util.getElement(this.order[middle])); │ │ │ │ + // Pixel │ │ │ │ │ │ │ │ - if (placement > 0) { │ │ │ │ - leftIndex = middle; │ │ │ │ - } else { │ │ │ │ - rightIndex = middle; │ │ │ │ + /** │ │ │ │ + * APIMethod: getXFromMapObjectPixel │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moPixel - {Object} MapObject Pixel format │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} X value of the MapObject Pixel │ │ │ │ + */ │ │ │ │ + getXFromMapObjectPixel: function(moPixel) { │ │ │ │ + return moPixel.x; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getYFromMapObjectPixel │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moPixel - {Object} MapObject Pixel format │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} Y value of the MapObject Pixel │ │ │ │ + */ │ │ │ │ + getYFromMapObjectPixel: function(moPixel) { │ │ │ │ + return moPixel.y; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Google" │ │ │ │ + }); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Property: OpenLayers.Layer.Google.cache │ │ │ │ + * {Object} Cache for elements that should only be created once per map. │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Google.cache = {}; │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Layer.Google.v2 │ │ │ │ + * │ │ │ │ + * Mixin providing functionality specific to the Google Maps API v2. │ │ │ │ + * │ │ │ │ + * This API has been deprecated by Google. │ │ │ │ + * Developers are encouraged to migrate to v3 of the API; support for this │ │ │ │ + * is provided by <OpenLayers.Layer.Google.v3> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Google.v2 = { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: termsOfUse │ │ │ │ + * {DOMElement} Div for Google's copyright and terms of use link │ │ │ │ + */ │ │ │ │ + termsOfUse: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: poweredBy │ │ │ │ + * {DOMElement} Div for Google's powered by logo and link │ │ │ │ + */ │ │ │ │ + poweredBy: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: dragObject │ │ │ │ + * {GDraggableObject} Since 2.93, Google has exposed the ability to get │ │ │ │ + * the maps GDraggableObject. We can now use this for smooth panning │ │ │ │ + */ │ │ │ │ + dragObject: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: loadMapObject │ │ │ │ + * Load the GMap and register appropriate event listeners. If we can't │ │ │ │ + * load GMap2, then display a warning message. │ │ │ │ + */ │ │ │ │ + loadMapObject: function() { │ │ │ │ + if (!this.type) { │ │ │ │ + this.type = G_NORMAL_MAP; │ │ │ │ + } │ │ │ │ + var mapObject, termsOfUse, poweredBy; │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache) { │ │ │ │ + // there are already Google layers added to this map │ │ │ │ + mapObject = cache.mapObject; │ │ │ │ + termsOfUse = cache.termsOfUse; │ │ │ │ + poweredBy = cache.poweredBy; │ │ │ │ + // increment the layer count │ │ │ │ + ++cache.count; │ │ │ │ + } else { │ │ │ │ + // this is the first Google layer for this map │ │ │ │ + │ │ │ │ + var container = this.map.viewPortDiv; │ │ │ │ + var div = document.createElement("div"); │ │ │ │ + div.id = this.map.id + "_GMap2Container"; │ │ │ │ + div.style.position = "absolute"; │ │ │ │ + div.style.width = "100%"; │ │ │ │ + div.style.height = "100%"; │ │ │ │ + container.appendChild(div); │ │ │ │ + │ │ │ │ + // create GMap and shuffle elements │ │ │ │ + try { │ │ │ │ + mapObject = new GMap2(div); │ │ │ │ + │ │ │ │ + // move the ToS and branding stuff up to the container div │ │ │ │ + termsOfUse = div.lastChild; │ │ │ │ + container.appendChild(termsOfUse); │ │ │ │ + termsOfUse.style.zIndex = "1100"; │ │ │ │ + termsOfUse.style.right = ""; │ │ │ │ + termsOfUse.style.bottom = ""; │ │ │ │ + termsOfUse.className = "olLayerGoogleCopyright"; │ │ │ │ + │ │ │ │ + poweredBy = div.lastChild; │ │ │ │ + container.appendChild(poweredBy); │ │ │ │ + poweredBy.style.zIndex = "1100"; │ │ │ │ + poweredBy.style.right = ""; │ │ │ │ + poweredBy.style.bottom = ""; │ │ │ │ + poweredBy.className = "olLayerGooglePoweredBy gmnoprint"; │ │ │ │ + │ │ │ │ + } catch (e) { │ │ │ │ + throw (e); │ │ │ │ } │ │ │ │ + // cache elements for use by any other google layers added to │ │ │ │ + // this same map │ │ │ │ + OpenLayers.Layer.Google.cache[this.map.id] = { │ │ │ │ + mapObject: mapObject, │ │ │ │ + termsOfUse: termsOfUse, │ │ │ │ + poweredBy: poweredBy, │ │ │ │ + count: 1 │ │ │ │ + }; │ │ │ │ } │ │ │ │ │ │ │ │ - this.order.splice(rightIndex, 0, nodeId); │ │ │ │ - this.indices[nodeId] = this.getZIndex(newNode); │ │ │ │ + this.mapObject = mapObject; │ │ │ │ + this.termsOfUse = termsOfUse; │ │ │ │ + this.poweredBy = poweredBy; │ │ │ │ + │ │ │ │ + // ensure this layer type is one of the mapObject types │ │ │ │ + if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), │ │ │ │ + this.type) === -1) { │ │ │ │ + this.mapObject.addMapType(this.type); │ │ │ │ + } │ │ │ │ + │ │ │ │ + //since v 2.93 getDragObject is now available. │ │ │ │ + if (typeof mapObject.getDragObject == "function") { │ │ │ │ + this.dragObject = mapObject.getDragObject(); │ │ │ │ + } else { │ │ │ │ + this.dragPanMapObject = null; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.isBaseLayer === false) { │ │ │ │ + this.setGMapVisibility(this.div.style.display !== "none"); │ │ │ │ + } │ │ │ │ │ │ │ │ - // 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 │ │ │ │ + * APIMethod: onMapResize │ │ │ │ + */ │ │ │ │ + onMapResize: function() { │ │ │ │ + // workaround for resizing of invisible or not yet fully loaded layers │ │ │ │ + // where GMap2.checkResize() does not work. We need to load the GMap │ │ │ │ + // for the old div size, then checkResize(), and then call │ │ │ │ + // layer.moveTo() to trigger GMap.setCenter() (which will finish │ │ │ │ + // the GMap initialization). │ │ │ │ + if (this.visibility && this.mapObject.isLoaded()) { │ │ │ │ + this.mapObject.checkResize(); │ │ │ │ + } else { │ │ │ │ + if (!this._resized) { │ │ │ │ + var layer = this; │ │ │ │ + var handle = GEvent.addListener(this.mapObject, "load", function() { │ │ │ │ + GEvent.removeListener(handle); │ │ │ │ + delete layer._resized; │ │ │ │ + layer.mapObject.checkResize(); │ │ │ │ + layer.moveTo(layer.map.getCenter(), layer.map.getZoom()); │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + this._resized = true; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setGMapVisibility │ │ │ │ + * Display the GMap container and associated elements. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} The node to be removed. │ │ │ │ + * visible - {Boolean} Display the GMap elements. │ │ │ │ */ │ │ │ │ - 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]; │ │ │ │ + setGMapVisibility: function(visible) { │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache) { │ │ │ │ + var container = this.mapObject.getContainer(); │ │ │ │ + if (visible === true) { │ │ │ │ + this.mapObject.setMapType(this.type); │ │ │ │ + container.style.display = ""; │ │ │ │ + this.termsOfUse.style.left = ""; │ │ │ │ + this.termsOfUse.style.display = ""; │ │ │ │ + this.poweredBy.style.display = ""; │ │ │ │ + cache.displayed = this.id; │ │ │ │ } else { │ │ │ │ - this.maxZIndex = 0; │ │ │ │ + if (cache.displayed === this.id) { │ │ │ │ + delete cache.displayed; │ │ │ │ + } │ │ │ │ + if (!cache.displayed) { │ │ │ │ + container.style.display = "none"; │ │ │ │ + this.termsOfUse.style.display = "none"; │ │ │ │ + // move ToU far to the left in addition to setting display │ │ │ │ + // to "none", because at the end of the GMap2 load │ │ │ │ + // sequence, display: none will be unset and ToU would be │ │ │ │ + // visible after loading a map with a google layer that is │ │ │ │ + // initially hidden. │ │ │ │ + this.termsOfUse.style.left = "-9999px"; │ │ │ │ + this.poweredBy.style.display = "none"; │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clear │ │ │ │ + * Method: getMapContainer │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} the GMap container's div │ │ │ │ */ │ │ │ │ - clear: function() { │ │ │ │ - this.order = []; │ │ │ │ - this.indices = {}; │ │ │ │ - this.maxZIndex = 0; │ │ │ │ + getMapContainer: function() { │ │ │ │ + return this.mapObject.getContainer(); │ │ │ │ }, │ │ │ │ │ │ │ │ + // │ │ │ │ + // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds │ │ │ │ + // │ │ │ │ + │ │ │ │ /** │ │ │ │ - * APIMethod: exists │ │ │ │ - * │ │ │ │ + * APIMethod: getMapObjectBoundsFromOLBounds │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} The node to test for existence. │ │ │ │ - * │ │ │ │ + * olBounds - {<OpenLayers.Bounds>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Whether or not the node exists in the indexer? │ │ │ │ + * {Object} A MapObject Bounds, translated from olBounds │ │ │ │ + * Returns null if null value is passed in │ │ │ │ */ │ │ │ │ - exists: function(node) { │ │ │ │ - return (this.indices[node.id] != null); │ │ │ │ + getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ + var moBounds = null; │ │ │ │ + if (olBounds != null) { │ │ │ │ + var sw = this.sphericalMercator ? │ │ │ │ + this.inverseMercator(olBounds.bottom, olBounds.left) : │ │ │ │ + new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ + var ne = this.sphericalMercator ? │ │ │ │ + this.inverseMercator(olBounds.top, olBounds.right) : │ │ │ │ + new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ + moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), │ │ │ │ + new GLatLng(ne.lat, ne.lon)); │ │ │ │ + } │ │ │ │ + return moBounds; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + │ │ │ │ + /************************************ │ │ │ │ + * * │ │ │ │ + * MapObject Interface Controls * │ │ │ │ + * * │ │ │ │ + ************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // Get&Set Center, Zoom │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: setMapObjectCenter │ │ │ │ + * Set the mapObject to the specified center and zoom │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * center - {Object} MapObject LonLat format │ │ │ │ + * zoom - {int} MapObject zoom format │ │ │ │ + */ │ │ │ │ + setMapObjectCenter: function(center, zoom) { │ │ │ │ + this.mapObject.setCenter(center, zoom); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getZIndex │ │ │ │ - * Get the z-index value for the current node from the node data itself. │ │ │ │ + * APIMethod: dragPanMapObject │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} The node whose z-index to get. │ │ │ │ + * dX - {Integer} │ │ │ │ + * dY - {Integer} │ │ │ │ + */ │ │ │ │ + dragPanMapObject: function(dX, dY) { │ │ │ │ + this.dragObject.moveBy(new GSize(-dX, dY)); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + │ │ │ │ + // LonLat - Pixel Translation │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getMapObjectLonLatFromMapObjectPixel │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moPixel - {Object} MapObject Pixel format │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Integer} The z-index value for the specified node (from the node │ │ │ │ - * data itself). │ │ │ │ + * {Object} MapObject LonLat translated from MapObject Pixel │ │ │ │ */ │ │ │ │ - getZIndex: function(node) { │ │ │ │ - return node._style.graphicZIndex; │ │ │ │ + getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ + return this.mapObject.fromContainerPixelToLatLng(moPixel); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIMethod: getMapObjectPixelFromMapObjectLonLat │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ + * moLonLat - {Object} MapObject LonLat format │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} MapObject Pixel transtlated from MapObject LonLat │ │ │ │ */ │ │ │ │ - determineZIndex: function(node) { │ │ │ │ - var zIndex = node._style.graphicZIndex; │ │ │ │ + getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + return this.mapObject.fromLatLngToContainerPixel(moLonLat); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // 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; │ │ │ │ - } │ │ │ │ + │ │ │ │ + // Bounds │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getMapObjectZoomFromMapObjectBounds │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moBounds - {Object} MapObject Bounds format │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} MapObject Zoom for specified MapObject Bounds │ │ │ │ + */ │ │ │ │ + getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ + return this.mapObject.getBoundsZoomLevel(moBounds); │ │ │ │ }, │ │ │ │ │ │ │ │ + /************************************ │ │ │ │ + * * │ │ │ │ + * MapObject Primitives * │ │ │ │ + * * │ │ │ │ + ************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // LonLat │ │ │ │ + │ │ │ │ /** │ │ │ │ - * APIMethod: getNextElement │ │ │ │ - * Get the next element in the order stack. │ │ │ │ + * APIMethod: getMapObjectLonLatFromLonLat │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * index - {Integer} The index of the current node in this.order. │ │ │ │ + * lon - {Float} │ │ │ │ + * lat - {Float} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} the node following the index passed in, or │ │ │ │ - * null. │ │ │ │ + * {Object} MapObject LonLat built from lon and lat params │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ + var gLatLng; │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + var lonlat = this.inverseMercator(lon, lat); │ │ │ │ + gLatLng = new GLatLng(lonlat.lat, lonlat.lon); │ │ │ │ } else { │ │ │ │ - return null; │ │ │ │ + gLatLng = new GLatLng(lat, lon); │ │ │ │ } │ │ │ │ + return gLatLng; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.ElementsIndexer" │ │ │ │ -}); │ │ │ │ + // Pixel │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getMapObjectPixelFromXY │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * x - {Integer} │ │ │ │ + * y - {Integer} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} MapObject Pixel from x and y parameters │ │ │ │ + */ │ │ │ │ + getMapObjectPixelFromXY: function(x, y) { │ │ │ │ + return new GPoint(x, y); │ │ │ │ + } │ │ │ │ + │ │ │ │ +}; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/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. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ + * @requires OpenLayers/Tile/Image.js │ │ │ │ */ │ │ │ │ -OpenLayers.ElementsIndexer.IndexingMethods = { │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.Image │ │ │ │ + * Instances of OpenLayers.Layer.Image are used to display data from a web │ │ │ │ + * accessible image as a map layer. Create a new image layer with the │ │ │ │ + * <OpenLayers.Layer.Image> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ - * │ │ │ │ + * Property: isBaseLayer │ │ │ │ + * {Boolean} The layer is a base layer. Default is true. Set this property │ │ │ │ + * in the layer options │ │ │ │ + */ │ │ │ │ + isBaseLayer: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: url │ │ │ │ + * {String} URL of the image to use │ │ │ │ + */ │ │ │ │ + url: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: extent │ │ │ │ + * {<OpenLayers.Bounds>} The image bounds in map units. This extent will │ │ │ │ + * also be used as the default maxExtent for the layer. If you wish │ │ │ │ + * to have a maxExtent that is different than the image extent, set the │ │ │ │ + * maxExtent property of the options argument (as with any other layer). │ │ │ │ + */ │ │ │ │ + extent: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: size │ │ │ │ + * {<OpenLayers.Size>} The image size in pixels │ │ │ │ + */ │ │ │ │ + size: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: tile │ │ │ │ + * {<OpenLayers.Tile.Image>} │ │ │ │ + */ │ │ │ │ + tile: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: aspectRatio │ │ │ │ + * {Float} The ratio of height/width represented by a single pixel in the │ │ │ │ + * graphic │ │ │ │ + */ │ │ │ │ + aspectRatio: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Image │ │ │ │ + * Create a new image layer │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * indexer - {<OpenLayers.ElementsIndexer>} │ │ │ │ - * newNode - {DOMElement} │ │ │ │ - * nextNode - {DOMElement} │ │ │ │ - * │ │ │ │ + * name - {String} A name for the layer. │ │ │ │ + * url - {String} Relative or absolute path to the image │ │ │ │ + * extent - {<OpenLayers.Bounds>} The extent represented by the image │ │ │ │ + * size - {<OpenLayers.Size>} The size (in pixels) of the image │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + */ │ │ │ │ + initialize: function(name, url, extent, size, options) { │ │ │ │ + this.url = url; │ │ │ │ + this.extent = extent; │ │ │ │ + this.maxExtent = extent; │ │ │ │ + this.size = size; │ │ │ │ + OpenLayers.Layer.prototype.initialize.apply(this, [name, options]); │ │ │ │ + │ │ │ │ + this.aspectRatio = (this.extent.getHeight() / this.size.h) / │ │ │ │ + (this.extent.getWidth() / this.size.w); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * Destroy this layer │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + if (this.tile) { │ │ │ │ + this.removeTileMonitoringHooks(this.tile); │ │ │ │ + this.tile.destroy(); │ │ │ │ + this.tile = null; │ │ │ │ + } │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ + * │ │ │ │ + * Paramters: │ │ │ │ + * obj - {Object} An optional layer (is this ever used?) │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Integer} │ │ │ │ + * {<OpenLayers.Layer.Image>} An exact copy of this layer │ │ │ │ */ │ │ │ │ - Z_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ - var newZIndex = indexer.getZIndex(newNode); │ │ │ │ + clone: function(obj) { │ │ │ │ │ │ │ │ - var returnVal = 0; │ │ │ │ - if (nextNode) { │ │ │ │ - var nextZIndex = indexer.getZIndex(nextNode); │ │ │ │ - returnVal = newZIndex - nextZIndex; │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.Image(this.name, │ │ │ │ + this.url, │ │ │ │ + this.extent, │ │ │ │ + this.size, │ │ │ │ + this.getOptions()); │ │ │ │ } │ │ │ │ │ │ │ │ - return returnVal; │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: 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. │ │ │ │ + * APIMethod: setMap │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * indexer - {<OpenLayers.ElementsIndexer>} │ │ │ │ - * newNode - {DOMElement} │ │ │ │ - * nextNode - {DOMElement} │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + /** │ │ │ │ + * If nothing to do with resolutions has been set, assume a single │ │ │ │ + * resolution determined by ratio*extent/size - if an image has a │ │ │ │ + * pixel aspect ratio different than one (as calculated above), the │ │ │ │ + * image will be stretched in one dimension only. │ │ │ │ + */ │ │ │ │ + if (this.options.maxResolution == null) { │ │ │ │ + this.options.maxResolution = this.aspectRatio * │ │ │ │ + this.extent.getWidth() / │ │ │ │ + this.size.w; │ │ │ │ + } │ │ │ │ + OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveTo │ │ │ │ + * Create the tile for the image or resize it for the new resolution │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * zoomChanged - {Boolean} │ │ │ │ + * dragging - {Boolean} │ │ │ │ */ │ │ │ │ - Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ - var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER( │ │ │ │ - indexer, │ │ │ │ - newNode, │ │ │ │ - nextNode │ │ │ │ - ); │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ │ │ │ │ - // 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; │ │ │ │ + var firstRendering = (this.tile == null); │ │ │ │ + │ │ │ │ + if (zoomChanged || firstRendering) { │ │ │ │ + │ │ │ │ + //determine new tile size │ │ │ │ + this.setTileSize(); │ │ │ │ + │ │ │ │ + //determine new position (upper left corner of new bounds) │ │ │ │ + var ulPx = this.map.getLayerPxFromLonLat({ │ │ │ │ + lon: this.extent.left, │ │ │ │ + lat: this.extent.top │ │ │ │ + }); │ │ │ │ + │ │ │ │ + if (firstRendering) { │ │ │ │ + //create the new tile │ │ │ │ + this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent, │ │ │ │ + null, this.tileSize); │ │ │ │ + this.addTileMonitoringHooks(this.tile); │ │ │ │ + } else { │ │ │ │ + //just resize the tile and set it's new position │ │ │ │ + this.tile.size = this.tileSize.clone(); │ │ │ │ + this.tile.position = ulPx.clone(); │ │ │ │ + } │ │ │ │ + this.tile.draw(); │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - return returnVal; │ │ │ │ + /** │ │ │ │ + * Set the tile size based on the map size. │ │ │ │ + */ │ │ │ │ + setTileSize: function() { │ │ │ │ + var tileWidth = this.extent.getWidth() / this.map.getResolution(); │ │ │ │ + var tileHeight = this.extent.getHeight() / this.map.getResolution(); │ │ │ │ + this.tileSize = new OpenLayers.Size(tileWidth, tileHeight); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: addTileMonitoringHooks │ │ │ │ + * This function takes a tile as input and adds the appropriate hooks to │ │ │ │ + * the tile so that the layer can keep track of the loading tiles. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * tile - {<OpenLayers.Tile>} │ │ │ │ + */ │ │ │ │ + addTileMonitoringHooks: function(tile) { │ │ │ │ + tile.onLoadStart = function() { │ │ │ │ + this.events.triggerEvent("loadstart"); │ │ │ │ + }; │ │ │ │ + tile.events.register("loadstart", this, tile.onLoadStart); │ │ │ │ + │ │ │ │ + tile.onLoadEnd = function() { │ │ │ │ + this.events.triggerEvent("loadend"); │ │ │ │ + }; │ │ │ │ + tile.events.register("loadend", this, tile.onLoadEnd); │ │ │ │ + tile.events.register("unload", this, tile.onLoadEnd); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: removeTileMonitoringHooks │ │ │ │ + * This function takes a tile as input and removes the tile hooks │ │ │ │ + * that were added in <addTileMonitoringHooks>. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * tile - {<OpenLayers.Tile>} │ │ │ │ + */ │ │ │ │ + removeTileMonitoringHooks: function(tile) { │ │ │ │ + tile.unload(); │ │ │ │ + tile.events.un({ │ │ │ │ + "loadstart": tile.onLoadStart, │ │ │ │ + "loadend": tile.onLoadEnd, │ │ │ │ + "unload": tile.onLoadEnd, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: 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. │ │ │ │ + * APIMethod: setUrl │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * indexer - {<OpenLayers.ElementsIndexer>} │ │ │ │ - * newNode - {DOMElement} │ │ │ │ - * nextNode - {DOMElement} │ │ │ │ + * newUrl - {String} │ │ │ │ + */ │ │ │ │ + setUrl: function(newUrl) { │ │ │ │ + this.url = newUrl; │ │ │ │ + this.tile.draw(); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getURL │ │ │ │ + * The url we return is always the same (the image itself never changes) │ │ │ │ + * so we can ignore the bounds parameter (it will always be the same, │ │ │ │ + * anyways) │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ - var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER( │ │ │ │ - indexer, │ │ │ │ - newNode, │ │ │ │ - nextNode │ │ │ │ - ); │ │ │ │ + getURL: function(bounds) { │ │ │ │ + return this.url; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (nextNode && returnVal === 0) { │ │ │ │ - var result = nextNode._boundsBottom - newNode._boundsBottom; │ │ │ │ - returnVal = (result === 0) ? 1 : result; │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Image" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/WMTS.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - return returnVal; │ │ │ │ - } │ │ │ │ -}; │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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.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. │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.WMTS │ │ │ │ + * Instances of the WMTS class allow viewing of tiles from a service that │ │ │ │ + * implements the OGC WMTS specification version 1.0.0. │ │ │ │ * │ │ │ │ - * Inherits: │ │ │ │ - * - <OpenLayers.Renderer> │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ */ │ │ │ │ -OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, { │ │ │ │ +OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: rendererRoot │ │ │ │ - * {DOMElement} │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} The layer will be considered a base layer. Default is true. │ │ │ │ */ │ │ │ │ - rendererRoot: null, │ │ │ │ + isBaseLayer: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: root │ │ │ │ - * {DOMElement} │ │ │ │ + * Property: version │ │ │ │ + * {String} WMTS version. Default is "1.0.0". │ │ │ │ */ │ │ │ │ - root: null, │ │ │ │ + version: "1.0.0", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: vectorRoot │ │ │ │ - * {DOMElement} │ │ │ │ + * APIProperty: requestEncoding │ │ │ │ + * {String} Request encoding. Can be "REST" or "KVP". Default is "KVP". │ │ │ │ */ │ │ │ │ - vectorRoot: null, │ │ │ │ + requestEncoding: "KVP", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: textRoot │ │ │ │ - * {DOMElement} │ │ │ │ + * APIProperty: url │ │ │ │ + * {String|Array(String)} The base URL or request URL template for the WMTS │ │ │ │ + * service. Must be provided. Array is only supported for base URLs, not │ │ │ │ + * for request URL templates. URL templates are only supported for │ │ │ │ + * REST <requestEncoding>. │ │ │ │ */ │ │ │ │ - textRoot: null, │ │ │ │ + url: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: xmlns │ │ │ │ - * {String} │ │ │ │ + * APIProperty: layer │ │ │ │ + * {String} The layer identifier advertised by the WMTS service. Must be │ │ │ │ + * provided. │ │ │ │ */ │ │ │ │ - xmlns: null, │ │ │ │ + layer: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: matrixSet │ │ │ │ + * {String} One of the advertised matrix set identifiers. Must be provided. │ │ │ │ + */ │ │ │ │ + matrixSet: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: style │ │ │ │ + * {String} One of the advertised layer styles. Must be provided. │ │ │ │ + */ │ │ │ │ + style: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: format │ │ │ │ + * {String} The image MIME type. Default is "image/jpeg". │ │ │ │ + */ │ │ │ │ + format: "image/jpeg", │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIProperty: tileOrigin │ │ │ │ + * {<OpenLayers.LonLat>} The top-left corner of the tile matrix in map │ │ │ │ + * units. If the tile origin for each matrix in a set is different, │ │ │ │ + * the <matrixIds> should include a topLeftCorner property. If │ │ │ │ + * not provided, the tile origin will default to the top left corner │ │ │ │ + * of the layer <maxExtent>. │ │ │ │ */ │ │ │ │ - xOffset: 0, │ │ │ │ + tileOrigin: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: rightOfDateLine │ │ │ │ - * {Boolean} Keeps track of the location of the map extent relative to the │ │ │ │ - * date line. The <setExtent> method compares this value (which is the one │ │ │ │ - * from the previous <setExtent> call) with the current position of the map │ │ │ │ - * extent relative to the date line and updates the xOffset when the extent │ │ │ │ - * has moved from one side of the date line to the other. │ │ │ │ + * APIProperty: tileFullExtent │ │ │ │ + * {<OpenLayers.Bounds>} The full extent of the tile set. If not supplied, │ │ │ │ + * the layer's <maxExtent> property will be used. │ │ │ │ */ │ │ │ │ + tileFullExtent: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: Indexer │ │ │ │ - * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer │ │ │ │ - * created upon initialization if the zIndexing or yOrdering options │ │ │ │ - * passed to this renderer's constructor are set to true. │ │ │ │ + * APIProperty: formatSuffix │ │ │ │ + * {String} For REST request encoding, an image format suffix must be │ │ │ │ + * included in the request. If not provided, the suffix will be derived │ │ │ │ + * from the <format> property. │ │ │ │ */ │ │ │ │ - indexer: null, │ │ │ │ + formatSuffix: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: BACKGROUND_ID_SUFFIX │ │ │ │ - * {String} │ │ │ │ + * APIProperty: matrixIds │ │ │ │ + * {Array} A list of tile matrix identifiers. If not provided, the matrix │ │ │ │ + * identifiers will be assumed to be integers corresponding to the │ │ │ │ + * map zoom level. If a list of strings is provided, each item should │ │ │ │ + * be the matrix identifier that corresponds to the map zoom level. │ │ │ │ + * Additionally, a list of objects can be provided. Each object should │ │ │ │ + * describe the matrix as presented in the WMTS capabilities. These │ │ │ │ + * objects should have the propertes shown below. │ │ │ │ + * │ │ │ │ + * Matrix properties: │ │ │ │ + * identifier - {String} The matrix identifier (required). │ │ │ │ + * scaleDenominator - {Number} The matrix scale denominator. │ │ │ │ + * topLeftCorner - {<OpenLayers.LonLat>} The top left corner of the │ │ │ │ + * matrix. Must be provided if different than the layer <tileOrigin>. │ │ │ │ + * tileWidth - {Number} The tile width for the matrix. Must be provided │ │ │ │ + * if different than the width given in the layer <tileSize>. │ │ │ │ + * tileHeight - {Number} The tile height for the matrix. Must be provided │ │ │ │ + * if different than the height given in the layer <tileSize>. │ │ │ │ */ │ │ │ │ - BACKGROUND_ID_SUFFIX: "_background", │ │ │ │ + matrixIds: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: LABEL_ID_SUFFIX │ │ │ │ - * {String} │ │ │ │ + * APIProperty: dimensions │ │ │ │ + * {Array} For RESTful request encoding, extra dimensions may be specified. │ │ │ │ + * Items in this list should be property names in the <params> object. │ │ │ │ + * Values of extra dimensions will be determined from the corresponding │ │ │ │ + * values in the <params> object. │ │ │ │ */ │ │ │ │ - LABEL_ID_SUFFIX: "_label", │ │ │ │ + dimensions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: LABEL_OUTLINE_SUFFIX │ │ │ │ - * {String} │ │ │ │ + * APIProperty: params │ │ │ │ + * {Object} Extra parameters to include in tile requests. For KVP │ │ │ │ + * <requestEncoding>, these properties will be encoded in the request │ │ │ │ + * query string. For REST <requestEncoding>, these properties will │ │ │ │ + * become part of the request path, with order determined by the │ │ │ │ + * <dimensions> list. │ │ │ │ */ │ │ │ │ - LABEL_OUTLINE_SUFFIX: "_outline", │ │ │ │ + params: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Renderer.Elements │ │ │ │ - * │ │ │ │ + * APIProperty: zoomOffset │ │ │ │ + * {Number} If your cache has more levels than you want to provide │ │ │ │ + * access to with this layer, supply a zoomOffset. This zoom offset │ │ │ │ + * is added to the current map zoom level to determine the level │ │ │ │ + * for a requested tile. For example, if you supply a zoomOffset │ │ │ │ + * of 3, when the map is at the zoom 0, tiles will be requested from │ │ │ │ + * level 3 of your cache. Default is 0 (assumes cache level and map │ │ │ │ + * zoom are equivalent). Additionally, if this layer is to be used │ │ │ │ + * as an overlay and the cache has fewer zoom levels than the base │ │ │ │ + * layer, you can supply a negative zoomOffset. For example, if a │ │ │ │ + * map zoom level of 1 corresponds to your cache level zero, you would │ │ │ │ + * supply a -1 zoomOffset (and set the maxResolution of the layer │ │ │ │ + * appropriately). The zoomOffset value has no effect if complete │ │ │ │ + * matrix definitions (including scaleDenominator) are supplied in │ │ │ │ + * the <matrixIds> property. Defaults to 0 (no zoom offset). │ │ │ │ + */ │ │ │ │ + zoomOffset: 0, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: serverResolutions │ │ │ │ + * {Array} A list of all resolutions available on the server. Only set this │ │ │ │ + * property if the map resolutions differ from the server. This │ │ │ │ + * property serves two purposes. (a) <serverResolutions> can include │ │ │ │ + * resolutions that the server supports and that you don't want to │ │ │ │ + * provide with this layer; you can also look at <zoomOffset>, which is │ │ │ │ + * an alternative to <serverResolutions> for that specific purpose. │ │ │ │ + * (b) The map can work with resolutions that aren't supported by │ │ │ │ + * the server, i.e. that aren't in <serverResolutions>. When the │ │ │ │ + * map is displayed in such a resolution data for the closest │ │ │ │ + * server-supported resolution is loaded and the layer div is │ │ │ │ + * stretched as necessary. │ │ │ │ + */ │ │ │ │ + serverResolutions: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: formatSuffixMap │ │ │ │ + * {Object} a map between WMTS 'format' request parameter and tile image file suffix │ │ │ │ + */ │ │ │ │ + formatSuffixMap: { │ │ │ │ + "image/png": "png", │ │ │ │ + "image/png8": "png", │ │ │ │ + "image/png24": "png", │ │ │ │ + "image/png32": "png", │ │ │ │ + "png": "png", │ │ │ │ + "image/jpeg": "jpg", │ │ │ │ + "image/jpg": "jpg", │ │ │ │ + "jpeg": "jpg", │ │ │ │ + "jpg": "jpg" │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: matrix │ │ │ │ + * {Object} Matrix definition for the current map resolution. Updated by │ │ │ │ + * the <updateMatrixProperties> method. │ │ │ │ + */ │ │ │ │ + matrix: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.WMTS │ │ │ │ + * Create a new WMTS layer. │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * (code) │ │ │ │ + * var wmts = new OpenLayers.Layer.WMTS({ │ │ │ │ + * name: "My WMTS Layer", │ │ │ │ + * url: "http://example.com/wmts", │ │ │ │ + * layer: "layer_id", │ │ │ │ + * style: "default", │ │ │ │ + * matrixSet: "matrix_id" │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * containerID - {String} │ │ │ │ - * options - {Object} options for this renderer. │ │ │ │ + * config - {Object} Configuration properties for the layer. │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * Required configuration properties: │ │ │ │ + * url - {String} The base url for the service. See the <url> property. │ │ │ │ + * layer - {String} The layer identifier. See the <layer> property. │ │ │ │ + * style - {String} The layer style identifier. See the <style> property. │ │ │ │ + * matrixSet - {String} The tile matrix set identifier. See the <matrixSet> │ │ │ │ + * property. │ │ │ │ + * │ │ │ │ + * Any other documented layer properties can be provided in the config object. │ │ │ │ */ │ │ │ │ - initialize: function(containerID, options) { │ │ │ │ - OpenLayers.Renderer.prototype.initialize.apply(this, arguments); │ │ │ │ + initialize: function(config) { │ │ │ │ │ │ │ │ - this.rendererRoot = this.createRenderRoot(); │ │ │ │ - this.root = this.createRoot("_root"); │ │ │ │ - this.vectorRoot = this.createRoot("_vroot"); │ │ │ │ - this.textRoot = this.createRoot("_troot"); │ │ │ │ + // confirm required properties are supplied │ │ │ │ + var required = { │ │ │ │ + url: true, │ │ │ │ + layer: true, │ │ │ │ + style: true, │ │ │ │ + matrixSet: true │ │ │ │ + }; │ │ │ │ + for (var prop in required) { │ │ │ │ + if (!(prop in config)) { │ │ │ │ + throw new Error("Missing property '" + prop + "' in layer configuration."); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - this.root.appendChild(this.vectorRoot); │ │ │ │ - this.root.appendChild(this.textRoot); │ │ │ │ + config.params = OpenLayers.Util.upperCaseObject(config.params); │ │ │ │ + var args = [config.name, config.url, config.params, config]; │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, args); │ │ │ │ │ │ │ │ - this.rendererRoot.appendChild(this.root); │ │ │ │ - this.container.appendChild(this.rendererRoot); │ │ │ │ │ │ │ │ - if (options && (options.zIndexing || options.yOrdering)) { │ │ │ │ - this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering); │ │ │ │ + // determine format suffix (for REST) │ │ │ │ + if (!this.formatSuffix) { │ │ │ │ + this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split("/").pop(); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // expand matrixIds (may be array of string or array of object) │ │ │ │ + if (this.matrixIds) { │ │ │ │ + var len = this.matrixIds.length; │ │ │ │ + if (len && typeof this.matrixIds[0] === "string") { │ │ │ │ + var ids = this.matrixIds; │ │ │ │ + this.matrixIds = new Array(len); │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ + this.matrixIds[i] = { │ │ │ │ + identifier: ids[i] │ │ │ │ + }; │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ + * Method: setMap │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - │ │ │ │ - this.clear(); │ │ │ │ - │ │ │ │ - this.rendererRoot = null; │ │ │ │ - this.root = null; │ │ │ │ - this.xmlns = null; │ │ │ │ - │ │ │ │ - OpenLayers.Renderer.prototype.destroy.apply(this, arguments); │ │ │ │ + setMap: function() { │ │ │ │ + OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clear │ │ │ │ - * Remove all the elements from the root │ │ │ │ + * Method: updateMatrixProperties │ │ │ │ + * Called when map resolution changes to update matrix related properties. │ │ │ │ */ │ │ │ │ - clear: function() { │ │ │ │ - var child; │ │ │ │ - var root = this.vectorRoot; │ │ │ │ - if (root) { │ │ │ │ - while (child = root.firstChild) { │ │ │ │ - root.removeChild(child); │ │ │ │ + updateMatrixProperties: function() { │ │ │ │ + this.matrix = this.getMatrix(); │ │ │ │ + if (this.matrix) { │ │ │ │ + if (this.matrix.topLeftCorner) { │ │ │ │ + this.tileOrigin = this.matrix.topLeftCorner; │ │ │ │ } │ │ │ │ - } │ │ │ │ - root = this.textRoot; │ │ │ │ - if (root) { │ │ │ │ - while (child = root.firstChild) { │ │ │ │ - root.removeChild(child); │ │ │ │ + if (this.matrix.tileWidth && this.matrix.tileHeight) { │ │ │ │ + this.tileSize = new OpenLayers.Size( │ │ │ │ + this.matrix.tileWidth, this.matrix.tileHeight │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (!this.tileOrigin) { │ │ │ │ + this.tileOrigin = new OpenLayers.LonLat( │ │ │ │ + this.maxExtent.left, this.maxExtent.top │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (!this.tileFullExtent) { │ │ │ │ + this.tileFullExtent = this.maxExtent; │ │ │ │ } │ │ │ │ } │ │ │ │ - if (this.indexer) { │ │ │ │ - this.indexer.clear(); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveTo │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to │ │ │ │ + * do some init work in that case. │ │ │ │ + * dragging - {Boolean} │ │ │ │ + */ │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + if (zoomChanged || !this.matrix) { │ │ │ │ + this.updateMatrixProperties(); │ │ │ │ } │ │ │ │ + return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setExtent │ │ │ │ - * Set the visible part of the layer. │ │ │ │ - * │ │ │ │ + * APIMethod: clone │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * extent - {<OpenLayers.Bounds>} │ │ │ │ - * resolutionChanged - {Boolean} │ │ │ │ - * │ │ │ │ + * obj - {Object} │ │ │ │ + * │ │ │ │ * 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. │ │ │ │ + * {<OpenLayers.Layer.WMTS>} An exact clone of this <OpenLayers.Layer.WMTS> │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.WMTS(this.options); │ │ │ │ + } │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getIdentifier │ │ │ │ + * Get the current index in the matrixIds array. │ │ │ │ + */ │ │ │ │ + getIdentifier: function() { │ │ │ │ + return this.getServerZoom(); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getMatrix │ │ │ │ + * Get the appropriate matrix definition for the current map resolution. │ │ │ │ + */ │ │ │ │ + getMatrix: function() { │ │ │ │ + var matrix; │ │ │ │ + if (!this.matrixIds || this.matrixIds.length === 0) { │ │ │ │ + matrix = { │ │ │ │ + identifier: this.getIdentifier() │ │ │ │ + }; │ │ │ │ + } else { │ │ │ │ + // get appropriate matrix given the map scale if possible │ │ │ │ + if ("scaleDenominator" in this.matrixIds[0]) { │ │ │ │ + // scale denominator calculation based on WMTS spec │ │ │ │ + var denom = │ │ │ │ + OpenLayers.METERS_PER_INCH * │ │ │ │ + OpenLayers.INCHES_PER_UNIT[this.units] * │ │ │ │ + this.getServerResolution() / 0.28E-3; │ │ │ │ + var diff = Number.POSITIVE_INFINITY; │ │ │ │ + var delta; │ │ │ │ + for (var i = 0, ii = this.matrixIds.length; i < ii; ++i) { │ │ │ │ + delta = Math.abs(1 - (this.matrixIds[i].scaleDenominator / denom)); │ │ │ │ + if (delta < diff) { │ │ │ │ + diff = delta; │ │ │ │ + matrix = this.matrixIds[i]; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + // fall back on zoom as index │ │ │ │ + matrix = this.matrixIds[this.getIdentifier()]; │ │ │ │ } │ │ │ │ - this.rightOfDateLine = rightOfDateLine; │ │ │ │ } │ │ │ │ - return coordSysUnchanged; │ │ │ │ + return matrix; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ - * │ │ │ │ + * Method: getTileInfo │ │ │ │ + * Get tile information for a given location at the current map resolution. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * │ │ │ │ + * loc - {<OpenLayers.LonLat} A location in map coordinates. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} The corresponding node type for the specified geometry │ │ │ │ + * {Object} An object with "col", "row", "i", and "j" properties. The col │ │ │ │ + * and row values are zero based tile indexes from the top left. The │ │ │ │ + * i and j values are the number of pixels to the left and top │ │ │ │ + * (respectively) of the given location within the target tile. │ │ │ │ */ │ │ │ │ - getNodeType: function(geometry, style) {}, │ │ │ │ + getTileInfo: function(loc) { │ │ │ │ + var res = this.getServerResolution(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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. │ │ │ │ - * │ │ │ │ + var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w); │ │ │ │ + var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h); │ │ │ │ + │ │ │ │ + var col = Math.floor(fx); │ │ │ │ + var row = Math.floor(fy); │ │ │ │ + │ │ │ │ + return { │ │ │ │ + col: col, │ │ │ │ + row: row, │ │ │ │ + i: Math.floor((fx - col) * this.tileSize.w), │ │ │ │ + j: Math.floor((fy - row) * this.tileSize.h) │ │ │ │ + }; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getURL │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {String} │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} true if the geometry has been drawn completely; null if │ │ │ │ - * incomplete; false otherwise │ │ │ │ + * {String} A URL for the tile corresponding to the given bounds. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ + getURL: function(bounds) { │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ + var url = ""; │ │ │ │ + if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) { │ │ │ │ │ │ │ │ - rendered = false; │ │ │ │ - var removeBackground = false; │ │ │ │ - if (style.display != "none") { │ │ │ │ - if (style.backgroundGraphic) { │ │ │ │ - this.redrawBackgroundNode(geometry.id, geometry, style, │ │ │ │ - featureId); │ │ │ │ + var center = bounds.getCenterLonLat(); │ │ │ │ + var info = this.getTileInfo(center); │ │ │ │ + var matrixId = this.matrix.identifier; │ │ │ │ + var dimensions = this.dimensions, │ │ │ │ + params; │ │ │ │ + │ │ │ │ + if (OpenLayers.Util.isArray(this.url)) { │ │ │ │ + url = this.selectUrl([ │ │ │ │ + this.version, this.style, this.matrixSet, │ │ │ │ + this.matrix.identifier, info.row, info.col │ │ │ │ + ].join(","), this.url); │ │ │ │ } else { │ │ │ │ - removeBackground = true; │ │ │ │ + url = this.url; │ │ │ │ } │ │ │ │ - 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; │ │ │ │ + │ │ │ │ + if (this.requestEncoding.toUpperCase() === "REST") { │ │ │ │ + params = this.params; │ │ │ │ + if (url.indexOf("{") !== -1) { │ │ │ │ + var template = url.replace(/\{/g, "${"); │ │ │ │ + var context = { │ │ │ │ + // spec does not make clear if capital S or not │ │ │ │ + style: this.style, │ │ │ │ + Style: this.style, │ │ │ │ + TileMatrixSet: this.matrixSet, │ │ │ │ + TileMatrix: this.matrix.identifier, │ │ │ │ + TileRow: info.row, │ │ │ │ + TileCol: info.col │ │ │ │ + }; │ │ │ │ + if (dimensions) { │ │ │ │ + var dimension, i; │ │ │ │ + for (i = dimensions.length - 1; i >= 0; --i) { │ │ │ │ + dimension = dimensions[i]; │ │ │ │ + context[dimension] = params[dimension.toUpperCase()]; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + url = OpenLayers.String.format(template, context); │ │ │ │ + } else { │ │ │ │ + // include 'version', 'layer' and 'style' in tile resource url │ │ │ │ + var path = this.version + "/" + this.layer + "/" + this.style + "/"; │ │ │ │ + │ │ │ │ + // append optional dimension path elements │ │ │ │ + if (dimensions) { │ │ │ │ + for (var i = 0; i < dimensions.length; i++) { │ │ │ │ + if (params[dimensions[i]]) { │ │ │ │ + path = path + params[dimensions[i]] + "/"; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // append other required path elements │ │ │ │ + path = path + this.matrixSet + "/" + this.matrix.identifier + │ │ │ │ + "/" + info.row + "/" + info.col + "." + this.formatSuffix; │ │ │ │ + │ │ │ │ + if (!url.match(/\/$/)) { │ │ │ │ + url = url + "/"; │ │ │ │ + } │ │ │ │ + url = url + path; │ │ │ │ } │ │ │ │ - node.parentNode.removeChild(node); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (removeBackground) { │ │ │ │ - var node = document.getElementById( │ │ │ │ - geometry.id + this.BACKGROUND_ID_SUFFIX); │ │ │ │ - if (node) { │ │ │ │ - node.parentNode.removeChild(node); │ │ │ │ + } else if (this.requestEncoding.toUpperCase() === "KVP") { │ │ │ │ + │ │ │ │ + // assemble all required parameters │ │ │ │ + params = { │ │ │ │ + SERVICE: "WMTS", │ │ │ │ + REQUEST: "GetTile", │ │ │ │ + VERSION: this.version, │ │ │ │ + LAYER: this.layer, │ │ │ │ + STYLE: this.style, │ │ │ │ + TILEMATRIXSET: this.matrixSet, │ │ │ │ + TILEMATRIX: this.matrix.identifier, │ │ │ │ + TILEROW: info.row, │ │ │ │ + TILECOL: info.col, │ │ │ │ + FORMAT: this.format │ │ │ │ + }; │ │ │ │ + url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]); │ │ │ │ + │ │ │ │ } │ │ │ │ } │ │ │ │ - return rendered; │ │ │ │ + return url; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: redrawNode │ │ │ │ + * APIMethod: mergeNewParams │ │ │ │ + * Extend the existing layer <params> with new properties. Tiles will be │ │ │ │ + * reloaded with updated params in the request. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * id - {String} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {String} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} true if the complete geometry could be drawn, null if parts of │ │ │ │ - * the geometry could not be drawn, false otherwise │ │ │ │ + * newParams - {Object} Properties to extend to existing <params>. │ │ │ │ */ │ │ │ │ - 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)); │ │ │ │ + mergeNewParams: function(newParams) { │ │ │ │ + if (this.requestEncoding.toUpperCase() === "KVP") { │ │ │ │ + return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply( │ │ │ │ + this, [OpenLayers.Util.upperCaseObject(newParams)] │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // 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; │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.WMTS" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/Zoomify.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - var drawResult = this.drawGeometryNode(node, geometry, style); │ │ │ │ - if (drawResult === false) { │ │ │ │ - return 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. */ │ │ │ │ + │ │ │ │ +/* │ │ │ │ + * Development supported by a R&D grant DC08P02OUK006 - Old Maps Online │ │ │ │ + * (www.oldmapsonline.org) from Ministry of Culture of the Czech Republic. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.Zoomify │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: size │ │ │ │ + * {<OpenLayers.Size>} The Zoomify image size in pixels. │ │ │ │ + */ │ │ │ │ + size: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} │ │ │ │ + */ │ │ │ │ + isBaseLayer: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: standardTileSize │ │ │ │ + * {Integer} The size of a standard (non-border) square tile in pixels. │ │ │ │ + */ │ │ │ │ + standardTileSize: 256, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: tileOriginCorner │ │ │ │ + * {String} This layer uses top-left as tile origin │ │ │ │ + **/ │ │ │ │ + tileOriginCorner: "tl", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: numberOfTiers │ │ │ │ + * {Integer} Depth of the Zoomify pyramid, number of tiers (zoom levels) │ │ │ │ + * - filled during Zoomify pyramid initialization. │ │ │ │ + */ │ │ │ │ + numberOfTiers: 0, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: tileCountUpToTier │ │ │ │ + * {Array(Integer)} Number of tiles up to the given tier of pyramid. │ │ │ │ + * - filled during Zoomify pyramid initialization. │ │ │ │ + */ │ │ │ │ + tileCountUpToTier: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: tierSizeInTiles │ │ │ │ + * {Array(<OpenLayers.Size>)} Size (in tiles) for each tier of pyramid. │ │ │ │ + * - filled during Zoomify pyramid initialization. │ │ │ │ + */ │ │ │ │ + tierSizeInTiles: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: tierImageSize │ │ │ │ + * {Array(<OpenLayers.Size>)} Image size in pixels for each pyramid tier. │ │ │ │ + * - filled during Zoomify pyramid initialization. │ │ │ │ + */ │ │ │ │ + tierImageSize: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Zoomify │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} A name for the layer. │ │ │ │ + * url - {String} - Relative or absolute path to the image or more │ │ │ │ + * precisly to the TileGroup[X] directories root. │ │ │ │ + * Flash plugin use the variable name "zoomifyImagePath" for this. │ │ │ │ + * size - {<OpenLayers.Size>} The size (in pixels) of the image. │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + */ │ │ │ │ + initialize: function(name, url, size, options) { │ │ │ │ + │ │ │ │ + // initilize the Zoomify pyramid for given size │ │ │ │ + this.initializeZoomify(size); │ │ │ │ + │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, [ │ │ │ │ + name, url, size, {}, │ │ │ │ + options │ │ │ │ + ]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: initializeZoomify │ │ │ │ + * It generates constants for all tiers of the Zoomify pyramid │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * size - {<OpenLayers.Size>} The size of the image in pixels │ │ │ │ + * │ │ │ │ + */ │ │ │ │ + initializeZoomify: function(size) { │ │ │ │ + │ │ │ │ + var imageSize = size.clone(); │ │ │ │ + this.size = size.clone(); │ │ │ │ + var tiles = new OpenLayers.Size( │ │ │ │ + Math.ceil(imageSize.w / this.standardTileSize), │ │ │ │ + Math.ceil(imageSize.h / this.standardTileSize) │ │ │ │ + ); │ │ │ │ + │ │ │ │ + this.tierSizeInTiles = [tiles]; │ │ │ │ + this.tierImageSize = [imageSize]; │ │ │ │ + │ │ │ │ + while (imageSize.w > this.standardTileSize || │ │ │ │ + imageSize.h > this.standardTileSize) { │ │ │ │ + │ │ │ │ + imageSize = new OpenLayers.Size( │ │ │ │ + Math.floor(imageSize.w / 2), │ │ │ │ + Math.floor(imageSize.h / 2) │ │ │ │ + ); │ │ │ │ + tiles = new OpenLayers.Size( │ │ │ │ + Math.ceil(imageSize.w / this.standardTileSize), │ │ │ │ + Math.ceil(imageSize.h / this.standardTileSize) │ │ │ │ + ); │ │ │ │ + this.tierSizeInTiles.push(tiles); │ │ │ │ + this.tierImageSize.push(imageSize); │ │ │ │ } │ │ │ │ │ │ │ │ - node = drawResult.node; │ │ │ │ + this.tierSizeInTiles.reverse(); │ │ │ │ + this.tierImageSize.reverse(); │ │ │ │ │ │ │ │ - // 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.numberOfTiers = this.tierSizeInTiles.length; │ │ │ │ + var resolutions = [1]; │ │ │ │ + this.tileCountUpToTier = [0]; │ │ │ │ + for (var i = 1; i < this.numberOfTiers; i++) { │ │ │ │ + resolutions.unshift(Math.pow(2, i)); │ │ │ │ + this.tileCountUpToTier.push( │ │ │ │ + this.tierSizeInTiles[i - 1].w * this.tierSizeInTiles[i - 1].h + │ │ │ │ + this.tileCountUpToTier[i - 1] │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (!this.serverResolutions) { │ │ │ │ + this.serverResolutions = resolutions; │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.postDraw(node); │ │ │ │ + /** │ │ │ │ + * APIMethod:destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + // for now, nothing special to do here. │ │ │ │ + OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments); │ │ │ │ + │ │ │ │ + // Remove from memory the Zoomify pyramid - is that enough? │ │ │ │ + this.tileCountUpToTier.length = 0; │ │ │ │ + this.tierSizeInTiles.length = 0; │ │ │ │ + this.tierImageSize.length = 0; │ │ │ │ │ │ │ │ - return drawResult.complete; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: redrawBackgroundNode │ │ │ │ - * Redraws the node using special 'background' style properties. Basically │ │ │ │ - * just calls redrawNode(), but instead of directly using the │ │ │ │ - * 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and │ │ │ │ - * 'graphicZIndex' properties directly from the specified 'style' │ │ │ │ - * parameter, we create a new style object and set those properties │ │ │ │ - * from the corresponding 'background'-prefixed properties from │ │ │ │ - * specified 'style' parameter. │ │ │ │ - * │ │ │ │ + * APIMethod: clone │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * id - {String} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {String} │ │ │ │ - * │ │ │ │ + * obj - {Object} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} true if the complete geometry could be drawn, null if parts of │ │ │ │ - * the geometry could not be drawn, false otherwise │ │ │ │ + * {<OpenLayers.Layer.Zoomify>} An exact clone of this <OpenLayers.Layer.Zoomify> │ │ │ │ */ │ │ │ │ - redrawBackgroundNode: function(id, geometry, style, featureId) { │ │ │ │ - var backgroundStyle = OpenLayers.Util.extend({}, style); │ │ │ │ + clone: function(obj) { │ │ │ │ │ │ │ │ - // 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; │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.Zoomify(this.name, │ │ │ │ + this.url, │ │ │ │ + this.size, │ │ │ │ + this.options); │ │ │ │ + } │ │ │ │ │ │ │ │ - // Erase background styles. │ │ │ │ - backgroundStyle.backgroundGraphic = null; │ │ │ │ - backgroundStyle.backgroundXOffset = null; │ │ │ │ - backgroundStyle.backgroundYOffset = null; │ │ │ │ - backgroundStyle.backgroundGraphicZIndex = null; │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ │ │ │ │ - return this.redrawNode( │ │ │ │ - id + this.BACKGROUND_ID_SUFFIX, │ │ │ │ - geometry, │ │ │ │ - backgroundStyle, │ │ │ │ - null │ │ │ │ - ); │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: getURL │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * │ │ │ │ * 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) │ │ │ │ + * {String} A string with the layer's url and parameters and also the │ │ │ │ + * passed-in bounds and appropriate tile size specified as │ │ │ │ + * parameters │ │ │ │ */ │ │ │ │ - drawGeometryNode: function(node, geometry, style) { │ │ │ │ - style = style || node._style; │ │ │ │ + getURL: function(bounds) { │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ + var res = this.getServerResolution(); │ │ │ │ + var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); │ │ │ │ + var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h)); │ │ │ │ + var z = this.getZoomForResolution(res); │ │ │ │ │ │ │ │ - var 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; │ │ │ │ + var tileIndex = x + y * this.tierSizeInTiles[z].w + this.tileCountUpToTier[z]; │ │ │ │ + var path = "TileGroup" + Math.floor((tileIndex) / 256) + │ │ │ │ + "/" + z + "-" + x + "-" + y + ".jpg"; │ │ │ │ + var url = this.url; │ │ │ │ + if (OpenLayers.Util.isArray(url)) { │ │ │ │ + url = this.selectUrl(path, url); │ │ │ │ } │ │ │ │ + return url + path; │ │ │ │ + }, │ │ │ │ │ │ │ │ - node._options = options; │ │ │ │ - │ │ │ │ - //set style │ │ │ │ - //TBD simplify this │ │ │ │ - if (drawn != false) { │ │ │ │ - return { │ │ │ │ - node: this.setStyle(node, style, options, geometry), │ │ │ │ - complete: drawn │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Method: getImageSize │ │ │ │ + * getImageSize returns size for a particular tile. If bounds are given as │ │ │ │ + * first argument, size is calculated (bottom-right tiles are non square). │ │ │ │ + * │ │ │ │ + */ │ │ │ │ + getImageSize: function() { │ │ │ │ + if (arguments.length > 0) { │ │ │ │ + var bounds = this.adjustBounds(arguments[0]); │ │ │ │ + var res = this.getServerResolution(); │ │ │ │ + var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); │ │ │ │ + var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h)); │ │ │ │ + var z = this.getZoomForResolution(res); │ │ │ │ + var w = this.standardTileSize; │ │ │ │ + var h = this.standardTileSize; │ │ │ │ + if (x == this.tierSizeInTiles[z].w - 1) { │ │ │ │ + var w = this.tierImageSize[z].w % this.standardTileSize; │ │ │ │ + } │ │ │ │ + if (y == this.tierSizeInTiles[z].h - 1) { │ │ │ │ + var h = this.tierImageSize[z].h % this.standardTileSize; │ │ │ │ + } │ │ │ │ + return (new OpenLayers.Size(w, h)); │ │ │ │ } else { │ │ │ │ - return false; │ │ │ │ + return this.tileSize; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: postDraw │ │ │ │ - * Things that have do be done after the geometry node is appended │ │ │ │ - * to its parent node. To be overridden by subclasses. │ │ │ │ - * │ │ │ │ + * APIMethod: setMap │ │ │ │ + * When the layer is added to a map, then we can fetch our origin │ │ │ │ + * (if we don't have one.) │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - postDraw: function(node) {}, │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ + this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, │ │ │ │ + this.map.maxExtent.top); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Zoomify" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/TMS.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.TMS │ │ │ │ + * Create a layer for accessing tiles from services that conform with the │ │ │ │ + * Tile Map Service Specification │ │ │ │ + * (http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification). │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * (code) │ │ │ │ + * var layer = new OpenLayers.Layer.TMS( │ │ │ │ + * "My Layer", // name for display in LayerSwitcher │ │ │ │ + * "http://tilecache.osgeo.org/wms-c/Basic.py/", // service endpoint │ │ │ │ + * {layername: "basic", type: "png"} // required properties │ │ │ │ + * ); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawPoint │ │ │ │ - * Virtual function for drawing Point Geometry. │ │ │ │ - * Should be implemented by subclasses. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or false if the renderer could not draw the point │ │ │ │ + * APIProperty: serviceVersion │ │ │ │ + * {String} Service version for tile requests. Default is "1.0.0". │ │ │ │ */ │ │ │ │ - drawPoint: function(node, geometry) {}, │ │ │ │ + serviceVersion: "1.0.0", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawLineString │ │ │ │ - * Virtual function for drawing LineString Geometry. │ │ │ │ - * Should be implemented by subclasses. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or null if the renderer could not draw all components of │ │ │ │ - * the linestring, or false if nothing could be drawn │ │ │ │ + * APIProperty: layername │ │ │ │ + * {String} The identifier for the <TileMap> as advertised by the service. │ │ │ │ + * For example, if the service advertises a <TileMap> with │ │ │ │ + * 'href="http://tms.osgeo.org/1.0.0/vmap0"', the <layername> property │ │ │ │ + * would be set to "vmap0". │ │ │ │ */ │ │ │ │ - drawLineString: function(node, geometry) {}, │ │ │ │ + layername: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawLinearRing │ │ │ │ - * Virtual function for drawing LinearRing Geometry. │ │ │ │ - * Should be implemented by subclasses. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or null if the renderer could not draw all components │ │ │ │ - * of the linear ring, or false if nothing could be drawn │ │ │ │ + * APIProperty: type │ │ │ │ + * {String} The format extension corresponding to the requested tile image │ │ │ │ + * type. This is advertised in a <TileFormat> element as the │ │ │ │ + * "extension" attribute. For example, if the service advertises a │ │ │ │ + * <TileMap> with <TileFormat width="256" height="256" mime-type="image/jpeg" extension="jpg" />, │ │ │ │ + * the <type> property would be set to "jpg". │ │ │ │ */ │ │ │ │ - drawLinearRing: function(node, geometry) {}, │ │ │ │ + type: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawPolygon │ │ │ │ - * Virtual function for drawing Polygon Geometry. │ │ │ │ - * Should be implemented by subclasses. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or null if the renderer could not draw all components │ │ │ │ - * of the polygon, or false if nothing could be drawn │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} Make this layer a base layer. Default is true. Set false to │ │ │ │ + * use the layer as an overlay. │ │ │ │ */ │ │ │ │ - drawPolygon: function(node, geometry) {}, │ │ │ │ + isBaseLayer: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawRectangle │ │ │ │ - * Virtual function for drawing Rectangle Geometry. │ │ │ │ - * Should be implemented by subclasses. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or false if the renderer could not draw the rectangle │ │ │ │ + * APIProperty: tileOrigin │ │ │ │ + * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles. │ │ │ │ + * If provided, requests for tiles at all resolutions will be aligned │ │ │ │ + * with this location (no tiles shall overlap this location). If │ │ │ │ + * not provided, the grid of tiles will be aligned with the bottom-left │ │ │ │ + * corner of the map's <maxExtent>. Default is ``null``. │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * (code) │ │ │ │ + * var layer = new OpenLayers.Layer.TMS( │ │ │ │ + * "My Layer", │ │ │ │ + * "http://tilecache.osgeo.org/wms-c/Basic.py/", │ │ │ │ + * { │ │ │ │ + * layername: "basic", │ │ │ │ + * type: "png", │ │ │ │ + * // set if different than the bottom left of map.maxExtent │ │ │ │ + * tileOrigin: new OpenLayers.LonLat(-180, -90) │ │ │ │ + * } │ │ │ │ + * ); │ │ │ │ + * (end) │ │ │ │ */ │ │ │ │ - drawRectangle: function(node, geometry) {}, │ │ │ │ + tileOrigin: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawCircle │ │ │ │ - * Virtual function for drawing Circle Geometry. │ │ │ │ - * Should be implemented by subclasses. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or false if the renderer could not draw the circle │ │ │ │ + * APIProperty: serverResolutions │ │ │ │ + * {Array} A list of all resolutions available on the server. Only set this │ │ │ │ + * property if the map resolutions differ from the server. This │ │ │ │ + * property serves two purposes. (a) <serverResolutions> can include │ │ │ │ + * resolutions that the server supports and that you don't want to │ │ │ │ + * provide with this layer; you can also look at <zoomOffset>, which is │ │ │ │ + * an alternative to <serverResolutions> for that specific purpose. │ │ │ │ + * (b) The map can work with resolutions that aren't supported by │ │ │ │ + * the server, i.e. that aren't in <serverResolutions>. When the │ │ │ │ + * map is displayed in such a resolution data for the closest │ │ │ │ + * server-supported resolution is loaded and the layer div is │ │ │ │ + * stretched as necessary. │ │ │ │ */ │ │ │ │ - drawCircle: function(node, geometry) {}, │ │ │ │ + serverResolutions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: removeText │ │ │ │ - * Removes a label │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * featureId - {String} │ │ │ │ + * APIProperty: zoomOffset │ │ │ │ + * {Number} If your cache has more zoom levels than you want to provide │ │ │ │ + * access to with this layer, supply a zoomOffset. This zoom offset │ │ │ │ + * is added to the current map zoom level to determine the level │ │ │ │ + * for a requested tile. For example, if you supply a zoomOffset │ │ │ │ + * of 3, when the map is at the zoom 0, tiles will be requested from │ │ │ │ + * level 3 of your cache. Default is 0 (assumes cache level and map │ │ │ │ + * zoom are equivalent). Using <zoomOffset> is an alternative to │ │ │ │ + * setting <serverResolutions> if you only want to expose a subset │ │ │ │ + * of the server resolutions. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + zoomOffset: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getFeatureIdFromEvent │ │ │ │ + * Constructor: OpenLayers.Layer.TMS │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} An <OpenLayers.Event> object │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A feature id or undefined. │ │ │ │ + * name - {String} Title to be displayed in a <OpenLayers.Control.LayerSwitcher> │ │ │ │ + * url - {String} Service endpoint (without the version number). E.g. │ │ │ │ + * "http://tms.osgeo.org/". │ │ │ │ + * options - {Object} Additional properties to be set on the layer. The │ │ │ │ + * <layername> and <type> properties must be set here. │ │ │ │ */ │ │ │ │ - getFeatureIdFromEvent: function(evt) { │ │ │ │ - var target = evt.target; │ │ │ │ - var useElement = target && target.correspondingUseElement; │ │ │ │ - var node = useElement ? useElement : (target || evt.srcElement); │ │ │ │ - return node._featureId; │ │ │ │ + initialize: function(name, url, options) { │ │ │ │ + var newArguments = []; │ │ │ │ + newArguments.push(name, url, {}, options); │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * APIMethod: clone │ │ │ │ + * Create a complete copy of this layer. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * featureId - {String} │ │ │ │ + * obj - {Object} Should only be provided by subclasses that call this │ │ │ │ + * method. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.TMS>} An exact clone of this <OpenLayers.Layer.TMS> │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + clone: function(obj) { │ │ │ │ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.TMS(this.name, │ │ │ │ + this.url, │ │ │ │ + this.getOptions()); │ │ │ │ } │ │ │ │ + │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: 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. │ │ │ │ + /** │ │ │ │ + * Method: getURL │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * id - {String} │ │ │ │ - * type - {String} type Kind of node to draw. │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} A new node of the given type and id. │ │ │ │ + * {String} A string with the layer's url and parameters and also the │ │ │ │ + * passed-in bounds and appropriate tile size specified as │ │ │ │ + * parameters │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + getURL: function(bounds) { │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ + var res = this.getServerResolution(); │ │ │ │ + var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); │ │ │ │ + var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h)); │ │ │ │ + var z = this.getServerZoom(); │ │ │ │ + var path = this.serviceVersion + "/" + this.layername + "/" + z + "/" + x + "/" + y + "." + this.type; │ │ │ │ + var url = this.url; │ │ │ │ + if (OpenLayers.Util.isArray(url)) { │ │ │ │ + url = this.selectUrl(path, url); │ │ │ │ } │ │ │ │ - return node; │ │ │ │ + return url + path; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: nodeTypeCompare │ │ │ │ + * Method: setMap │ │ │ │ + * When the layer is added to a map, then we can fetch our origin │ │ │ │ + * (if we don't have one.) │ │ │ │ * │ │ │ │ * 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. │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - nodeTypeCompare: function(node, type) {}, │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ + if (!this.tileOrigin) { │ │ │ │ + this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, │ │ │ │ + this.map.maxExtent.bottom); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: createNode │ │ │ │ - * │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.TMS" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/XYZ.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.XYZ │ │ │ │ + * The XYZ class is designed to make it easier for people who have tiles │ │ │ │ + * arranged by a standard XYZ grid. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * Default is true, as this is designed to be a base tile source. │ │ │ │ + */ │ │ │ │ + isBaseLayer: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: sphericalMercator │ │ │ │ + * Whether the tile extents should be set to the defaults for │ │ │ │ + * spherical mercator. Useful for things like OpenStreetMap. │ │ │ │ + * Default is false, except for the OSM subclass. │ │ │ │ + */ │ │ │ │ + sphericalMercator: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomOffset │ │ │ │ + * {Number} If your cache has more zoom levels than you want to provide │ │ │ │ + * access to with this layer, supply a zoomOffset. This zoom offset │ │ │ │ + * is added to the current map zoom level to determine the level │ │ │ │ + * for a requested tile. For example, if you supply a zoomOffset │ │ │ │ + * of 3, when the map is at the zoom 0, tiles will be requested from │ │ │ │ + * level 3 of your cache. Default is 0 (assumes cache level and map │ │ │ │ + * zoom are equivalent). Using <zoomOffset> is an alternative to │ │ │ │ + * setting <serverResolutions> if you only want to expose a subset │ │ │ │ + * of the server resolutions. │ │ │ │ + */ │ │ │ │ + zoomOffset: 0, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: serverResolutions │ │ │ │ + * {Array} A list of all resolutions available on the server. Only set this │ │ │ │ + * property if the map resolutions differ from the server. This │ │ │ │ + * property serves two purposes. (a) <serverResolutions> can include │ │ │ │ + * resolutions that the server supports and that you don't want to │ │ │ │ + * provide with this layer; you can also look at <zoomOffset>, which is │ │ │ │ + * an alternative to <serverResolutions> for that specific purpose. │ │ │ │ + * (b) The map can work with resolutions that aren't supported by │ │ │ │ + * the server, i.e. that aren't in <serverResolutions>. When the │ │ │ │ + * map is displayed in such a resolution data for the closest │ │ │ │ + * server-supported resolution is loaded and the layer div is │ │ │ │ + * stretched as necessary. │ │ │ │ + */ │ │ │ │ + serverResolutions: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.XYZ │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * type - {String} Kind of node to draw. │ │ │ │ - * id - {String} Id for node. │ │ │ │ + * name - {String} │ │ │ │ + * url - {String} │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + */ │ │ │ │ + initialize: function(name, url, options) { │ │ │ │ + if (options && options.sphericalMercator || this.sphericalMercator) { │ │ │ │ + options = OpenLayers.Util.extend({ │ │ │ │ + projection: "EPSG:900913", │ │ │ │ + numZoomLevels: 19 │ │ │ │ + }, options); │ │ │ │ + } │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, [ │ │ │ │ + name || this.name, url || this.url, {}, │ │ │ │ + options │ │ │ │ + ]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * obj - {Object} Is this ever used? │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} A new node of the given type and id. │ │ │ │ - * This function must be overridden by subclasses. │ │ │ │ + * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ │ │ │ │ */ │ │ │ │ - createNode: function(type, id) {}, │ │ │ │ + clone: function(obj) { │ │ │ │ + │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.XYZ(this.name, │ │ │ │ + this.url, │ │ │ │ + this.getOptions()); │ │ │ │ + } │ │ │ │ + │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveRoot │ │ │ │ - * moves this renderer's root to a different renderer. │ │ │ │ - * │ │ │ │ + * Method: getURL │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * renderer - {<OpenLayers.Renderer>} target renderer for the moved root │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A string with the layer's url and parameters and also the │ │ │ │ + * passed-in bounds and appropriate tile size specified as │ │ │ │ + * parameters │ │ │ │ */ │ │ │ │ - moveRoot: function(renderer) { │ │ │ │ - var root = this.root; │ │ │ │ - if (renderer.root.parentNode == this.rendererRoot) { │ │ │ │ - root = renderer.root; │ │ │ │ + getURL: function(bounds) { │ │ │ │ + var xyz = this.getXYZ(bounds); │ │ │ │ + var url = this.url; │ │ │ │ + if (OpenLayers.Util.isArray(url)) { │ │ │ │ + var s = '' + xyz.x + xyz.y + xyz.z; │ │ │ │ + url = this.selectUrl(s, url); │ │ │ │ } │ │ │ │ - root.parentNode.removeChild(root); │ │ │ │ - renderer.rendererRoot.appendChild(root); │ │ │ │ + │ │ │ │ + return OpenLayers.String.format(url, xyz); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ - * │ │ │ │ + * Method: getXYZ │ │ │ │ + * Calculates x, y and z for the given bounds. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} the id of the output layer. │ │ │ │ + * {Object} - an object with x, y and z properties. │ │ │ │ */ │ │ │ │ - getRenderLayerId: function() { │ │ │ │ - return this.root.parentNode.parentNode.id; │ │ │ │ + getXYZ: function(bounds) { │ │ │ │ + var res = this.getServerResolution(); │ │ │ │ + var x = Math.round((bounds.left - this.maxExtent.left) / │ │ │ │ + (res * this.tileSize.w)); │ │ │ │ + var y = Math.round((this.maxExtent.top - bounds.top) / │ │ │ │ + (res * this.tileSize.h)); │ │ │ │ + var z = this.getServerZoom(); │ │ │ │ + │ │ │ │ + if (this.wrapDateLine) { │ │ │ │ + var limit = Math.pow(2, z); │ │ │ │ + x = ((x % limit) + limit) % limit; │ │ │ │ + } │ │ │ │ + │ │ │ │ + return { │ │ │ │ + 'x': x, │ │ │ │ + 'y': y, │ │ │ │ + 'z': z │ │ │ │ + }; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: isComplexSymbol │ │ │ │ - * Determines if a symbol cannot be rendered using drawCircle │ │ │ │ + /* APIMethod: setMap │ │ │ │ + * When the layer is added to a map, then we can fetch our origin │ │ │ │ + * (if we don't have one.) │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * graphicName - {String} │ │ │ │ - * │ │ │ │ - * Returns │ │ │ │ - * {Boolean} true if the symbol is complex, false if not │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - isComplexSymbol: function(graphicName) { │ │ │ │ - return (graphicName != "circle") && !!graphicName; │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ + if (!this.tileOrigin) { │ │ │ │ + this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, │ │ │ │ + this.maxExtent.bottom); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer.Elements" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.XYZ" │ │ │ │ }); │ │ │ │ - │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Renderer/SVG.js │ │ │ │ + OpenLayers/Layer/OSM.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Renderer/Elements.js │ │ │ │ + * @requires OpenLayers/Layer/XYZ.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Renderer.SVG │ │ │ │ + * Class: OpenLayers.Layer.OSM │ │ │ │ + * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap │ │ │ │ + * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use │ │ │ │ + * a different layer instead, you need to provide a different │ │ │ │ + * URL to the constructor. Here's an example for using OpenCycleMap: │ │ │ │ * │ │ │ │ - * Inherits: │ │ │ │ - * - <OpenLayers.Renderer.Elements> │ │ │ │ + * (code) │ │ │ │ + * new OpenLayers.Layer.OSM("OpenCycleMap", │ │ │ │ + * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ + * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ + * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.XYZ> │ │ │ │ */ │ │ │ │ -OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ +OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: xmlns │ │ │ │ - * {String} │ │ │ │ + /** │ │ │ │ + * APIProperty: name │ │ │ │ + * {String} The layer name. Defaults to "OpenStreetMap" if the first │ │ │ │ + * argument to the constructor is null or undefined. │ │ │ │ */ │ │ │ │ - xmlns: "http://www.w3.org/2000/svg", │ │ │ │ + name: "OpenStreetMap", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: xlinkns │ │ │ │ - * {String} │ │ │ │ + * APIProperty: url │ │ │ │ + * {String} The tileset URL scheme. Defaults to │ │ │ │ + * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png │ │ │ │ + * (the official OSM tileset) if the second argument to the constructor │ │ │ │ + * is null or undefined. To use another tileset you can have something │ │ │ │ + * like this: │ │ │ │ + * (code) │ │ │ │ + * new OpenLayers.Layer.OSM("OpenCycleMap", │ │ │ │ + * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ + * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ + * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); │ │ │ │ + * (end) │ │ │ │ */ │ │ │ │ - xlinkns: "http://www.w3.org/1999/xlink", │ │ │ │ + url: [ │ │ │ │ + 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png', │ │ │ │ + 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png', │ │ │ │ + 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png' │ │ │ │ + ], │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Property: attribution │ │ │ │ + * {String} The layer attribution. │ │ │ │ */ │ │ │ │ - MAX_PIXEL: 15000, │ │ │ │ + attribution: "© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: translationParameters │ │ │ │ - * {Object} Hash with "x" and "y" properties │ │ │ │ + * Property: sphericalMercator │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - translationParameters: null, │ │ │ │ + sphericalMercator: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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]. │ │ │ │ + * Property: wrapDateLine │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - symbolMetrics: null, │ │ │ │ + wrapDateLine: true, │ │ │ │ + │ │ │ │ + /** APIProperty: tileOptions │ │ │ │ + * {Object} optional configuration options for <OpenLayers.Tile> instances │ │ │ │ + * created by this Layer. Default is │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * {crossOriginKeyword: 'anonymous'} │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * When using OSM tilesets other than the default ones, it may be │ │ │ │ + * necessary to set this to │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * {crossOriginKeyword: null} │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * if the server does not send Access-Control-Allow-Origin headers. │ │ │ │ + */ │ │ │ │ + tileOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Renderer.SVG │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Layer.OSM │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * containerID - {String} │ │ │ │ + * name - {String} The layer name. │ │ │ │ + * url - {String} The tileset URL scheme. │ │ │ │ + * options - {Object} Configuration options for the layer. Any inherited │ │ │ │ + * layer option can be set in this object (e.g. │ │ │ │ + * <OpenLayers.Layer.Grid.buffer>). │ │ │ │ */ │ │ │ │ - initialize: function(containerID) { │ │ │ │ - if (!this.supported()) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - OpenLayers.Renderer.Elements.prototype.initialize.apply(this, │ │ │ │ - arguments); │ │ │ │ - this.translationParameters = { │ │ │ │ - x: 0, │ │ │ │ - y: 0 │ │ │ │ - }; │ │ │ │ + initialize: function(name, url, options) { │ │ │ │ + OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); │ │ │ │ + this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ + crossOriginKeyword: 'anonymous' │ │ │ │ + }, this.options && this.options.tileOptions); │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.symbolMetrics = {}; │ │ │ │ + /** │ │ │ │ + * Method: clone │ │ │ │ + */ │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.OSM( │ │ │ │ + this.name, this.url, this.getOptions()); │ │ │ │ + } │ │ │ │ + obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.OSM" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/MapGuide.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.MapGuide │ │ │ │ + * Instances of OpenLayers.Layer.MapGuide are used to display │ │ │ │ + * data from a MapGuide OS instance. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} Treat this layer as a base layer. Default is true. │ │ │ │ + **/ │ │ │ │ + isBaseLayer: true, │ │ │ │ + │ │ │ │ /** │ │ │ │ - * APIMethod: supported │ │ │ │ + * APIProperty: useHttpTile │ │ │ │ + * {Boolean} use a tile cache exposed directly via a webserver rather than the │ │ │ │ + * via mapguide server. This does require extra configuration on the Mapguide Server, │ │ │ │ + * and will only work when singleTile is false. The url for the layer must be set to the │ │ │ │ + * webserver path rather than the Mapguide mapagent. │ │ │ │ + * See http://trac.osgeo.org/mapguide/wiki/CodeSamples/Tiles/ServingTilesViaHttp │ │ │ │ + **/ │ │ │ │ + useHttpTile: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: singleTile │ │ │ │ + * {Boolean} use tile server or request single tile image. │ │ │ │ + **/ │ │ │ │ + singleTile: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: useOverlay │ │ │ │ + * {Boolean} flag to indicate if the layer should be retrieved using │ │ │ │ + * GETMAPIMAGE (default) or using GETDYNAMICOVERLAY requests. │ │ │ │ + **/ │ │ │ │ + useOverlay: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: useAsyncOverlay │ │ │ │ + * {Boolean} indicates if the MapGuide site supports the asynchronous │ │ │ │ + * GETDYNAMICOVERLAY requests which is available in MapGuide Enterprise 2010 │ │ │ │ + * and MapGuide Open Source v2.0.3 or higher. The newer versions of MG │ │ │ │ + * is called asynchronously, allows selections to be drawn separately from │ │ │ │ + * the map and offers styling options. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Whether or not the browser supports the SVG renderer │ │ │ │ + * With older versions of MapGuide, set useAsyncOverlay=false. Note that in │ │ │ │ + * this case a synchronous AJAX call is issued and the mapname and session │ │ │ │ + * parameters must be used to initialize the layer, not the mapdefinition │ │ │ │ + * parameter. Also note that this will issue a synchronous AJAX request │ │ │ │ + * before the image request can be issued so the users browser may lock │ │ │ │ + * up if the MG Web tier does not respond in a timely fashion. │ │ │ │ + **/ │ │ │ │ + useAsyncOverlay: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: TILE_PARAMS │ │ │ │ + * {Object} Hashtable of default parameter key/value pairs for tiled layer │ │ │ │ */ │ │ │ │ - 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"))); │ │ │ │ + TILE_PARAMS: { │ │ │ │ + operation: 'GETTILEIMAGE', │ │ │ │ + version: '1.2.0' │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: inValidRange │ │ │ │ - * See #669 for more information │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * x - {Integer} │ │ │ │ - * y - {Integer} │ │ │ │ - * xyOnly - {Boolean} whether or not to just check for x and y, which means │ │ │ │ - * to not take the current translation parameters into account if true. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Whether or not the 'x' and 'y' coordinates are in the │ │ │ │ - * valid range. │ │ │ │ + * Constant: SINGLE_TILE_PARAMS │ │ │ │ + * {Object} Hashtable of default parameter key/value pairs for untiled layer │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + SINGLE_TILE_PARAMS: { │ │ │ │ + operation: 'GETMAPIMAGE', │ │ │ │ + format: 'PNG', │ │ │ │ + locale: 'en', │ │ │ │ + clip: '1', │ │ │ │ + version: '1.0.0' │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setExtent │ │ │ │ - * │ │ │ │ + * Constant: OVERLAY_PARAMS │ │ │ │ + * {Object} Hashtable of default parameter key/value pairs for untiled layer │ │ │ │ + */ │ │ │ │ + OVERLAY_PARAMS: { │ │ │ │ + operation: 'GETDYNAMICMAPOVERLAYIMAGE', │ │ │ │ + format: 'PNG', │ │ │ │ + locale: 'en', │ │ │ │ + clip: '1', │ │ │ │ + version: '2.0.0' │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: FOLDER_PARAMS │ │ │ │ + * {Object} Hashtable of parameter key/value pairs which describe │ │ │ │ + * the folder structure for tiles as configured in the mapguide │ │ │ │ + * serverconfig.ini section [TileServiceProperties] │ │ │ │ + */ │ │ │ │ + FOLDER_PARAMS: { │ │ │ │ + tileColumnsPerFolder: 30, │ │ │ │ + tileRowsPerFolder: 30, │ │ │ │ + format: 'png', │ │ │ │ + querystring: null │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: defaultSize │ │ │ │ + * {<OpenLayers.Size>} Tile size as produced by MapGuide server │ │ │ │ + **/ │ │ │ │ + defaultSize: new OpenLayers.Size(300, 300), │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: tileOriginCorner │ │ │ │ + * {String} MapGuide tile server uses top-left as tile origin │ │ │ │ + **/ │ │ │ │ + tileOriginCorner: "tl", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.MapGuide │ │ │ │ + * Create a new Mapguide layer, either tiled or untiled. │ │ │ │ + * │ │ │ │ + * For tiled layers, the 'groupName' and 'mapDefinition' values │ │ │ │ + * must be specified as parameters in the constructor. │ │ │ │ + * │ │ │ │ + * For untiled base layers, specify either combination of 'mapName' and │ │ │ │ + * 'session', or 'mapDefinition' and 'locale'. │ │ │ │ + * │ │ │ │ + * For older versions of MapGuide and overlay layers, set useAsyncOverlay │ │ │ │ + * to false and in this case mapName and session are required parameters │ │ │ │ + * for the constructor. │ │ │ │ + * │ │ │ │ + * NOTE: MapGuide OS uses a DPI value and degrees to meters conversion │ │ │ │ + * factor that are different than the defaults used in OpenLayers, │ │ │ │ + * so these must be adjusted accordingly in your application. │ │ │ │ + * See the MapGuide example for how to set these values for MGOS. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * extent - {<OpenLayers.Bounds>} │ │ │ │ - * resolutionChanged - {Boolean} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} true to notify the layer that the new extent does not exceed │ │ │ │ - * the coordinate range, and the features will not need to be redrawn. │ │ │ │ - * False otherwise. │ │ │ │ + * name - {String} Name of the layer displayed in the interface │ │ │ │ + * url - {String} Location of the MapGuide mapagent executable │ │ │ │ + * (e.g. http://localhost:8008/mapguide/mapagent/mapagent.fcgi) │ │ │ │ + * params - {Object} hashtable of additional parameters to use. Some │ │ │ │ + * parameters may require additional code on the server. The ones that │ │ │ │ + * you may want to use are: │ │ │ │ + * - mapDefinition - {String} The MapGuide resource definition │ │ │ │ + * (e.g. Library://Samples/Gmap/Maps/gmapTiled.MapDefinition) │ │ │ │ + * - locale - Locale setting │ │ │ │ + * (for untiled overlays layers only) │ │ │ │ + * - mapName - {String} Name of the map as stored in the MapGuide session. │ │ │ │ + * (for untiled layers with a session parameter only) │ │ │ │ + * - session - { String} MapGuide session ID │ │ │ │ + * (for untiled overlays layers only) │ │ │ │ + * - basemaplayergroupname - {String} GroupName for tiled MapGuide layers only │ │ │ │ + * - format - Image format to be returned (for untiled overlay layers only) │ │ │ │ + * - showLayers - {String} A comma separated list of GUID's for the │ │ │ │ + * layers to display eg: 'cvc-xcv34,453-345-345sdf'. │ │ │ │ + * - hideLayers - {String} A comma separated list of GUID's for the │ │ │ │ + * layers to hide eg: 'cvc-xcv34,453-345-345sdf'. │ │ │ │ + * - showGroups - {String} A comma separated list of GUID's for the │ │ │ │ + * groups to display eg: 'cvc-xcv34,453-345-345sdf'. │ │ │ │ + * - hideGroups - {String} A comma separated list of GUID's for the │ │ │ │ + * groups to hide eg: 'cvc-xcv34,453-345-345sdf' │ │ │ │ + * - selectionXml - {String} A selection xml string Some server plumbing │ │ │ │ + * is required to read such a value. │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer; │ │ │ │ + * will vary depending if tiled or untiled maps are being requested │ │ │ │ */ │ │ │ │ - setExtent: function(extent, resolutionChanged) { │ │ │ │ - var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); │ │ │ │ + initialize: function(name, url, params, options) { │ │ │ │ │ │ │ │ - var resolution = this.getResolution(), │ │ │ │ - left = -extent.left / resolution, │ │ │ │ - top = extent.top / resolution; │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments); │ │ │ │ │ │ │ │ - // 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; │ │ │ │ + // unless explicitly set in options, if the layer is transparent, │ │ │ │ + // it will be an overlay │ │ │ │ + if (options == null || options.isBaseLayer == null) { │ │ │ │ + this.isBaseLayer = ((this.transparent != "true") && │ │ │ │ + (this.transparent != true)); │ │ │ │ + } │ │ │ │ │ │ │ │ - this.rendererRoot.setAttributeNS(null, "viewBox", extentString); │ │ │ │ - this.translate(this.xOffset, 0); │ │ │ │ - return true; │ │ │ │ + if (options && options.useOverlay != null) { │ │ │ │ + this.useOverlay = options.useOverlay; │ │ │ │ + } │ │ │ │ + │ │ │ │ + //initialize for untiled layers │ │ │ │ + if (this.singleTile) { │ │ │ │ + if (this.useOverlay) { │ │ │ │ + OpenLayers.Util.applyDefaults( │ │ │ │ + this.params, │ │ │ │ + this.OVERLAY_PARAMS │ │ │ │ + ); │ │ │ │ + if (!this.useAsyncOverlay) { │ │ │ │ + this.params.version = "1.0.0"; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + OpenLayers.Util.applyDefaults( │ │ │ │ + this.params, │ │ │ │ + this.SINGLE_TILE_PARAMS │ │ │ │ + ); │ │ │ │ + } │ │ │ │ } else { │ │ │ │ - var inRange = this.translate(left - this.left + this.xOffset, top - this.top); │ │ │ │ - if (!inRange) { │ │ │ │ - // recenter the coordinate system │ │ │ │ - this.setExtent(extent, true); │ │ │ │ + //initialize for tiled layers │ │ │ │ + if (this.useHttpTile) { │ │ │ │ + OpenLayers.Util.applyDefaults( │ │ │ │ + this.params, │ │ │ │ + this.FOLDER_PARAMS │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + OpenLayers.Util.applyDefaults( │ │ │ │ + this.params, │ │ │ │ + this.TILE_PARAMS │ │ │ │ + ); │ │ │ │ } │ │ │ │ - return coordSysUnchanged && inRange; │ │ │ │ + this.setTileSize(this.defaultSize); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: translate │ │ │ │ - * Transforms the SVG coordinate system │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * x - {Float} │ │ │ │ - * y - {Float} │ │ │ │ - * │ │ │ │ + * Method: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} true if the translation parameters are in the valid coordinates │ │ │ │ - * range, false otherwise. │ │ │ │ + * {<OpenLayers.Layer.MapGuide>} An exact clone of this layer │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.MapGuide(this.name, │ │ │ │ + this.url, │ │ │ │ + this.params, │ │ │ │ + this.getOptions()); │ │ │ │ } │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setSize │ │ │ │ - * Sets the size of the drawing surface. │ │ │ │ - * │ │ │ │ + * Method: getURL │ │ │ │ + * Return a query string for this layer │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * size - {<OpenLayers.Size>} The size of the drawing surface │ │ │ │ + * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox │ │ │ │ + * for the request │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A string with the layer's url and parameters and also │ │ │ │ + * the passed-in bounds and appropriate tile size specified │ │ │ │ + * as parameters. │ │ │ │ */ │ │ │ │ - setSize: function(size) { │ │ │ │ - OpenLayers.Renderer.prototype.setSize.apply(this, arguments); │ │ │ │ + getURL: function(bounds) { │ │ │ │ + var url; │ │ │ │ + var center = bounds.getCenterLonLat(); │ │ │ │ + var mapSize = this.map.getSize(); │ │ │ │ │ │ │ │ - this.rendererRoot.setAttributeNS(null, "width", this.size.w); │ │ │ │ - this.rendererRoot.setAttributeNS(null, "height", this.size.h); │ │ │ │ + if (this.singleTile) { │ │ │ │ + //set up the call for GETMAPIMAGE or GETDYNAMICMAPOVERLAY with │ │ │ │ + //dynamic map parameters │ │ │ │ + var params = { │ │ │ │ + setdisplaydpi: OpenLayers.DOTS_PER_INCH, │ │ │ │ + setdisplayheight: mapSize.h * this.ratio, │ │ │ │ + setdisplaywidth: mapSize.w * this.ratio, │ │ │ │ + setviewcenterx: center.lon, │ │ │ │ + setviewcentery: center.lat, │ │ │ │ + setviewscale: this.map.getScale() │ │ │ │ + }; │ │ │ │ + │ │ │ │ + if (this.useOverlay && !this.useAsyncOverlay) { │ │ │ │ + //first we need to call GETVISIBLEMAPEXTENT to set the extent │ │ │ │ + var getVisParams = {}; │ │ │ │ + getVisParams = OpenLayers.Util.extend(getVisParams, params); │ │ │ │ + getVisParams.operation = "GETVISIBLEMAPEXTENT"; │ │ │ │ + getVisParams.version = "1.0.0"; │ │ │ │ + getVisParams.session = this.params.session; │ │ │ │ + getVisParams.mapName = this.params.mapName; │ │ │ │ + getVisParams.format = 'text/xml'; │ │ │ │ + url = this.getFullRequestString(getVisParams); │ │ │ │ + │ │ │ │ + OpenLayers.Request.GET({ │ │ │ │ + url: url, │ │ │ │ + async: false │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + //construct the full URL │ │ │ │ + url = this.getFullRequestString(params); │ │ │ │ + } else { │ │ │ │ + │ │ │ │ + //tiled version │ │ │ │ + var currentRes = this.map.getResolution(); │ │ │ │ + var colidx = Math.floor((bounds.left - this.maxExtent.left) / currentRes); │ │ │ │ + colidx = Math.round(colidx / this.tileSize.w); │ │ │ │ + var rowidx = Math.floor((this.maxExtent.top - bounds.top) / currentRes); │ │ │ │ + rowidx = Math.round(rowidx / this.tileSize.h); │ │ │ │ + │ │ │ │ + if (this.useHttpTile) { │ │ │ │ + url = this.getImageFilePath({ │ │ │ │ + tilecol: colidx, │ │ │ │ + tilerow: rowidx, │ │ │ │ + scaleindex: this.resolutions.length - this.map.zoom - 1 │ │ │ │ + }); │ │ │ │ + │ │ │ │ + } else { │ │ │ │ + url = this.getFullRequestString({ │ │ │ │ + tilecol: colidx, │ │ │ │ + tilerow: rowidx, │ │ │ │ + scaleindex: this.resolutions.length - this.map.zoom - 1 │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return url; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getNodeType │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: getFullRequestString │ │ │ │ + * getFullRequestString on MapGuide layers is special, because we │ │ │ │ + * do a regular expression replace on ',' in parameters to '+'. │ │ │ │ + * This is why it is subclassed here. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * │ │ │ │ + * altUrl - {String} Alternative base URL to use. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} The corresponding node type for the specified geometry │ │ │ │ + * {String} A string with the layer's url appropriately encoded for MapGuide │ │ │ │ */ │ │ │ │ - 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"; │ │ │ │ + getFullRequestString: function(newParams, altUrl) { │ │ │ │ + // use layer's url unless altUrl passed in │ │ │ │ + var url = (altUrl == null) ? this.url : altUrl; │ │ │ │ + │ │ │ │ + // if url is not a string, it should be an array of strings, │ │ │ │ + // in which case we will randomly select one of them in order │ │ │ │ + // to evenly distribute requests to different urls. │ │ │ │ + if (typeof url == "object") { │ │ │ │ + url = url[Math.floor(Math.random() * url.length)]; │ │ │ │ + } │ │ │ │ + // requestString always starts with url │ │ │ │ + var requestString = url; │ │ │ │ + │ │ │ │ + // create a new params hashtable with all the layer params and the │ │ │ │ + // new params together. then convert to string │ │ │ │ + var allParams = OpenLayers.Util.extend({}, this.params); │ │ │ │ + allParams = OpenLayers.Util.extend(allParams, newParams); │ │ │ │ + // ignore parameters that are already in the url search string │ │ │ │ + var urlParams = OpenLayers.Util.upperCaseObject( │ │ │ │ + OpenLayers.Util.getParameters(url)); │ │ │ │ + for (var key in allParams) { │ │ │ │ + if (key.toUpperCase() in urlParams) { │ │ │ │ + delete allParams[key]; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ + │ │ │ │ + /* MapGuide needs '+' seperating things like bounds/height/width. │ │ │ │ + Since typically this is URL encoded, we use a slight hack: we │ │ │ │ + depend on the list-like functionality of getParameterString to │ │ │ │ + leave ',' only in the case of list items (since otherwise it is │ │ │ │ + encoded) then do a regular expression replace on the , characters │ │ │ │ + to '+' */ │ │ │ │ + paramsString = paramsString.replace(/,/g, "+"); │ │ │ │ + │ │ │ │ + if (paramsString != "") { │ │ │ │ + var lastServerChar = url.charAt(url.length - 1); │ │ │ │ + if ((lastServerChar == "&") || (lastServerChar == "?")) { │ │ │ │ + requestString += paramsString; │ │ │ │ + } else { │ │ │ │ + if (url.indexOf('?') == -1) { │ │ │ │ + //serverPath has no ? -- add one │ │ │ │ + requestString += '?' + paramsString; │ │ │ │ } else { │ │ │ │ - nodeType = "circle"; │ │ │ │ + //serverPath contains ?, so must already have paramsString at the end │ │ │ │ + requestString += '&' + paramsString; │ │ │ │ } │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.Rectangle": │ │ │ │ - nodeType = "rect"; │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.LineString": │ │ │ │ - nodeType = "polyline"; │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.LinearRing": │ │ │ │ - nodeType = "polygon"; │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.Polygon": │ │ │ │ - case "OpenLayers.Geometry.Curve": │ │ │ │ - nodeType = "path"; │ │ │ │ - break; │ │ │ │ - default: │ │ │ │ - break; │ │ │ │ + } │ │ │ │ } │ │ │ │ - return nodeType; │ │ │ │ + return requestString; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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 │ │ │ │ + * Method: getImageFilePath │ │ │ │ + * special handler to request mapguide tiles from an http exposed tilecache │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {SVGDomElement} An SVG element to decorate │ │ │ │ - * style - {Object} │ │ │ │ - * options - {Object} Currently supported options include │ │ │ │ - * 'isFilled' {Boolean} and │ │ │ │ - * 'isStroked' {Boolean} │ │ │ │ + * altUrl - {String} Alternative base URL to use. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A string with the url for the tile image │ │ │ │ */ │ │ │ │ - setStyle: function(node, style, options) { │ │ │ │ - style = style || node._style; │ │ │ │ - options = options || node._options; │ │ │ │ + getImageFilePath: function(newParams, altUrl) { │ │ │ │ + // use layer's url unless altUrl passed in │ │ │ │ + var url = (altUrl == null) ? this.url : altUrl; │ │ │ │ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + // if url is not a string, it should be an array of strings, │ │ │ │ + // in which case we will randomly select one of them in order │ │ │ │ + // to evenly distribute requests to different urls. │ │ │ │ + if (typeof url == "object") { │ │ │ │ + url = url[Math.floor(Math.random() * url.length)]; │ │ │ │ } │ │ │ │ + // requestString always starts with url │ │ │ │ + var requestString = url; │ │ │ │ │ │ │ │ - var r = parseFloat(node.getAttributeNS(null, "r")); │ │ │ │ - var widthFactor = 1; │ │ │ │ - var pos; │ │ │ │ - if (node._geometryClass == "OpenLayers.Geometry.Point" && r) { │ │ │ │ - node.style.visibility = ""; │ │ │ │ - if (style.graphic === false) { │ │ │ │ - node.style.visibility = "hidden"; │ │ │ │ - } else if (style.externalGraphic) { │ │ │ │ - pos = this.getPosition(node); │ │ │ │ - if (style.graphicWidth && style.graphicHeight) { │ │ │ │ - node.setAttributeNS(null, "preserveAspectRatio", "none"); │ │ │ │ - } │ │ │ │ - var width = style.graphicWidth || style.graphicHeight; │ │ │ │ - var height = style.graphicHeight || style.graphicWidth; │ │ │ │ - width = width ? width : style.pointRadius * 2; │ │ │ │ - height = height ? height : style.pointRadius * 2; │ │ │ │ - var xOffset = (style.graphicXOffset != undefined) ? │ │ │ │ - style.graphicXOffset : -(0.5 * width); │ │ │ │ - var yOffset = (style.graphicYOffset != undefined) ? │ │ │ │ - style.graphicYOffset : -(0.5 * height); │ │ │ │ + var tileRowGroup = ""; │ │ │ │ + var tileColGroup = ""; │ │ │ │ │ │ │ │ - var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ + if (newParams.tilerow < 0) { │ │ │ │ + tileRowGroup = '-'; │ │ │ │ + } │ │ │ │ │ │ │ │ - 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; │ │ │ │ + if (newParams.tilerow == 0) { │ │ │ │ + tileRowGroup += '0'; │ │ │ │ + } else { │ │ │ │ + tileRowGroup += Math.floor(Math.abs(newParams.tilerow / this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder; │ │ │ │ + } │ │ │ │ │ │ │ │ - // 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); │ │ │ │ - } │ │ │ │ + if (newParams.tilecol < 0) { │ │ │ │ + tileColGroup = '-'; │ │ │ │ + } │ │ │ │ │ │ │ │ - // 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")); │ │ │ │ + if (newParams.tilecol == 0) { │ │ │ │ + tileColGroup += '0'; │ │ │ │ + } else { │ │ │ │ + tileColGroup += Math.floor(Math.abs(newParams.tilecol / this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder; │ │ │ │ + } │ │ │ │ │ │ │ │ - node.setAttributeNS(null, "width", size); │ │ │ │ - node.setAttributeNS(null, "height", size); │ │ │ │ - node.setAttributeNS(null, "x", pos.x - offset); │ │ │ │ - node.setAttributeNS(null, "y", pos.y - offset); │ │ │ │ + var tilePath = '/S' + Math.floor(newParams.scaleindex) + │ │ │ │ + '/' + this.params.basemaplayergroupname + │ │ │ │ + '/R' + tileRowGroup + │ │ │ │ + '/C' + tileColGroup + │ │ │ │ + '/' + (newParams.tilerow % this.params.tileRowsPerFolder) + │ │ │ │ + '_' + (newParams.tilecol % this.params.tileColumnsPerFolder) + │ │ │ │ + '.' + this.params.format; │ │ │ │ │ │ │ │ - // 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); │ │ │ │ - } │ │ │ │ + if (this.params.querystring) { │ │ │ │ + tilePath += "?" + this.params.querystring; │ │ │ │ + } │ │ │ │ │ │ │ │ - var rotation = style.rotation; │ │ │ │ + requestString += tilePath; │ │ │ │ + return requestString; │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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] + ")"); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.MapGuide" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/ArcIMS.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - if (options.isFilled) { │ │ │ │ - node.setAttributeNS(null, "fill", style.fillColor); │ │ │ │ - node.setAttributeNS(null, "fill-opacity", style.fillOpacity); │ │ │ │ - } else { │ │ │ │ - node.setAttributeNS(null, "fill", "none"); │ │ │ │ - } │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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 (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"); │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + * @requires OpenLayers/Format/ArcXML.js │ │ │ │ + * @requires OpenLayers/Request.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - if (style.pointerEvents) { │ │ │ │ - node.setAttributeNS(null, "pointer-events", style.pointerEvents); │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.ArcIMS │ │ │ │ + * Instances of OpenLayers.Layer.ArcIMS are used to display data from ESRI ArcIMS │ │ │ │ + * Mapping Services. Create a new ArcIMS layer with the <OpenLayers.Layer.ArcIMS> │ │ │ │ + * constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.ArcIMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ │ │ │ │ - if (style.cursor != null) { │ │ │ │ - node.setAttributeNS(null, "cursor", style.cursor); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Constant: DEFAULT_PARAMS │ │ │ │ + * {Object} Default query string parameters. │ │ │ │ + */ │ │ │ │ + DEFAULT_PARAMS: { │ │ │ │ + ClientVersion: "9.2", │ │ │ │ + ServiceName: '' │ │ │ │ + }, │ │ │ │ │ │ │ │ - return node; │ │ │ │ + /** │ │ │ │ + * APIProperty: featureCoordSys │ │ │ │ + * {String} Code for feature coordinate system. Default is "4326". │ │ │ │ + */ │ │ │ │ + featureCoordSys: "4326", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: filterCoordSys │ │ │ │ + * {String} Code for filter coordinate system. Default is "4326". │ │ │ │ + */ │ │ │ │ + filterCoordSys: "4326", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: layers │ │ │ │ + * {Array} An array of objects with layer properties. │ │ │ │ + */ │ │ │ │ + layers: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: async │ │ │ │ + * {Boolean} Request images asynchronously. Default is true. │ │ │ │ + */ │ │ │ │ + async: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: name │ │ │ │ + * {String} Layer name. Default is "ArcIMS". │ │ │ │ + */ │ │ │ │ + name: "ArcIMS", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} The layer is a base layer. Default is true. │ │ │ │ + */ │ │ │ │ + isBaseLayer: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: DEFAULT_OPTIONS │ │ │ │ + * {Object} Default layers properties. │ │ │ │ + */ │ │ │ │ + DEFAULT_OPTIONS: { │ │ │ │ + tileSize: new OpenLayers.Size(512, 512), │ │ │ │ + featureCoordSys: "4326", │ │ │ │ + filterCoordSys: "4326", │ │ │ │ + layers: null, │ │ │ │ + isBaseLayer: true, │ │ │ │ + async: true, │ │ │ │ + name: "ArcIMS" │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: dashStyle │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.ArcIMS │ │ │ │ + * Create a new ArcIMS layer object. │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * (code) │ │ │ │ + * var arcims = new OpenLayers.Layer.ArcIMS( │ │ │ │ + * "Global Sample", │ │ │ │ + * "http://sample.avencia.com/servlet/com.esri.esrimap.Esrimap", │ │ │ │ + * { │ │ │ │ + * service: "OpenLayers_Sample", │ │ │ │ + * layers: [ │ │ │ │ + * // layers to manipulate │ │ │ │ + * {id: "1", visible: true} │ │ │ │ + * ] │ │ │ │ + * } │ │ │ │ + * ); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * style - {Object} │ │ │ │ - * widthFactor - {Number} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A SVG compliant 'stroke-dasharray' value │ │ │ │ + * name - {String} A name for the layer │ │ │ │ + * url - {String} Base url for the ArcIMS server │ │ │ │ + * options - {Object} Optional object with properties to be set on the │ │ │ │ + * layer. │ │ │ │ */ │ │ │ │ - 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, ","); │ │ │ │ + initialize: function(name, url, options) { │ │ │ │ + │ │ │ │ + this.tileSize = new OpenLayers.Size(512, 512); │ │ │ │ + │ │ │ │ + // parameters │ │ │ │ + this.params = OpenLayers.Util.applyDefaults({ │ │ │ │ + ServiceName: options.serviceName │ │ │ │ + }, │ │ │ │ + this.DEFAULT_PARAMS │ │ │ │ + ); │ │ │ │ + this.options = OpenLayers.Util.applyDefaults( │ │ │ │ + options, this.DEFAULT_OPTIONS │ │ │ │ + ); │ │ │ │ + │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply( │ │ │ │ + this, [name, url, this.params, options] │ │ │ │ + ); │ │ │ │ + │ │ │ │ + //layer is transparent │ │ │ │ + if (this.transparent) { │ │ │ │ + │ │ │ │ + // unless explicitly set in options, make layer an overlay │ │ │ │ + if (!this.isBaseLayer) { │ │ │ │ + this.isBaseLayer = false; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // jpegs can never be transparent, so intelligently switch the │ │ │ │ + // format, depending on the browser's capabilities │ │ │ │ + if (this.format == "image/jpeg") { │ │ │ │ + this.format = OpenLayers.Util.alphaHack() ? "image/gif" : "image/png"; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // create an empty layer list if no layers specified in the options │ │ │ │ + if (this.options.layers === null) { │ │ │ │ + this.options.layers = []; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: createNode │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: getURL │ │ │ │ + * Return an image url this layer. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * type - {String} Kind of node to draw │ │ │ │ - * id - {String} Id for node │ │ │ │ - * │ │ │ │ + * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the │ │ │ │ + * request. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} A new node of the given type and id │ │ │ │ + * {String} A string with the map image's url. │ │ │ │ */ │ │ │ │ - createNode: function(type, id) { │ │ │ │ - var node = document.createElementNS(this.xmlns, type); │ │ │ │ - if (id) { │ │ │ │ - node.setAttributeNS(null, "id", id); │ │ │ │ + getURL: function(bounds) { │ │ │ │ + var url = ""; │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ + │ │ │ │ + // create an arcxml request to generate the image │ │ │ │ + var axlReq = new OpenLayers.Format.ArcXML( │ │ │ │ + OpenLayers.Util.extend(this.options, { │ │ │ │ + requesttype: "image", │ │ │ │ + envelope: bounds.toArray(), │ │ │ │ + tileSize: this.tileSize │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + │ │ │ │ + // create a synchronous ajax request to get an arcims image │ │ │ │ + var req = new OpenLayers.Request.POST({ │ │ │ │ + url: this.getFullRequestString(), │ │ │ │ + data: axlReq.write(), │ │ │ │ + async: false │ │ │ │ + }); │ │ │ │ + │ │ │ │ + // if the response exists │ │ │ │ + if (req != null) { │ │ │ │ + var doc = req.responseXML; │ │ │ │ + │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = req.responseText; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // create a new arcxml format to read the response │ │ │ │ + var axlResp = new OpenLayers.Format.ArcXML(); │ │ │ │ + var arcxml = axlResp.read(doc); │ │ │ │ + url = this.getUrlOrImage(arcxml.image.output); │ │ │ │ } │ │ │ │ - return node; │ │ │ │ + │ │ │ │ + return url; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: nodeTypeCompare │ │ │ │ - * │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getURLasync │ │ │ │ + * Get an image url this layer asynchronously, and execute a callback │ │ │ │ + * when the image url is generated. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {SVGDomElement} An SVG element │ │ │ │ - * type - {String} Kind of node │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ + * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the │ │ │ │ + * request. │ │ │ │ + * callback - {Function} Function to call when image url is retrieved. │ │ │ │ + * scope - {Object} The scope of the callback method. │ │ │ │ */ │ │ │ │ - nodeTypeCompare: function(node, type) { │ │ │ │ - return (type == node.nodeName); │ │ │ │ + getURLasync: function(bounds, callback, scope) { │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ + │ │ │ │ + // create an arcxml request to generate the image │ │ │ │ + var axlReq = new OpenLayers.Format.ArcXML( │ │ │ │ + OpenLayers.Util.extend(this.options, { │ │ │ │ + requesttype: "image", │ │ │ │ + envelope: bounds.toArray(), │ │ │ │ + tileSize: this.tileSize │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + │ │ │ │ + // create an asynchronous ajax request to get an arcims image │ │ │ │ + OpenLayers.Request.POST({ │ │ │ │ + url: this.getFullRequestString(), │ │ │ │ + async: true, │ │ │ │ + data: axlReq.write(), │ │ │ │ + callback: function(req) { │ │ │ │ + // process the response from ArcIMS, and call the callback function │ │ │ │ + // to set the image URL │ │ │ │ + var doc = req.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = req.responseText; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // create a new arcxml format to read the response │ │ │ │ + var axlResp = new OpenLayers.Format.ArcXML(); │ │ │ │ + var arcxml = axlResp.read(doc); │ │ │ │ + │ │ │ │ + callback.call(scope, this.getUrlOrImage(arcxml.image.output)); │ │ │ │ + }, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createRenderRoot │ │ │ │ - * │ │ │ │ + * Method: getUrlOrImage │ │ │ │ + * Extract a url or image from the ArcXML image output. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * output - {Object} The image.output property of the object returned from │ │ │ │ + * the ArcXML format read method. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} The specific render engine's root element │ │ │ │ + * {String} A URL for an image (potentially with the data protocol). │ │ │ │ */ │ │ │ │ - createRenderRoot: function() { │ │ │ │ - var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg"); │ │ │ │ - svg.style.display = "block"; │ │ │ │ - return svg; │ │ │ │ + getUrlOrImage: function(output) { │ │ │ │ + var ret = ""; │ │ │ │ + if (output.url) { │ │ │ │ + // If the image response output url is a string, then the image │ │ │ │ + // data is not inline. │ │ │ │ + ret = output.url; │ │ │ │ + } else if (output.data) { │ │ │ │ + // The image data is inline and base64 encoded, create a data │ │ │ │ + // url for the image. This will only work for small images, │ │ │ │ + // due to browser url length limits. │ │ │ │ + ret = "data:image/" + output.type + │ │ │ │ + ";base64," + output.data; │ │ │ │ + } │ │ │ │ + return ret; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createRoot │ │ │ │ - * │ │ │ │ + * Method: setLayerQuery │ │ │ │ + * Set the query definition on this layer. Query definitions are used to │ │ │ │ + * render parts of the spatial data in an image, and can be used to │ │ │ │ + * filter features or layers in the ArcIMS service. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * suffix - {String} suffix to append to the id │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * id - {String} The ArcIMS layer ID. │ │ │ │ + * querydef - {Object} The query definition to apply to this layer. │ │ │ │ */ │ │ │ │ - createRoot: function(suffix) { │ │ │ │ - return this.nodeFactory(this.container.id + suffix, "g"); │ │ │ │ + setLayerQuery: function(id, querydef) { │ │ │ │ + // find the matching layer, if it exists │ │ │ │ + for (var lyr = 0; lyr < this.options.layers.length; lyr++) { │ │ │ │ + if (id == this.options.layers[lyr].id) { │ │ │ │ + // replace this layer definition │ │ │ │ + this.options.layers[lyr].query = querydef; │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // no layer found, create a new definition │ │ │ │ + this.options.layers.push({ │ │ │ │ + id: id, │ │ │ │ + visible: true, │ │ │ │ + query: querydef │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createDefs │ │ │ │ + * Method: getFeatureInfo │ │ │ │ + * Get feature information from ArcIMS. Using the applied geometry, apply │ │ │ │ + * the options to the query (buffer, area/envelope intersection), and │ │ │ │ + * query the ArcIMS service. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} The element to which we'll add the symbol definitions │ │ │ │ + * A note about accuracy: │ │ │ │ + * ArcIMS interprets the accuracy attribute in feature requests to be │ │ │ │ + * something like the 'modulus' operator on feature coordinates, │ │ │ │ + * applied to the database geometry of the feature. It doesn't round, │ │ │ │ + * so your feature coordinates may be up to (1 x accuracy) offset from │ │ │ │ + * the actual feature coordinates. If the accuracy of the layer is not │ │ │ │ + * specified, the accuracy will be computed to be approximately 1 │ │ │ │ + * feature coordinate per screen pixel. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.LonLat>} or {<OpenLayers.Geometry.Polygon>} The │ │ │ │ + * geometry to use when making the query. This should be a closed │ │ │ │ + * polygon for behavior approximating a free selection. │ │ │ │ + * layer - {Object} The ArcIMS layer definition. This is an anonymous object │ │ │ │ + * that looks like: │ │ │ │ + * (code) │ │ │ │ + * { │ │ │ │ + * id: "ArcXML layer ID", // the ArcXML layer ID │ │ │ │ + * query: { │ │ │ │ + * where: "STATE = 'PA'", // the where clause of the query │ │ │ │ + * accuracy: 100 // the accuracy of the returned feature │ │ │ │ + * } │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ + * options - {Object} Object with non-default properties to set on the layer. │ │ │ │ + * Supported properties are buffer, callback, scope, and any other │ │ │ │ + * properties applicable to the ArcXML format. Set the 'callback' and │ │ │ │ + * 'scope' for an object and function to recieve the parsed features │ │ │ │ + * from ArcIMS. │ │ │ │ */ │ │ │ │ - createDefs: function() { │ │ │ │ - var defs = this.nodeFactory(this.container.id + "_defs", "defs"); │ │ │ │ - this.rendererRoot.appendChild(defs); │ │ │ │ - return defs; │ │ │ │ - }, │ │ │ │ + getFeatureInfo: function(geometry, layer, options) { │ │ │ │ + // set the buffer to 1 unit (dd/m/ft?) by default │ │ │ │ + var buffer = options.buffer || 1; │ │ │ │ + // empty callback by default │ │ │ │ + var callback = options.callback || function() {}; │ │ │ │ + // default scope is window (global) │ │ │ │ + var scope = options.scope || window; │ │ │ │ │ │ │ │ - /************************************** │ │ │ │ - * * │ │ │ │ - * GEOMETRY DRAWING FUNCTIONS * │ │ │ │ - * * │ │ │ │ - **************************************/ │ │ │ │ + // apply these option to the request options │ │ │ │ + var requestOptions = {}; │ │ │ │ + OpenLayers.Util.extend(requestOptions, this.options); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawPoint │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or false if the renderer could not draw the point │ │ │ │ - */ │ │ │ │ - drawPoint: function(node, geometry) { │ │ │ │ - return this.drawCircle(node, geometry, 1); │ │ │ │ + // this is a feature request │ │ │ │ + requestOptions.requesttype = "feature"; │ │ │ │ + │ │ │ │ + if (geometry instanceof OpenLayers.LonLat) { │ │ │ │ + // create an envelope if the geometry is really a lon/lat │ │ │ │ + requestOptions.polygon = null; │ │ │ │ + requestOptions.envelope = [ │ │ │ │ + geometry.lon - buffer, │ │ │ │ + geometry.lat - buffer, │ │ │ │ + geometry.lon + buffer, │ │ │ │ + geometry.lat + buffer │ │ │ │ + ]; │ │ │ │ + } else if (geometry instanceof OpenLayers.Geometry.Polygon) { │ │ │ │ + // use the polygon assigned, and empty the envelope │ │ │ │ + requestOptions.envelope = null; │ │ │ │ + requestOptions.polygon = geometry; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // create an arcxml request to get feature requests │ │ │ │ + var arcxml = new OpenLayers.Format.ArcXML(requestOptions); │ │ │ │ + │ │ │ │ + // apply any get feature options to the arcxml request │ │ │ │ + OpenLayers.Util.extend(arcxml.request.get_feature, options); │ │ │ │ + │ │ │ │ + arcxml.request.get_feature.layer = layer.id; │ │ │ │ + if (typeof layer.query.accuracy == "number") { │ │ │ │ + // set the accuracy if it was specified │ │ │ │ + arcxml.request.get_feature.query.accuracy = layer.query.accuracy; │ │ │ │ + } else { │ │ │ │ + // guess that the accuracy is 1 per screen pixel │ │ │ │ + var mapCenter = this.map.getCenter(); │ │ │ │ + var viewPx = this.map.getViewPortPxFromLonLat(mapCenter); │ │ │ │ + viewPx.x++; │ │ │ │ + var mapOffCenter = this.map.getLonLatFromPixel(viewPx); │ │ │ │ + arcxml.request.get_feature.query.accuracy = mapOffCenter.lon - mapCenter.lon; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // set the get_feature query to be the same as the layer passed in │ │ │ │ + arcxml.request.get_feature.query.where = layer.query.where; │ │ │ │ + │ │ │ │ + // use area_intersection │ │ │ │ + arcxml.request.get_feature.query.spatialfilter.relation = "area_intersection"; │ │ │ │ + │ │ │ │ + // create a new asynchronous request to get the feature info │ │ │ │ + OpenLayers.Request.POST({ │ │ │ │ + url: this.getFullRequestString({ │ │ │ │ + 'CustomService': 'Query' │ │ │ │ + }), │ │ │ │ + data: arcxml.write(), │ │ │ │ + callback: function(request) { │ │ │ │ + // parse the arcxml response │ │ │ │ + var response = arcxml.parseResponse(request.responseText); │ │ │ │ + │ │ │ │ + if (!arcxml.iserror()) { │ │ │ │ + // if the arcxml is not an error, call the callback with the features parsed │ │ │ │ + callback.call(scope, response.features); │ │ │ │ + } else { │ │ │ │ + // if the arcxml is an error, return null features selected │ │ │ │ + callback.call(scope, null); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawCircle │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * radius - {Float} │ │ │ │ - * │ │ │ │ + * Method: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} or false if the renderer could not draw the circle │ │ │ │ + * {<OpenLayers.Layer.ArcIMS>} An exact clone of this layer │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + clone: function(obj) { │ │ │ │ │ │ │ │ - if (this.inValidRange(x, y)) { │ │ │ │ - node.setAttributeNS(null, "cx", x); │ │ │ │ - node.setAttributeNS(null, "cy", y); │ │ │ │ - node.setAttributeNS(null, "r", radius); │ │ │ │ - return node; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.ArcIMS(this.name, │ │ │ │ + this.url, │ │ │ │ + this.getOptions()); │ │ │ │ } │ │ │ │ │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.ArcIMS" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/KaMap.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.KaMap │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} KaMap Layer is always a base layer │ │ │ │ + */ │ │ │ │ + isBaseLayer: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: DEFAULT_PARAMS │ │ │ │ + * {Object} parameters set by default. The default parameters set │ │ │ │ + * the format via the 'i' parameter to 'jpeg'. │ │ │ │ + */ │ │ │ │ + DEFAULT_PARAMS: { │ │ │ │ + i: 'jpeg', │ │ │ │ + map: '' │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawLineString │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * Constructor: OpenLayers.Layer.KaMap │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or null if the renderer could not draw all components of │ │ │ │ - * the linestring, or false if nothing could be drawn │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} │ │ │ │ + * url - {String} │ │ │ │ + * params - {Object} Parameters to be sent to the HTTP server in the │ │ │ │ + * query string for the tile. The format can be set via the 'i' │ │ │ │ + * parameter (defaults to jpg) , and the map should be set via │ │ │ │ + * the 'map' parameter. It has been reported that ka-Map may behave │ │ │ │ + * inconsistently if your format parameter does not match the format │ │ │ │ + * parameter configured in your config.php. (See ticket #327 for more │ │ │ │ + * information.) │ │ │ │ + * options - {Object} Additional options for the layer. Any of the │ │ │ │ + * APIProperties listed on this layer, and any layer types it │ │ │ │ + * extends, can be overridden through the options parameter. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ + initialize: function(name, url, params, options) { │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments); │ │ │ │ + this.params = OpenLayers.Util.applyDefaults( │ │ │ │ + this.params, this.DEFAULT_PARAMS │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawLinearRing │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ + * Method: getURL │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} or null if the renderer could not draw all components │ │ │ │ - * of the linear ring, or false if nothing could be drawn │ │ │ │ + * {String} A string with the layer's url and parameters and also the │ │ │ │ + * passed-in bounds and appropriate tile size specified as │ │ │ │ + * parameters │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ + getURL: function(bounds) { │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ + var mapRes = this.map.getResolution(); │ │ │ │ + var scale = Math.round((this.map.getScale() * 10000)) / 10000; │ │ │ │ + var pX = Math.round(bounds.left / mapRes); │ │ │ │ + var pY = -Math.round(bounds.top / mapRes); │ │ │ │ + return this.getFullRequestString({ │ │ │ │ + t: pY, │ │ │ │ + l: pX, │ │ │ │ + s: scale │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: calculateGridLayout │ │ │ │ + * ka-Map uses the center point of the map as an origin for │ │ │ │ + * its tiles. Override calculateGridLayout to center tiles │ │ │ │ + * correctly for this case. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bound>} │ │ │ │ + * origin - {<OpenLayers.LonLat>} │ │ │ │ + * resolution - {Number} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} Object containing properties tilelon, tilelat, startcol, │ │ │ │ + * startrow │ │ │ │ + */ │ │ │ │ + calculateGridLayout: function(bounds, origin, resolution) { │ │ │ │ + var tilelon = resolution * this.tileSize.w; │ │ │ │ + var tilelat = resolution * this.tileSize.h; │ │ │ │ + │ │ │ │ + var offsetlon = bounds.left; │ │ │ │ + var tilecol = Math.floor(offsetlon / tilelon) - this.buffer; │ │ │ │ + │ │ │ │ + var offsetlat = bounds.top; │ │ │ │ + var tilerow = Math.floor(offsetlat / tilelat) + this.buffer; │ │ │ │ + │ │ │ │ + return { │ │ │ │ + tilelon: tilelon, │ │ │ │ + tilelat: tilelat, │ │ │ │ + startcol: tilecol, │ │ │ │ + startrow: tilerow │ │ │ │ + }; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawPolygon │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ + * Method: getTileBoundsForGridIndex │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * row - {Number} The row of the grid │ │ │ │ + * col - {Number} The column of the grid │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} or null if the renderer could not draw all components │ │ │ │ - * of the polygon, or false if nothing could be drawn │ │ │ │ + * {<OpenLayers.Bounds>} The bounds for the tile at (row, col) │ │ │ │ */ │ │ │ │ - drawPolygon: function(node, geometry) { │ │ │ │ - var d = ""; │ │ │ │ - var draw = true; │ │ │ │ - var complete = true; │ │ │ │ - var linearRingResult, path; │ │ │ │ - for (var j = 0, len = geometry.components.length; j < len; j++) { │ │ │ │ - d += " M"; │ │ │ │ - linearRingResult = this.getComponentsString( │ │ │ │ - geometry.components[j].components, " "); │ │ │ │ - path = linearRingResult.path; │ │ │ │ - if (path) { │ │ │ │ - d += " " + path; │ │ │ │ - complete = linearRingResult.complete && complete; │ │ │ │ - } else { │ │ │ │ - draw = false; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - d += " z"; │ │ │ │ - if (draw) { │ │ │ │ - node.setAttributeNS(null, "d", d); │ │ │ │ - node.setAttributeNS(null, "fill-rule", "evenodd"); │ │ │ │ - return complete ? node : null; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ + getTileBoundsForGridIndex: function(row, col) { │ │ │ │ + var origin = this.getTileOrigin(); │ │ │ │ + var tileLayout = this.gridLayout; │ │ │ │ + var tilelon = tileLayout.tilelon; │ │ │ │ + var tilelat = tileLayout.tilelat; │ │ │ │ + var minX = (tileLayout.startcol + col) * tilelon; │ │ │ │ + var minY = (tileLayout.startrow - row) * tilelat; │ │ │ │ + return new OpenLayers.Bounds( │ │ │ │ + minX, minY, │ │ │ │ + minX + tilelon, minY + tilelat │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawRectangle │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ + * APIMethod: clone │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * obj - {Object} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} or false if the renderer could not draw the rectangle │ │ │ │ + * {<OpenLayers.Layer.Kamap>} An exact clone of this OpenLayers.Layer.KaMap │ │ │ │ */ │ │ │ │ - drawRectangle: function(node, geometry) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var x = ((geometry.x - this.featureDx) / resolution + this.left); │ │ │ │ - var y = (this.top - geometry.y / resolution); │ │ │ │ + clone: function(obj) { │ │ │ │ │ │ │ │ - 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; │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.KaMap(this.name, │ │ │ │ + this.url, │ │ │ │ + this.params, │ │ │ │ + this.getOptions()); │ │ │ │ + } │ │ │ │ + │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + if (this.tileSize != null) { │ │ │ │ + obj.tileSize = this.tileSize.clone(); │ │ │ │ } │ │ │ │ + │ │ │ │ + // we do not want to copy reference to grid, so we make a new array │ │ │ │ + obj.grid = []; │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawText │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ + * APIMethod: getTileBounds │ │ │ │ + * Returns The tile bounds for a layer given a pixel location. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * featureId - {String} │ │ │ │ - * style - │ │ │ │ - * location - {<OpenLayers.Geometry.Point>} │ │ │ │ + * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ - │ │ │ │ + getTileBounds: function(viewPortPx) { │ │ │ │ var resolution = this.getResolution(); │ │ │ │ + var tileMapWidth = resolution * this.tileSize.w; │ │ │ │ + var tileMapHeight = resolution * this.tileSize.h; │ │ │ │ + var mapPoint = this.getLonLatFromViewPortPx(viewPortPx); │ │ │ │ + var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth); │ │ │ │ + var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight); │ │ │ │ + return new OpenLayers.Bounds(tileLeft, tileBottom, │ │ │ │ + tileLeft + tileMapWidth, │ │ │ │ + tileBottom + tileMapHeight); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var x = ((location.x - this.featureDx) / resolution + this.left); │ │ │ │ - var y = (location.y / resolution - this.top); │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.KaMap" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/ArcGIS93Rest.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - var suffix = (drawOutline) ? this.LABEL_OUTLINE_SUFFIX : this.LABEL_ID_SUFFIX; │ │ │ │ - var label = this.nodeFactory(featureId + suffix, "text"); │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - label.setAttributeNS(null, "x", x); │ │ │ │ - label.setAttributeNS(null, "y", -y); │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - 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"); │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.ArcGIS93Rest │ │ │ │ + * Instances of OpenLayers.Layer.ArcGIS93Rest are used to display data from │ │ │ │ + * ESRI ArcGIS Server 9.3 (and up?) Mapping Services using the REST API. │ │ │ │ + * Create a new ArcGIS93Rest layer with the <OpenLayers.Layer.ArcGIS93Rest> │ │ │ │ + * constructor. More detail on the REST API is available at │ │ │ │ + * http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/index.html ; │ │ │ │ + * specifically, the URL provided to this layer should be an export service │ │ │ │ + * URL: http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.ArcGIS93Rest = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ │ │ │ │ - if (OpenLayers.IS_GECKO === true) { │ │ │ │ - label.setAttributeNS(null, "dominant-baseline", │ │ │ │ - OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central"); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Constant: DEFAULT_PARAMS │ │ │ │ + * {Object} Hashtable of default parameter key/value pairs │ │ │ │ + */ │ │ │ │ + DEFAULT_PARAMS: { │ │ │ │ + format: "png" │ │ │ │ + }, │ │ │ │ │ │ │ │ - var labelRows = style.label.split('\n'); │ │ │ │ - var numRows = labelRows.length; │ │ │ │ - while (label.childNodes.length > numRows) { │ │ │ │ - label.removeChild(label.lastChild); │ │ │ │ - } │ │ │ │ - for (var i = 0; i < numRows; i++) { │ │ │ │ - var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan"); │ │ │ │ - if (style.labelSelect === true) { │ │ │ │ - tspan._featureId = featureId; │ │ │ │ - tspan._geometry = location; │ │ │ │ - tspan._geometryClass = location.CLASS_NAME; │ │ │ │ - } │ │ │ │ - if (OpenLayers.IS_GECKO === false) { │ │ │ │ - tspan.setAttributeNS(null, "baseline-shift", │ │ │ │ - OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%"); │ │ │ │ - } │ │ │ │ - tspan.setAttribute("x", x); │ │ │ │ - if (i == 0) { │ │ │ │ - var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]]; │ │ │ │ - if (vfactor == null) { │ │ │ │ - vfactor = -.5; │ │ │ │ - } │ │ │ │ - tspan.setAttribute("dy", (vfactor * (numRows - 1)) + "em"); │ │ │ │ - } else { │ │ │ │ - tspan.setAttribute("dy", "1em"); │ │ │ │ - } │ │ │ │ - tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i]; │ │ │ │ - if (!tspan.parentNode) { │ │ │ │ - label.appendChild(tspan); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} Default is true for ArcGIS93Rest layer │ │ │ │ + */ │ │ │ │ + isBaseLayer: true, │ │ │ │ │ │ │ │ - if (!label.parentNode) { │ │ │ │ - this.textRoot.appendChild(label); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getComponentString │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.ArcGIS93Rest │ │ │ │ + * Create a new ArcGIS93Rest layer object. │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * (code) │ │ │ │ + * var arcims = new OpenLayers.Layer.ArcGIS93Rest("MyName", │ │ │ │ + * "http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Specialty/ESRI_StateCityHighway_USA/MapServer/export", │ │ │ │ + * { │ │ │ │ + * layers: "0,1,2" │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * components - {Array(<OpenLayers.Geometry.Point>)} Array of points │ │ │ │ - * separator - {String} character between coordinate pairs. Defaults to "," │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} hash with properties "path" (the string created from the │ │ │ │ - * components and "complete" (false if the renderer was unable to │ │ │ │ - * draw all components) │ │ │ │ + * name - {String} A name for the layer │ │ │ │ + * url - {String} Base url for the ArcGIS server REST service │ │ │ │ + * options - {Object} An object with key/value pairs representing the │ │ │ │ + * options and option values. │ │ │ │ + * │ │ │ │ + * Valid Options: │ │ │ │ + * format - {String} MIME type of desired image type. │ │ │ │ + * layers - {String} Comma-separated list of layers to display. │ │ │ │ + * srs - {String} Projection ID. │ │ │ │ */ │ │ │ │ - getComponentsString: function(components, separator) { │ │ │ │ - var renderCmp = []; │ │ │ │ - var complete = true; │ │ │ │ - var len = components.length; │ │ │ │ - var strings = []; │ │ │ │ - var str, component; │ │ │ │ - for (var i = 0; i < len; i++) { │ │ │ │ - component = components[i]; │ │ │ │ - renderCmp.push(component); │ │ │ │ - str = this.getShortString(component); │ │ │ │ - if (str) { │ │ │ │ - strings.push(str); │ │ │ │ - } else { │ │ │ │ - // The current component is outside the valid range. Let's │ │ │ │ - // see if the previous or next component is inside the range. │ │ │ │ - // If so, add the coordinate of the intersection with the │ │ │ │ - // valid range bounds. │ │ │ │ - if (i > 0) { │ │ │ │ - if (this.getShortString(components[i - 1])) { │ │ │ │ - strings.push(this.clipLine(components[i], │ │ │ │ - components[i - 1])); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (i < len - 1) { │ │ │ │ - if (this.getShortString(components[i + 1])) { │ │ │ │ - strings.push(this.clipLine(components[i], │ │ │ │ - components[i + 1])); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - complete = false; │ │ │ │ + initialize: function(name, url, params, options) { │ │ │ │ + var newArguments = []; │ │ │ │ + //uppercase params │ │ │ │ + params = OpenLayers.Util.upperCaseObject(params); │ │ │ │ + newArguments.push(name, url, params, options); │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ + OpenLayers.Util.applyDefaults( │ │ │ │ + this.params, │ │ │ │ + OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS) │ │ │ │ + ); │ │ │ │ + │ │ │ │ + //layer is transparent │ │ │ │ + if (this.params.TRANSPARENT && │ │ │ │ + this.params.TRANSPARENT.toString().toLowerCase() == "true") { │ │ │ │ + │ │ │ │ + // unless explicitly set in options, make layer an overlay │ │ │ │ + if ((options == null) || (!options.isBaseLayer)) { │ │ │ │ + this.isBaseLayer = false; │ │ │ │ } │ │ │ │ - } │ │ │ │ │ │ │ │ - return { │ │ │ │ - path: strings.join(separator || ","), │ │ │ │ - complete: complete │ │ │ │ - }; │ │ │ │ + // jpegs can never be transparent, so intelligently switch the │ │ │ │ + // format, depending on the browser's capabilities │ │ │ │ + if (this.params.FORMAT == "jpg") { │ │ │ │ + this.params.FORMAT = OpenLayers.Util.alphaHack() ? "gif" : │ │ │ │ + "png"; │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clipLine │ │ │ │ - * Given two points (one inside the valid range, and one outside), │ │ │ │ - * clips the line betweeen the two points so that the new points are both │ │ │ │ - * inside the valid range. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * badComponent - {<OpenLayers.Geometry.Point>} original geometry of the │ │ │ │ - * invalid point │ │ │ │ - * goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the │ │ │ │ - * valid point │ │ │ │ - * Returns │ │ │ │ - * {String} the SVG coordinate pair of the clipped point (like │ │ │ │ - * getShortString), or an empty string if both passed componets are at │ │ │ │ - * the same point. │ │ │ │ + * Method: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.ArcGIS93Rest>} An exact clone of this layer │ │ │ │ */ │ │ │ │ - clipLine: function(badComponent, goodComponent) { │ │ │ │ - if (goodComponent.equals(badComponent)) { │ │ │ │ - return ""; │ │ │ │ - } │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var maxX = this.MAX_PIXEL - this.translationParameters.x; │ │ │ │ - var maxY = this.MAX_PIXEL - this.translationParameters.y; │ │ │ │ - var x1 = (goodComponent.x - this.featureDx) / resolution + this.left; │ │ │ │ - var y1 = this.top - goodComponent.y / resolution; │ │ │ │ - var x2 = (badComponent.x - this.featureDx) / resolution + this.left; │ │ │ │ - var y2 = this.top - badComponent.y / resolution; │ │ │ │ - var k; │ │ │ │ - if (x2 < -maxX || x2 > maxX) { │ │ │ │ - k = (y2 - y1) / (x2 - x1); │ │ │ │ - x2 = x2 < 0 ? -maxX : maxX; │ │ │ │ - y2 = y1 + (x2 - x1) * k; │ │ │ │ - } │ │ │ │ - if (y2 < -maxY || y2 > maxY) { │ │ │ │ - k = (x2 - x1) / (y2 - y1); │ │ │ │ - y2 = y2 < 0 ? -maxY : maxY; │ │ │ │ - x2 = x1 + (y2 - y1) * k; │ │ │ │ + clone: function(obj) { │ │ │ │ + │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.ArcGIS93Rest(this.name, │ │ │ │ + this.url, │ │ │ │ + this.params, │ │ │ │ + this.getOptions()); │ │ │ │ } │ │ │ │ - return x2 + "," + y2; │ │ │ │ + │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getShortString │ │ │ │ - * │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getURL │ │ │ │ + * Return an image url this layer. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} │ │ │ │ - * │ │ │ │ + * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the │ │ │ │ + * request. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} or false if point is outside the valid range │ │ │ │ + * {String} A string with the map image's url. │ │ │ │ */ │ │ │ │ - getShortString: function(point) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var x = ((point.x - this.featureDx) / resolution + this.left); │ │ │ │ - var y = (this.top - point.y / resolution); │ │ │ │ + getURL: function(bounds) { │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ │ │ │ │ - if (this.inValidRange(x, y)) { │ │ │ │ - return x + "," + y; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ + // ArcGIS Server only wants the numeric portion of the projection ID. │ │ │ │ + var projWords = this.projection.getCode().split(":"); │ │ │ │ + var srid = projWords[projWords.length - 1]; │ │ │ │ + │ │ │ │ + var imageSize = this.getImageSize(); │ │ │ │ + var newParams = { │ │ │ │ + 'BBOX': bounds.toBBOX(), │ │ │ │ + 'SIZE': imageSize.w + "," + imageSize.h, │ │ │ │ + // We always want image, the other options were json, image with a whole lotta html around it, etc. │ │ │ │ + 'F': "image", │ │ │ │ + 'BBOXSR': srid, │ │ │ │ + 'IMAGESR': srid │ │ │ │ + }; │ │ │ │ + │ │ │ │ + // Now add the filter parameters. │ │ │ │ + if (this.layerDefs) { │ │ │ │ + var layerDefStrList = []; │ │ │ │ + var layerID; │ │ │ │ + for (layerID in this.layerDefs) { │ │ │ │ + if (this.layerDefs.hasOwnProperty(layerID)) { │ │ │ │ + if (this.layerDefs[layerID]) { │ │ │ │ + layerDefStrList.push(layerID); │ │ │ │ + layerDefStrList.push(":"); │ │ │ │ + layerDefStrList.push(this.layerDefs[layerID]); │ │ │ │ + layerDefStrList.push(";"); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (layerDefStrList.length > 0) { │ │ │ │ + newParams['LAYERDEFS'] = layerDefStrList.join(""); │ │ │ │ + } │ │ │ │ } │ │ │ │ + var requestString = this.getFullRequestString(newParams); │ │ │ │ + return requestString; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getPosition │ │ │ │ - * Finds the position of an svg node. │ │ │ │ - * │ │ │ │ + * Method: setLayerFilter │ │ │ │ + * addTile creates a tile, initializes it, and adds it to the layer div. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} hash with x and y properties, representing the coordinates │ │ │ │ - * within the svg coordinate system │ │ │ │ + * id - {String} The id of the layer to which the filter applies. │ │ │ │ + * queryDef - {String} A sql-ish query filter, for more detail see the ESRI │ │ │ │ + * documentation at http://sampleserver1.arcgisonline.com/ArcGIS/SDK/REST/export.html │ │ │ │ */ │ │ │ │ - getPosition: function(node) { │ │ │ │ - return ({ │ │ │ │ - x: parseFloat(node.getAttributeNS(null, "cx")), │ │ │ │ - y: parseFloat(node.getAttributeNS(null, "cy")) │ │ │ │ - }); │ │ │ │ + setLayerFilter: function(id, queryDef) { │ │ │ │ + if (!this.layerDefs) { │ │ │ │ + this.layerDefs = {}; │ │ │ │ + } │ │ │ │ + if (queryDef) { │ │ │ │ + this.layerDefs[id] = queryDef; │ │ │ │ + } else { │ │ │ │ + delete this.layerDefs[id]; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: importSymbol │ │ │ │ - * add a new symbol definition from the rendererer's symbol hash │ │ │ │ - * │ │ │ │ + * Method: clearLayerFilter │ │ │ │ + * Clears layer filters, either from a specific layer, │ │ │ │ + * or all of them. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * graphicName - {String} name of the symbol to import │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} - the imported symbol │ │ │ │ + * id - {String} The id of the layer from which to remove any │ │ │ │ + * filter. If unspecified/blank, all filters │ │ │ │ + * will be removed. │ │ │ │ */ │ │ │ │ - importSymbol: function(graphicName) { │ │ │ │ - if (!this.defs) { │ │ │ │ - // create svg defs tag │ │ │ │ - this.defs = this.createDefs(); │ │ │ │ - } │ │ │ │ - var id = this.container.id + "-" + graphicName; │ │ │ │ - │ │ │ │ - // check if symbol already exists in the defs │ │ │ │ - var existing = document.getElementById(id); │ │ │ │ - if (existing != null) { │ │ │ │ - return existing; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ - if (!symbol) { │ │ │ │ - throw new Error(graphicName + ' is not a valid symbol name'); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var symbolNode = this.nodeFactory(id, "symbol"); │ │ │ │ - var node = this.nodeFactory(null, "polygon"); │ │ │ │ - symbolNode.appendChild(node); │ │ │ │ - var symbolExtent = new OpenLayers.Bounds( │ │ │ │ - Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ - │ │ │ │ - var points = []; │ │ │ │ - var x, y; │ │ │ │ - for (var i = 0; i < symbol.length; i = i + 2) { │ │ │ │ - x = symbol[i]; │ │ │ │ - y = symbol[i + 1]; │ │ │ │ - symbolExtent.left = Math.min(symbolExtent.left, x); │ │ │ │ - symbolExtent.bottom = Math.min(symbolExtent.bottom, y); │ │ │ │ - symbolExtent.right = Math.max(symbolExtent.right, x); │ │ │ │ - symbolExtent.top = Math.max(symbolExtent.top, y); │ │ │ │ - points.push(x, ",", y); │ │ │ │ + clearLayerFilter: function(id) { │ │ │ │ + if (id) { │ │ │ │ + delete this.layerDefs[id]; │ │ │ │ + } else { │ │ │ │ + delete this.layerDefs; │ │ │ │ } │ │ │ │ - │ │ │ │ - node.setAttributeNS(null, "points", points.join(" ")); │ │ │ │ - │ │ │ │ - var width = symbolExtent.getWidth(); │ │ │ │ - var height = symbolExtent.getHeight(); │ │ │ │ - // create a viewBox three times as large as the symbol itself, │ │ │ │ - // to allow for strokeWidth being displayed correctly at the corners. │ │ │ │ - var viewBox = [symbolExtent.left - width, │ │ │ │ - symbolExtent.bottom - height, width * 3, height * 3 │ │ │ │ - ]; │ │ │ │ - symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" ")); │ │ │ │ - this.symbolMetrics[id] = [ │ │ │ │ - Math.max(width, height), │ │ │ │ - symbolExtent.getCenterLonLat().lon, │ │ │ │ - symbolExtent.getCenterLonLat().lat │ │ │ │ - ]; │ │ │ │ - │ │ │ │ - this.defs.appendChild(symbolNode); │ │ │ │ - return symbolNode; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getFeatureIdFromEvent │ │ │ │ + * APIMethod: mergeNewParams │ │ │ │ + * Catch changeParams and uppercase the new params to be merged in │ │ │ │ + * before calling changeParams on the super class. │ │ │ │ + * │ │ │ │ + * Once params have been changed, the tiles will be reloaded with │ │ │ │ + * the new parameters. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} An <OpenLayers.Event> object │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A feature id or undefined. │ │ │ │ + * newParams - {Object} Hashtable of new params to use │ │ │ │ */ │ │ │ │ - getFeatureIdFromEvent: function(evt) { │ │ │ │ - var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments); │ │ │ │ - if (!featureId) { │ │ │ │ - var target = evt.target; │ │ │ │ - featureId = target.parentNode && target != this.rendererRoot ? │ │ │ │ - target.parentNode._featureId : undefined; │ │ │ │ - } │ │ │ │ - return featureId; │ │ │ │ + mergeNewParams: function(newParams) { │ │ │ │ + var upperParams = OpenLayers.Util.upperCaseObject(newParams); │ │ │ │ + var newArguments = [upperParams]; │ │ │ │ + return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, │ │ │ │ + newArguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer.SVG" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.ArcGIS93Rest" │ │ │ │ }); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN │ │ │ │ - * {Object} │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.SVG.LABEL_ALIGN = { │ │ │ │ - "l": "start", │ │ │ │ - "r": "end", │ │ │ │ - "b": "bottom", │ │ │ │ - "t": "hanging" │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT │ │ │ │ - * {Object} │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.SVG.LABEL_VSHIFT = { │ │ │ │ - // according to │ │ │ │ - // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html │ │ │ │ - // a baseline-shift of -70% shifts the text exactly from the │ │ │ │ - // bottom to the top of the baseline, so -35% moves the text to │ │ │ │ - // the center of the baseline. │ │ │ │ - "t": "-70%", │ │ │ │ - "b": "0" │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR │ │ │ │ - * {Object} │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.SVG.LABEL_VFACTOR = { │ │ │ │ - "t": 0, │ │ │ │ - "b": -1 │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Function: OpenLayers.Renderer.SVG.preventDefault │ │ │ │ - * *Deprecated*. Use <OpenLayers.Event.preventDefault> method instead. │ │ │ │ - * Used to prevent default events (especially opening images in a new tab on │ │ │ │ - * ctrl-click) from being executed for externalGraphic symbols │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.SVG.preventDefault = function(e) { │ │ │ │ - OpenLayers.Event.preventDefault(e); │ │ │ │ -}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Renderer/Canvas.js │ │ │ │ + OpenLayers/Layer/Bing.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Renderer.js │ │ │ │ + * @requires OpenLayers/Layer/XYZ.js │ │ │ │ */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Renderer.Canvas │ │ │ │ - * A renderer based on the 2D 'canvas' drawing element. │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.Bing │ │ │ │ + * Bing layer using direct tile access as provided by Bing Maps REST Services. │ │ │ │ + * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more │ │ │ │ + * information. Note: Terms of Service compliant use requires the map to be │ │ │ │ + * configured with an <OpenLayers.Control.Attribution> control and the │ │ │ │ + * attribution placed on or near the map. │ │ │ │ * │ │ │ │ - * Inherits: │ │ │ │ - * - <OpenLayers.Renderer> │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.XYZ> │ │ │ │ */ │ │ │ │ -OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { │ │ │ │ +OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: hitDetection │ │ │ │ - * {Boolean} Allow for hit detection of features. Default is true. │ │ │ │ + * Property: key │ │ │ │ + * {String} API key for Bing maps, get your own key │ │ │ │ + * at http://bingmapsportal.com/ . │ │ │ │ */ │ │ │ │ - hitDetection: true, │ │ │ │ + key: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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). │ │ │ │ + * Property: serverResolutions │ │ │ │ + * {Array} the resolutions provided by the Bing servers. │ │ │ │ */ │ │ │ │ - hitOverflow: 0, │ │ │ │ + serverResolutions: [ │ │ │ │ + 156543.03390625, 78271.516953125, 39135.7584765625, │ │ │ │ + 19567.87923828125, 9783.939619140625, 4891.9698095703125, │ │ │ │ + 2445.9849047851562, 1222.9924523925781, 611.4962261962891, │ │ │ │ + 305.74811309814453, 152.87405654907226, 76.43702827453613, │ │ │ │ + 38.218514137268066, 19.109257068634033, 9.554628534317017, │ │ │ │ + 4.777314267158508, 2.388657133579254, 1.194328566789627, │ │ │ │ + 0.5971642833948135, 0.29858214169740677, 0.14929107084870338, │ │ │ │ + 0.07464553542435169 │ │ │ │ + ], │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: canvas │ │ │ │ - * {Canvas} The canvas context object. │ │ │ │ + * Property: attributionTemplate │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - canvas: null, │ │ │ │ + attributionTemplate: '<span class="olBingAttribution ${type}">' + │ │ │ │ + '<div><a target="_blank" href="http://www.bing.com/maps/">' + │ │ │ │ + '<img src="${logo}" /></a></div>${copyrights}' + │ │ │ │ + '<a style="white-space: nowrap" target="_blank" ' + │ │ │ │ + 'href="http://www.microsoft.com/maps/product/terms.html">' + │ │ │ │ + 'Terms of Use</a></span>', │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: features │ │ │ │ - * {Object} Internal object of feature/style pairs for use in redrawing the layer. │ │ │ │ + * Property: metadata │ │ │ │ + * {Object} Metadata for this layer, as returned by the callback script │ │ │ │ */ │ │ │ │ - features: null, │ │ │ │ + metadata: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: pendingRedraw │ │ │ │ - * {Boolean} The renderer needs a redraw call to render features added while │ │ │ │ - * the renderer was locked. │ │ │ │ + * Property: protocolRegex │ │ │ │ + * {RegExp} Regular expression to match and replace http: in bing urls │ │ │ │ */ │ │ │ │ - pendingRedraw: false, │ │ │ │ + protocolRegex: /^http:/i, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: cachedSymbolBounds │ │ │ │ - * {Object} Internal cache of calculated symbol extents. │ │ │ │ + * APIProperty: type │ │ │ │ + * {String} The layer identifier. Any non-birdseye imageryType │ │ │ │ + * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be │ │ │ │ + * used. Default is "Road". │ │ │ │ */ │ │ │ │ - cachedSymbolBounds: {}, │ │ │ │ + type: "Road", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Renderer.Canvas │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * containerID - {<String>} │ │ │ │ - * options - {Object} Optional properties to be set on the renderer. │ │ │ │ + * APIProperty: culture │ │ │ │ + * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx │ │ │ │ + * for the definition and the possible values. Default is "en-US". │ │ │ │ */ │ │ │ │ - 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"); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + culture: "en-US", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setExtent │ │ │ │ - * Set the visible part of the layer. │ │ │ │ + * APIProperty: metadataParams │ │ │ │ + * {Object} Optional url parameters for the Get Imagery Metadata request │ │ │ │ + * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx │ │ │ │ + */ │ │ │ │ + metadataParams: null, │ │ │ │ + │ │ │ │ + /** APIProperty: tileOptions │ │ │ │ + * {Object} optional configuration options for <OpenLayers.Tile> instances │ │ │ │ + * created by this Layer. Default is │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * extent - {<OpenLayers.Bounds>} │ │ │ │ - * resolutionChanged - {Boolean} │ │ │ │ + * (code) │ │ │ │ + * {crossOriginKeyword: 'anonymous'} │ │ │ │ + * (end) │ │ │ │ + */ │ │ │ │ + tileOptions: null, │ │ │ │ + │ │ │ │ + /** APIProperty: protocol │ │ │ │ + * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo │ │ │ │ + * Can be 'http:' 'https:' or '' │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * Warning: tiles may not be available under both HTTP and HTTPS protocols. │ │ │ │ + * Microsoft approved use of both HTTP and HTTPS urls for tiles. However │ │ │ │ + * this is undocumented and the Imagery Metadata API always returns HTTP │ │ │ │ + * urls. │ │ │ │ + * │ │ │ │ + * Default is '', unless when executed from a file:/// uri, in which case │ │ │ │ + * it is 'http:'. │ │ │ │ */ │ │ │ │ - setExtent: function() { │ │ │ │ - OpenLayers.Renderer.prototype.setExtent.apply(this, arguments); │ │ │ │ - // always redraw features │ │ │ │ - return false; │ │ │ │ - }, │ │ │ │ + protocol: ~window.location.href.indexOf('http') ? '' : 'http:', │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Bing │ │ │ │ + * Create a new Bing layer. │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * (code) │ │ │ │ + * var road = new OpenLayers.Layer.Bing({ │ │ │ │ + * name: "My Bing Aerial Layer", │ │ │ │ + * type: "Aerial", │ │ │ │ + * key: "my-api-key-here", │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * featureId - {String} │ │ │ │ + * options - {Object} Configuration properties for the layer. │ │ │ │ + * │ │ │ │ + * Required configuration properties: │ │ │ │ + * key - {String} Bing Maps API key for your application. Get one at │ │ │ │ + * http://bingmapsportal.com/. │ │ │ │ + * type - {String} The layer identifier. Any non-birdseye imageryType │ │ │ │ + * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be │ │ │ │ + * used. │ │ │ │ + * │ │ │ │ + * Any other documented layer properties can be provided in the config object. │ │ │ │ */ │ │ │ │ - eraseGeometry: function(geometry, featureId) { │ │ │ │ - this.eraseFeatures(this.features[featureId][0]); │ │ │ │ + initialize: function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults({ │ │ │ │ + sphericalMercator: true │ │ │ │ + }, options); │ │ │ │ + var name = options.name || "Bing " + (options.type || this.type); │ │ │ │ + │ │ │ │ + var newArgs = [name, null, options]; │ │ │ │ + OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs); │ │ │ │ + this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ + crossOriginKeyword: 'anonymous' │ │ │ │ + }, this.options.tileOptions); │ │ │ │ + this.loadMetadata(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: supported │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Whether or not the browser supports the renderer class │ │ │ │ + * Method: loadMetadata │ │ │ │ */ │ │ │ │ - supported: function() { │ │ │ │ - return OpenLayers.CANVAS_SUPPORTED; │ │ │ │ + loadMetadata: function() { │ │ │ │ + this._callbackId = "_callback_" + this.id.replace(/\./g, "_"); │ │ │ │ + // link the processMetadata method to the global scope and bind it │ │ │ │ + // to this instance │ │ │ │ + window[this._callbackId] = OpenLayers.Function.bind( │ │ │ │ + OpenLayers.Layer.Bing.processMetadata, this │ │ │ │ + ); │ │ │ │ + var params = OpenLayers.Util.applyDefaults({ │ │ │ │ + key: this.key, │ │ │ │ + jsonp: this._callbackId, │ │ │ │ + include: "ImageryProviders" │ │ │ │ + }, this.metadataParams); │ │ │ │ + var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" + │ │ │ │ + this.type + "?" + OpenLayers.Util.getParameterString(params); │ │ │ │ + var script = document.createElement("script"); │ │ │ │ + script.type = "text/javascript"; │ │ │ │ + script.src = url; │ │ │ │ + script.id = this._callbackId; │ │ │ │ + document.getElementsByTagName("head")[0].appendChild(script); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setSize │ │ │ │ - * Sets the size of the drawing surface. │ │ │ │ - * │ │ │ │ - * Once the size is updated, redraw the canvas. │ │ │ │ + * Method: initLayer │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * size - {<OpenLayers.Size>} │ │ │ │ + * Sets layer properties according to the metadata provided by the API │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + initLayer: function() { │ │ │ │ + var res = this.metadata.resourceSets[0].resources[0]; │ │ │ │ + var url = res.imageUrl.replace("{quadkey}", "${quadkey}"); │ │ │ │ + url = url.replace("{culture}", this.culture); │ │ │ │ + url = url.replace(this.protocolRegex, this.protocol); │ │ │ │ + this.url = []; │ │ │ │ + for (var i = 0; i < res.imageUrlSubdomains.length; ++i) { │ │ │ │ + this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i])); │ │ │ │ + } │ │ │ │ + this.addOptions({ │ │ │ │ + maxResolution: Math.min( │ │ │ │ + this.serverResolutions[res.zoomMin], │ │ │ │ + this.maxResolution || Number.POSITIVE_INFINITY │ │ │ │ + ), │ │ │ │ + numZoomLevels: Math.min( │ │ │ │ + res.zoomMax + 1 - res.zoomMin, this.numZoomLevels │ │ │ │ + ) │ │ │ │ + }, true); │ │ │ │ + if (!this.isBaseLayer) { │ │ │ │ + this.redraw(); │ │ │ │ } │ │ │ │ + this.updateAttribution(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawFeature │ │ │ │ - * Draw the feature. Stores the feature in the features list, │ │ │ │ - * then redraws the layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * style - {<Object>} │ │ │ │ + * Method: getURL │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * Paramters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ + getURL: function(bounds) { │ │ │ │ + if (!this.url) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + var xyz = this.getXYZ(bounds), │ │ │ │ + x = xyz.x, │ │ │ │ + y = xyz.y, │ │ │ │ + z = xyz.z; │ │ │ │ + var quadDigits = []; │ │ │ │ + for (var i = z; i > 0; --i) { │ │ │ │ + var digit = '0'; │ │ │ │ + var mask = 1 << (i - 1); │ │ │ │ + if ((x & mask) != 0) { │ │ │ │ + digit++; │ │ │ │ } │ │ │ │ - │ │ │ │ - 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]); │ │ │ │ + if ((y & mask) != 0) { │ │ │ │ + digit++; │ │ │ │ + digit++; │ │ │ │ } │ │ │ │ - this.pendingRedraw = true; │ │ │ │ - } │ │ │ │ - if (this.pendingRedraw && !this.locked) { │ │ │ │ - this.redraw(); │ │ │ │ - this.pendingRedraw = false; │ │ │ │ + quadDigits.push(digit); │ │ │ │ } │ │ │ │ - return rendered; │ │ │ │ + var quadKey = quadDigits.join(""); │ │ │ │ + var url = this.selectUrl('' + x + y + z, this.url); │ │ │ │ + │ │ │ │ + return OpenLayers.String.format(url, { │ │ │ │ + 'quadkey': quadKey │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawGeometry │ │ │ │ - * Used when looping (in redraw) over the features; draws │ │ │ │ - * the canvas. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ + /** │ │ │ │ + * Method: updateAttribution │ │ │ │ + * Updates the attribution according to the requirements outlined in │ │ │ │ + * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + updateAttribution: function() { │ │ │ │ + var metadata = this.metadata; │ │ │ │ + if (!metadata.resourceSets || !this.map || !this.map.center) { │ │ │ │ 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; │ │ │ │ + var res = metadata.resourceSets[0].resources[0]; │ │ │ │ + var extent = this.map.getExtent().transform( │ │ │ │ + this.map.getProjectionObject(), │ │ │ │ + new OpenLayers.Projection("EPSG:4326") │ │ │ │ + ); │ │ │ │ + var providers = res.imageryProviders || [], │ │ │ │ + zoom = OpenLayers.Util.indexOf(this.serverResolutions, │ │ │ │ + this.getServerResolution()), │ │ │ │ + copyrights = "", │ │ │ │ + provider, i, ii, j, jj, bbox, coverage; │ │ │ │ + for (i = 0, ii = providers.length; i < ii; ++i) { │ │ │ │ + provider = providers[i]; │ │ │ │ + for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) { │ │ │ │ + coverage = provider.coverageAreas[j]; │ │ │ │ + // axis order provided is Y,X │ │ │ │ + bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true); │ │ │ │ + if (extent.intersectsBounds(bbox) && │ │ │ │ + zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) { │ │ │ │ + copyrights += provider.attribution + " "; │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol); │ │ │ │ + this.attribution = OpenLayers.String.format(this.attributionTemplate, { │ │ │ │ + type: this.type.toLowerCase(), │ │ │ │ + logo: logo, │ │ │ │ + copyrights: copyrights │ │ │ │ + }); │ │ │ │ + this.map && this.map.events.triggerEvent("changelayer", { │ │ │ │ + layer: this, │ │ │ │ + property: "attribution" │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawExternalGraphic │ │ │ │ - * Called to draw External graphics. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {String} │ │ │ │ + * Method: setMap │ │ │ │ */ │ │ │ │ - drawExternalGraphic: function(geometry, style, featureId) { │ │ │ │ - var img = new Image(); │ │ │ │ + setMap: function() { │ │ │ │ + OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments); │ │ │ │ + this.map.events.register("moveend", this, this.updateAttribution); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var title = style.title || style.graphicTitle; │ │ │ │ - if (title) { │ │ │ │ - img.title = title; │ │ │ │ + /** │ │ │ │ + * APIMethod: clone │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * obj - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing> │ │ │ │ + */ │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.Bing(this.options); │ │ │ │ } │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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); │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.map && │ │ │ │ + this.map.events.unregister("moveend", this, this.updateAttribution); │ │ │ │ + OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Bing" │ │ │ │ +}); │ │ │ │ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }; │ │ │ │ +/** │ │ │ │ + * Function: OpenLayers.Layer.Bing.processMetadata │ │ │ │ + * This function will be bound to an instance, linked to the global scope with │ │ │ │ + * an id, and called by the JSONP script returned by the API. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * metadata - {Object} metadata as returned by the API │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Bing.processMetadata = function(metadata) { │ │ │ │ + this.metadata = metadata; │ │ │ │ + this.initLayer(); │ │ │ │ + var script = document.getElementById(this._callbackId); │ │ │ │ + script.parentNode.removeChild(script); │ │ │ │ + window[this._callbackId] = undefined; // cannot delete from window in IE │ │ │ │ + delete this._callbackId; │ │ │ │ +}; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/MapServer.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - img.onload = OpenLayers.Function.bind(onLoad, this); │ │ │ │ - img.src = style.externalGraphic; │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.MapServer │ │ │ │ + * Instances of OpenLayers.Layer.MapServer are used to display │ │ │ │ + * data from a MapServer CGI instance. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constant: DEFAULT_PARAMS │ │ │ │ + * {Object} Hashtable of default parameter key/value pairs │ │ │ │ + */ │ │ │ │ + DEFAULT_PARAMS: { │ │ │ │ + mode: "map", │ │ │ │ + map_imagetype: "png" │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawNamedSymbol │ │ │ │ - * Called to draw Well Known Graphic Symbol Name. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {String} │ │ │ │ + * Constructor: OpenLayers.Layer.MapServer │ │ │ │ + * Create a new MapServer layer object │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} A name for the layer │ │ │ │ + * url - {String} Base url for the MapServer CGI │ │ │ │ + * (e.g. http://www2.dmsolutions.ca/cgi-bin/mapserv) │ │ │ │ + * params - {Object} An object with key/value pairs representing the │ │ │ │ + * GetMap query string parameters and parameter values. │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ */ │ │ │ │ - drawNamedSymbol: function(geometry, style, featureId) { │ │ │ │ - var x, y, cx, cy, i, symbolBounds, scaling, angle; │ │ │ │ - var unscaledStrokeWidth; │ │ │ │ - var deg2rad = Math.PI / 180.0; │ │ │ │ + initialize: function(name, url, params, options) { │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments); │ │ │ │ │ │ │ │ - var symbol = OpenLayers.Renderer.symbol[style.graphicName]; │ │ │ │ + this.params = OpenLayers.Util.applyDefaults( │ │ │ │ + this.params, this.DEFAULT_PARAMS │ │ │ │ + ); │ │ │ │ │ │ │ │ - if (!symbol) { │ │ │ │ - throw new Error(style.graphicName + ' is not a valid symbol name'); │ │ │ │ + // unless explicitly set in options, if the layer is transparent, │ │ │ │ + // it will be an overlay │ │ │ │ + if (options == null || options.isBaseLayer == null) { │ │ │ │ + this.isBaseLayer = ((this.params.transparent != "true") && │ │ │ │ + (this.params.transparent != true)); │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (!symbol.length || symbol.length < 2) return; │ │ │ │ + /** │ │ │ │ + * Method: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.MapServer>} An exact clone of this layer │ │ │ │ + */ │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.MapServer(this.name, │ │ │ │ + this.url, │ │ │ │ + this.params, │ │ │ │ + this.getOptions()); │ │ │ │ + } │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ │ │ │ │ - var pt = this.getLocalXY(geometry); │ │ │ │ - var p0 = pt[0]; │ │ │ │ - var p1 = pt[1]; │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ │ │ │ │ - if (isNaN(p0) || isNaN(p1)) return; │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Use rounded line caps │ │ │ │ - this.canvas.lineCap = "round"; │ │ │ │ - this.canvas.lineJoin = "round"; │ │ │ │ + /** │ │ │ │ + * Method: getURL │ │ │ │ + * Return a query string for this layer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox │ │ │ │ + * for the request │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A string with the layer's url and parameters and also │ │ │ │ + * the passed-in bounds and appropriate tile size specified │ │ │ │ + * as parameters. │ │ │ │ + */ │ │ │ │ + getURL: function(bounds) { │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ + // Make a list, so that getFullRequestString uses literal "," │ │ │ │ + var extent = [bounds.left, bounds.bottom, bounds.right, bounds.top]; │ │ │ │ │ │ │ │ - if (this.hitDetection) { │ │ │ │ - this.hitContext.lineCap = "round"; │ │ │ │ - this.hitContext.lineJoin = "round"; │ │ │ │ - } │ │ │ │ + var imageSize = this.getImageSize(); │ │ │ │ │ │ │ │ - // 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; │ │ │ │ - } │ │ │ │ + // make lists, so that literal ','s are used │ │ │ │ + var url = this.getFullRequestString({ │ │ │ │ + mapext: extent, │ │ │ │ + imgext: extent, │ │ │ │ + map_size: [imageSize.w, imageSize.h], │ │ │ │ + imgx: imageSize.w / 2, │ │ │ │ + imgy: imageSize.h / 2, │ │ │ │ + imgxy: [imageSize.w, imageSize.h] │ │ │ │ + }); │ │ │ │ │ │ │ │ - // 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(); │ │ │ │ - } │ │ │ │ + return url; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Step 3: place symbol at the desired location │ │ │ │ - this.canvas.translate(p0, p1); │ │ │ │ - if (this.hitDetection) { │ │ │ │ - this.hitContext.translate(p0, p1); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: getFullRequestString │ │ │ │ + * combine the layer's url with its params and these newParams. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * newParams - {Object} New parameters that should be added to the │ │ │ │ + * request string. │ │ │ │ + * altUrl - {String} (optional) Replace the URL in the full request │ │ │ │ + * string with the provided URL. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A string with the layer's url and parameters embedded in it. │ │ │ │ + */ │ │ │ │ + getFullRequestString: function(newParams, altUrl) { │ │ │ │ + // use layer's url unless altUrl passed in │ │ │ │ + var url = (altUrl == null) ? this.url : altUrl; │ │ │ │ │ │ │ │ - // 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); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + // 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); │ │ │ │ │ │ │ │ - // // 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); │ │ │ │ + // 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); │ │ │ │ } │ │ │ │ │ │ │ │ - // 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); │ │ │ │ + // 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); │ │ │ │ │ │ │ │ - // 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; │ │ │ │ + // requestString always starts with url │ │ │ │ + var requestString = url; │ │ │ │ │ │ │ │ - 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(); │ │ │ │ + // MapServer needs '+' seperating things like bounds/height/width. │ │ │ │ + // Since typically this is URL encoded, we use a slight hack: we │ │ │ │ + // depend on the list-like functionality of getParameterString to │ │ │ │ + // leave ',' only in the case of list items (since otherwise it is │ │ │ │ + // encoded) then do a regular expression replace on the , characters │ │ │ │ + // to '+' │ │ │ │ + // │ │ │ │ + paramsString = paramsString.replace(/,/g, "+"); │ │ │ │ │ │ │ │ - if (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); │ │ │ │ + if (paramsString != "") { │ │ │ │ + var lastServerChar = url.charAt(url.length - 1); │ │ │ │ + if ((lastServerChar == "&") || (lastServerChar == "?")) { │ │ │ │ + requestString += paramsString; │ │ │ │ + } else { │ │ │ │ + if (url.indexOf('?') == -1) { │ │ │ │ + //serverPath has no ? -- add one │ │ │ │ + requestString += '?' + paramsString; │ │ │ │ + } else { │ │ │ │ + //serverPath contains ?, so must already have paramsString at the end │ │ │ │ + requestString += '&' + paramsString; │ │ │ │ } │ │ │ │ - this.hitContext.closePath(); │ │ │ │ - this.hitContext.fill(); │ │ │ │ } │ │ │ │ } │ │ │ │ + return requestString; │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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(); │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.MapServer" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/GeoRSS.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - 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(); │ │ │ │ - } │ │ │ │ │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Markers.js │ │ │ │ + * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - style.strokeWidth = unscaledStrokeWidth; │ │ │ │ - this.canvas.restore(); │ │ │ │ - if (this.hitDetection) { │ │ │ │ - this.hitContext.restore(); │ │ │ │ - } │ │ │ │ - this.setCanvasStyle("reset"); │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.GeoRSS │ │ │ │ + * Add GeoRSS Point features to your map. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Markers> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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 │ │ │ │ + /** │ │ │ │ + * Property: location │ │ │ │ + * {String} store url of text file │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + location: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: featureIdToHex │ │ │ │ - * Convert a feature ID string into an RGB hex string. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * featureId - {String} Feature id │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} RGB hex string. │ │ │ │ + /** │ │ │ │ + * Property: features │ │ │ │ + * {Array(<OpenLayers.Feature>)} │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - }, │ │ │ │ + features: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setHitContextStyle │ │ │ │ - * Prepare the hit canvas for drawing by setting various global settings. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * type - {String} one of 'stroke', 'fill', or 'reset' │ │ │ │ - * featureId - {String} The feature id. │ │ │ │ - * symbolizer - {<OpenLayers.Symbolizer>} The symbolizer. │ │ │ │ + * APIProperty: formatOptions │ │ │ │ + * {Object} Hash of options which should be passed to the format when it is │ │ │ │ + * created. Must be passed in the constructor. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + formatOptions: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: selectedFeature │ │ │ │ + * {<OpenLayers.Feature>} │ │ │ │ + */ │ │ │ │ + selectedFeature: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: icon │ │ │ │ + * {<OpenLayers.Icon>}. This determines the Icon to be used on the map │ │ │ │ + * for this GeoRSS layer. │ │ │ │ + */ │ │ │ │ + icon: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawPoint │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {String} │ │ │ │ + * APIProperty: popupSize │ │ │ │ + * {<OpenLayers.Size>} This determines the size of GeoRSS popups. If │ │ │ │ + * not provided, defaults to 250px by 120px. │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + popupSize: null, │ │ │ │ │ │ │ │ - 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"); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: useFeedTitle │ │ │ │ + * {Boolean} Set layer.name to the first <title> element in the feed. Default is true. │ │ │ │ + */ │ │ │ │ + useFeedTitle: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawLineString │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {String} │ │ │ │ + * Constructor: OpenLayers.Layer.GeoRSS │ │ │ │ + * Create a GeoRSS Layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} │ │ │ │ + * location - {String} │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - drawLineString: function(geometry, style, featureId) { │ │ │ │ - style = OpenLayers.Util.applyDefaults({ │ │ │ │ - fill: false │ │ │ │ - }, style); │ │ │ │ - this.drawLinearRing(geometry, style, featureId); │ │ │ │ + initialize: function(name, location, options) { │ │ │ │ + OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]); │ │ │ │ + this.location = location; │ │ │ │ + this.features = []; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawLinearRing │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {String} │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - 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"); │ │ │ │ + destroy: function() { │ │ │ │ + // Warning: Layer.Markers.destroy() must be called prior to calling │ │ │ │ + // clearFeatures() here, otherwise we leak memory. Indeed, if │ │ │ │ + // Layer.Markers.destroy() is called after clearFeatures(), it won't be │ │ │ │ + // able to remove the marker image elements from the layer's div since │ │ │ │ + // the markers will have been destroyed by clearFeatures(). │ │ │ │ + OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments); │ │ │ │ + this.clearFeatures(); │ │ │ │ + this.features = null; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: renderPath │ │ │ │ - * Render a path with stroke and optional fill. │ │ │ │ + * Method: loadRSS │ │ │ │ + * Start the load of the RSS data. Don't do this when we first add the layer, │ │ │ │ + * since we may not be visible at any point, and it would therefore be a waste. │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ - } │ │ │ │ + loadRSS: function() { │ │ │ │ + if (!this.loaded) { │ │ │ │ + this.events.triggerEvent("loadstart"); │ │ │ │ + OpenLayers.Request.GET({ │ │ │ │ + url: this.location, │ │ │ │ + success: this.parseData, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.loaded = true; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawPolygon │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ + * Method: moveTo │ │ │ │ + * If layer is visible and RSS has not been loaded, load RSS. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {String} │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {Object} │ │ │ │ + * zoomChanged - {Object} │ │ │ │ + * minor - {Object} │ │ │ │ */ │ │ │ │ - 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 │ │ │ │ - ); │ │ │ │ + moveTo: function(bounds, zoomChanged, minor) { │ │ │ │ + OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments); │ │ │ │ + if (this.visibility && !this.loaded) { │ │ │ │ + this.loadRSS(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawText │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ + * Method: parseData │ │ │ │ + * Parse the data returned from the Events call. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * location - {<OpenLayers.Point>} │ │ │ │ - * style - {Object} │ │ │ │ + * ajaxRequest - {<OpenLayers.Request.XMLHttpRequest>} │ │ │ │ */ │ │ │ │ - drawText: function(location, style) { │ │ │ │ - var pt = this.getLocalXY(location); │ │ │ │ + parseData: function(ajaxRequest) { │ │ │ │ + var doc = ajaxRequest.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText); │ │ │ │ + } │ │ │ │ │ │ │ │ - this.setCanvasStyle("reset"); │ │ │ │ - this.canvas.fillStyle = style.fontColor; │ │ │ │ - this.canvas.globalAlpha = style.fontOpacity || 1.0; │ │ │ │ - var fontStyle = [style.fontStyle ? style.fontStyle : "normal", │ │ │ │ - "normal", // "font-variant" not supported │ │ │ │ - style.fontWeight ? style.fontWeight : "normal", │ │ │ │ - style.fontSize ? style.fontSize : "1em", │ │ │ │ - style.fontFamily ? style.fontFamily : "sans-serif" │ │ │ │ - ].join(" "); │ │ │ │ - var labelRows = style.label.split('\n'); │ │ │ │ - var numRows = labelRows.length; │ │ │ │ - if (this.canvas.fillText) { │ │ │ │ - // HTML5 │ │ │ │ - this.canvas.font = fontStyle; │ │ │ │ - this.canvas.textAlign = │ │ │ │ - OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] || │ │ │ │ - "center"; │ │ │ │ - this.canvas.textBaseline = │ │ │ │ - OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] || │ │ │ │ - "middle"; │ │ │ │ - var vfactor = │ │ │ │ - OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]]; │ │ │ │ - if (vfactor == null) { │ │ │ │ - vfactor = -.5; │ │ │ │ - } │ │ │ │ - var lineHeight = │ │ │ │ - this.canvas.measureText('Mg').height || │ │ │ │ - this.canvas.measureText('xx').width; │ │ │ │ - pt[1] += lineHeight * vfactor * (numRows - 1); │ │ │ │ - for (var i = 0; i < numRows; i++) { │ │ │ │ - if (style.labelOutlineWidth) { │ │ │ │ - this.canvas.save(); │ │ │ │ - this.canvas.globalAlpha = style.labelOutlineOpacity || style.fontOpacity || 1.0; │ │ │ │ - this.canvas.strokeStyle = style.labelOutlineColor; │ │ │ │ - this.canvas.lineWidth = style.labelOutlineWidth; │ │ │ │ - this.canvas.strokeText(labelRows[i], pt[0], pt[1] + (lineHeight * i) + 1); │ │ │ │ - this.canvas.restore(); │ │ │ │ - } │ │ │ │ - this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight * i)); │ │ │ │ - } │ │ │ │ - } else if (this.canvas.mozDrawText) { │ │ │ │ - // Mozilla pre-Gecko1.9.1 (<FF3.1) │ │ │ │ - this.canvas.mozTextStyle = fontStyle; │ │ │ │ - // No built-in text alignment, so we measure and adjust the position │ │ │ │ - var hfactor = │ │ │ │ - OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]]; │ │ │ │ - if (hfactor == null) { │ │ │ │ - hfactor = -.5; │ │ │ │ - } │ │ │ │ - var vfactor = │ │ │ │ - OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]]; │ │ │ │ - if (vfactor == null) { │ │ │ │ - vfactor = -.5; │ │ │ │ + if (this.useFeedTitle) { │ │ │ │ + var name = null; │ │ │ │ + try { │ │ │ │ + name = doc.getElementsByTagNameNS('*', 'title')[0].firstChild.nodeValue; │ │ │ │ + } catch (e) { │ │ │ │ + try { │ │ │ │ + name = doc.getElementsByTagName('title')[0].firstChild.nodeValue; │ │ │ │ + } catch (e) {} │ │ │ │ } │ │ │ │ - var lineHeight = this.canvas.mozMeasureText('xx'); │ │ │ │ - pt[1] += lineHeight * (1 + (vfactor * numRows)); │ │ │ │ - for (var i = 0; i < numRows; i++) { │ │ │ │ - var x = pt[0] + (hfactor * this.canvas.mozMeasureText(labelRows[i])); │ │ │ │ - var y = pt[1] + (i * lineHeight); │ │ │ │ - this.canvas.translate(x, y); │ │ │ │ - this.canvas.mozDrawText(labelRows[i]); │ │ │ │ - this.canvas.translate(-x, -y); │ │ │ │ + if (name) { │ │ │ │ + this.setName(name); │ │ │ │ } │ │ │ │ } │ │ │ │ - this.setCanvasStyle("reset"); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getLocalXY │ │ │ │ - * transform geographic xy into pixel xy │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} │ │ │ │ - */ │ │ │ │ - getLocalXY: function(point) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var extent = this.extent; │ │ │ │ - var x = ((point.x - this.featureDx) / resolution + (-extent.left / resolution)); │ │ │ │ - var y = ((extent.top / resolution) - point.y / resolution); │ │ │ │ - return [x, y]; │ │ │ │ - }, │ │ │ │ + var options = {}; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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); │ │ │ │ + OpenLayers.Util.extend(options, this.formatOptions); │ │ │ │ + │ │ │ │ + if (this.map && !this.projection.equals(this.map.getProjectionObject())) { │ │ │ │ + options.externalProjection = this.projection; │ │ │ │ + options.internalProjection = this.map.getProjectionObject(); │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getFeatureIdFromEvent │ │ │ │ - * Returns a feature id from an event on the renderer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector} A feature or undefined. This method returns a │ │ │ │ - * feature instead of a feature id to avoid an unnecessary lookup on the │ │ │ │ - * layer. │ │ │ │ - */ │ │ │ │ - getFeatureIdFromEvent: function(evt) { │ │ │ │ - var featureId, feature; │ │ │ │ + var format = new OpenLayers.Format.GeoRSS(options); │ │ │ │ + var features = format.read(doc); │ │ │ │ │ │ │ │ - if (this.hitDetection && this.root.style.display !== "none") { │ │ │ │ - // this dragging check should go in the feature handler │ │ │ │ - if (!this.map.dragging) { │ │ │ │ - var xy = evt.xy; │ │ │ │ - var x = xy.x | 0; │ │ │ │ - var y = xy.y | 0; │ │ │ │ - var data = this.hitContext.getImageData(x, y, 1, 1).data; │ │ │ │ - if (data[3] === 255) { // antialiased │ │ │ │ - var id = data[2] + (256 * (data[1] + (256 * data[0]))); │ │ │ │ - if (id) { │ │ │ │ - featureId = "OpenLayers_Feature_Vector_" + (id - 1 + this.hitOverflow); │ │ │ │ - try { │ │ │ │ - feature = this.features[featureId][0]; │ │ │ │ - } catch (err) { │ │ │ │ - // Because of antialiasing on the canvas, when the hit location is at a point where the edge of │ │ │ │ - // one symbol intersects the interior of another symbol, a wrong hit color (and therefore id) results. │ │ │ │ - // todo: set Antialiasing = 'off' on the hitContext as soon as browsers allow it. │ │ │ │ - } │ │ │ │ - } │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ + var data = {}; │ │ │ │ + var feature = features[i]; │ │ │ │ + │ │ │ │ + // we don't support features with no geometry in the GeoRSS │ │ │ │ + // layer at this time. │ │ │ │ + if (!feature.geometry) { │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ + │ │ │ │ + var title = feature.attributes.title ? │ │ │ │ + feature.attributes.title : "Untitled"; │ │ │ │ + │ │ │ │ + var description = feature.attributes.description ? │ │ │ │ + feature.attributes.description : "No description."; │ │ │ │ + │ │ │ │ + var link = feature.attributes.link ? feature.attributes.link : ""; │ │ │ │ + │ │ │ │ + var location = feature.geometry.getBounds().getCenterLonLat(); │ │ │ │ + │ │ │ │ + │ │ │ │ + data.icon = this.icon == null ? │ │ │ │ + OpenLayers.Marker.defaultIcon() : │ │ │ │ + this.icon.clone(); │ │ │ │ + │ │ │ │ + data.popupSize = this.popupSize ? │ │ │ │ + this.popupSize.clone() : │ │ │ │ + new OpenLayers.Size(250, 120); │ │ │ │ + │ │ │ │ + if (title || description) { │ │ │ │ + // we have supplemental data, store them. │ │ │ │ + data.title = title; │ │ │ │ + data.description = description; │ │ │ │ + │ │ │ │ + var contentHTML = '<div class="olLayerGeoRSSClose">[x]</div>'; │ │ │ │ + contentHTML += '<div class="olLayerGeoRSSTitle">'; │ │ │ │ + if (link) { │ │ │ │ + contentHTML += '<a class="link" href="' + link + '" target="_blank">'; │ │ │ │ } │ │ │ │ + contentHTML += title; │ │ │ │ + if (link) { │ │ │ │ + contentHTML += '</a>'; │ │ │ │ + } │ │ │ │ + contentHTML += '</div>'; │ │ │ │ + contentHTML += '<div style="" class="olLayerGeoRSSDescription">'; │ │ │ │ + contentHTML += description; │ │ │ │ + contentHTML += '</div>'; │ │ │ │ + data['popupContentHTML'] = contentHTML; │ │ │ │ } │ │ │ │ + var feature = new OpenLayers.Feature(this, location, data); │ │ │ │ + this.features.push(feature); │ │ │ │ + var marker = feature.createMarker(); │ │ │ │ + marker.events.register('click', feature, this.markerClick); │ │ │ │ + this.addMarker(marker); │ │ │ │ } │ │ │ │ - return feature; │ │ │ │ + this.events.triggerEvent("loadend"); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: eraseFeatures │ │ │ │ - * This is called by the layer to erase features; removes the feature from │ │ │ │ - * the list, then redraws the layer. │ │ │ │ - * │ │ │ │ + * Method: markerClick │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - eraseFeatures: function(features) { │ │ │ │ - if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ - features = [features]; │ │ │ │ + markerClick: function(evt) { │ │ │ │ + var sameMarkerClicked = (this == this.layer.selectedFeature); │ │ │ │ + this.layer.selectedFeature = (!sameMarkerClicked) ? this : null; │ │ │ │ + for (var i = 0, len = this.layer.map.popups.length; i < len; i++) { │ │ │ │ + this.layer.map.removePopup(this.layer.map.popups[i]); │ │ │ │ } │ │ │ │ - for (var i = 0; i < features.length; ++i) { │ │ │ │ - delete this.features[features[i].id]; │ │ │ │ + if (!sameMarkerClicked) { │ │ │ │ + var popup = this.createPopup(); │ │ │ │ + OpenLayers.Event.observe(popup.div, "click", │ │ │ │ + OpenLayers.Function.bind(function() { │ │ │ │ + for (var i = 0, len = this.layer.map.popups.length; i < len; i++) { │ │ │ │ + this.layer.map.removePopup(this.layer.map.popups[i]); │ │ │ │ + } │ │ │ │ + }, this) │ │ │ │ + ); │ │ │ │ + this.layer.map.addPopup(popup); │ │ │ │ } │ │ │ │ - this.redraw(); │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: clearFeatures │ │ │ │ + * Destroy all features in this layer. │ │ │ │ */ │ │ │ │ - redraw: function() { │ │ │ │ - if (!this.locked) { │ │ │ │ - var height = this.root.height; │ │ │ │ - var width = this.root.width; │ │ │ │ - this.canvas.clearRect(0, 0, width, height); │ │ │ │ - if (this.hitDetection) { │ │ │ │ - this.hitContext.clearRect(0, 0, width, height); │ │ │ │ - } │ │ │ │ - var labelMap = []; │ │ │ │ - var feature, geometry, style; │ │ │ │ - var worldBounds = (this.map.baseLayer && this.map.baseLayer.wrapDateLine) && this.map.getMaxExtent(); │ │ │ │ - for (var id in this.features) { │ │ │ │ - if (!this.features.hasOwnProperty(id)) { │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ - feature = this.features[id][0]; │ │ │ │ - geometry = feature.geometry; │ │ │ │ - this.calculateFeatureDx(geometry.getBounds(), worldBounds); │ │ │ │ - style = this.features[id][1]; │ │ │ │ - this.drawGeometry(geometry, style, feature.id); │ │ │ │ - if (style.label) { │ │ │ │ - labelMap.push([feature, style]); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var item; │ │ │ │ - for (var i = 0, len = labelMap.length; i < len; ++i) { │ │ │ │ - item = labelMap[i]; │ │ │ │ - this.drawText(item[0].geometry.getCentroid(), item[1]); │ │ │ │ + clearFeatures: function() { │ │ │ │ + if (this.features != null) { │ │ │ │ + while (this.features.length > 0) { │ │ │ │ + var feature = this.features[0]; │ │ │ │ + OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ + feature.destroy(); │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer.Canvas" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.GeoRSS" │ │ │ │ }); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Renderer.Canvas.LABEL_ALIGN │ │ │ │ - * {Object} │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.Canvas.LABEL_ALIGN = { │ │ │ │ - "l": "left", │ │ │ │ - "r": "right", │ │ │ │ - "t": "top", │ │ │ │ - "b": "bottom" │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR │ │ │ │ - * {Object} │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.Canvas.LABEL_FACTOR = { │ │ │ │ - "l": 0, │ │ │ │ - "r": -1, │ │ │ │ - "t": 0, │ │ │ │ - "b": -1 │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Renderer.Canvas.drawImageScaleFactor │ │ │ │ - * {Number} Scale factor to apply to the canvas drawImage arguments. This │ │ │ │ - * is always 1 except for Android 2.1 devices, to work around │ │ │ │ - * http://code.google.com/p/android/issues/detail?id=5141. │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.Canvas.drawImageScaleFactor = null; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Renderer/VML.js │ │ │ │ + OpenLayers/Layer/PointGrid.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Renderer/Elements.js │ │ │ │ + * @requires OpenLayers/Layer/Vector.js │ │ │ │ + * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Renderer.VML │ │ │ │ - * Render vector features in browsers with VML capability. Construct a new │ │ │ │ - * VML renderer with the <OpenLayers.Renderer.VML> constructor. │ │ │ │ - * │ │ │ │ - * Note that for all calculations in this class, we use (num | 0) to truncate a │ │ │ │ - * float value to an integer. This is done because it seems that VML doesn't │ │ │ │ - * support float values. │ │ │ │ + * Class: OpenLayers.Layer.PointGrid │ │ │ │ + * A point grid layer dynamically generates a regularly spaced grid of point │ │ │ │ + * features. This is a specialty layer for cases where an application needs │ │ │ │ + * a regular grid of points. It can be used, for example, in an editing │ │ │ │ + * environment to snap to a grid. │ │ │ │ + * │ │ │ │ + * Create a new vector layer with the <OpenLayers.Layer.PointGrid> constructor. │ │ │ │ + * (code) │ │ │ │ + * // create a grid with points spaced at 10 map units │ │ │ │ + * var points = new OpenLayers.Layer.PointGrid({dx: 10, dy: 10}); │ │ │ │ + * │ │ │ │ + * // create a grid with different x/y spacing rotated 15 degrees clockwise. │ │ │ │ + * var points = new OpenLayers.Layer.PointGrid({dx: 5, dy: 10, rotation: 15}); │ │ │ │ + * (end) │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Renderer.Elements> │ │ │ │ + * - <OpenLayers.Layer.Vector> │ │ │ │ */ │ │ │ │ -OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ +OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: xmlns │ │ │ │ - * {String} XML Namespace URN │ │ │ │ + * APIProperty: dx │ │ │ │ + * {Number} Point grid spacing in the x-axis direction (map units). │ │ │ │ + * Read-only. Use the <setSpacing> method to modify this value. │ │ │ │ */ │ │ │ │ - xmlns: "urn:schemas-microsoft-com:vml", │ │ │ │ + dx: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIProperty: dy │ │ │ │ + * {Number} Point grid spacing in the y-axis direction (map units). │ │ │ │ + * Read-only. Use the <setSpacing> method to modify this value. │ │ │ │ */ │ │ │ │ - symbolCache: {}, │ │ │ │ + dy: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: offset │ │ │ │ - * {Object} Hash with "x" and "y" properties │ │ │ │ + * APIProperty: ratio │ │ │ │ + * {Number} Ratio of the desired grid size to the map viewport size. │ │ │ │ + * Default is 1.5. Larger ratios mean the grid is recalculated less often │ │ │ │ + * while panning. The <maxFeatures> setting has precedence when determining │ │ │ │ + * grid size. Read-only. Use the <setRatio> method to modify this value. │ │ │ │ */ │ │ │ │ - offset: null, │ │ │ │ + ratio: 1.5, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Renderer.VML │ │ │ │ - * Create a new VML renderer. │ │ │ │ + * APIProperty: maxFeatures │ │ │ │ + * {Number} The maximum number of points to generate in the grid. Default │ │ │ │ + * is 250. Read-only. Use the <setMaxFeatures> method to modify this value. │ │ │ │ + */ │ │ │ │ + maxFeatures: 250, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: rotation │ │ │ │ + * {Number} Grid rotation (in degrees clockwise from the positive x-axis). │ │ │ │ + * Default is 0. Read-only. Use the <setRotation> method to modify this │ │ │ │ + * value. │ │ │ │ + */ │ │ │ │ + rotation: 0, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: origin │ │ │ │ + * {<OpenLayers.LonLat>} Grid origin. The grid lattice will be aligned with │ │ │ │ + * the origin. If not set at construction, the center of the map's maximum │ │ │ │ + * extent is used. Read-only. Use the <setOrigin> method to modify this │ │ │ │ + * value. │ │ │ │ + */ │ │ │ │ + origin: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: gridBounds │ │ │ │ + * {<OpenLayers.Bounds>} Internally cached grid bounds (with optional │ │ │ │ + * rotation applied). │ │ │ │ + */ │ │ │ │ + gridBounds: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.PointGrid │ │ │ │ + * Creates a new point grid layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * containerID - {String} The id for the element that contains the renderer │ │ │ │ + * config - {Object} An object containing all configuration properties for │ │ │ │ + * the layer. The <dx> and <dy> properties are required to be set at │ │ │ │ + * construction. Any other layer properties may be set in this object. │ │ │ │ */ │ │ │ │ - initialize: function(containerID) { │ │ │ │ - if (!this.supported()) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (!document.namespaces.olv) { │ │ │ │ - document.namespaces.add("olv", this.xmlns); │ │ │ │ - var style = document.createStyleSheet(); │ │ │ │ - var shapes = ['shape', 'rect', 'oval', 'fill', 'stroke', 'imagedata', 'group', 'textbox']; │ │ │ │ - for (var i = 0, len = shapes.length; i < len; i++) { │ │ │ │ - │ │ │ │ - style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " + │ │ │ │ - "position: absolute; display: inline-block;"); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + initialize: function(config) { │ │ │ │ + config = config || {}; │ │ │ │ + OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config]); │ │ │ │ + }, │ │ │ │ │ │ │ │ - OpenLayers.Renderer.Elements.prototype.initialize.apply(this, │ │ │ │ - arguments); │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * The layer has been added to the map. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); │ │ │ │ + map.events.register("moveend", this, this.onMoveEnd); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: supported │ │ │ │ - * Determine whether a browser supports this renderer. │ │ │ │ + * Method: removeMap │ │ │ │ + * The layer has been removed from the map. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The browser supports the VML renderer │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - supported: function() { │ │ │ │ - return !!(document.namespaces); │ │ │ │ + removeMap: function(map) { │ │ │ │ + map.events.unregister("moveend", this, this.onMoveEnd); │ │ │ │ + OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setExtent │ │ │ │ - * Set the renderer's extent │ │ │ │ + * APIMethod: setRatio │ │ │ │ + * Set the grid <ratio> property and update the grid. Can only be called │ │ │ │ + * after the layer has been added to a map with a center/extent. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * extent - {<OpenLayers.Bounds>} │ │ │ │ - * resolutionChanged - {Boolean} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} true to notify the layer that the new extent does not exceed │ │ │ │ - * the coordinate range, and the features will not need to be redrawn. │ │ │ │ + * ratio - {Number} │ │ │ │ */ │ │ │ │ - setExtent: function(extent, resolutionChanged) { │ │ │ │ - var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - │ │ │ │ - var left = (extent.left / resolution) | 0; │ │ │ │ - var top = (extent.top / resolution - this.size.h) | 0; │ │ │ │ - if (resolutionChanged || !this.offset) { │ │ │ │ - this.offset = { │ │ │ │ - x: left, │ │ │ │ - y: top │ │ │ │ - }; │ │ │ │ - left = 0; │ │ │ │ - top = 0; │ │ │ │ - } else { │ │ │ │ - left = left - this.offset.x; │ │ │ │ - top = top - this.offset.y; │ │ │ │ - } │ │ │ │ - │ │ │ │ - │ │ │ │ - var org = (left - this.xOffset) + " " + top; │ │ │ │ - this.root.coordorigin = org; │ │ │ │ - var roots = [this.root, this.vectorRoot, this.textRoot]; │ │ │ │ - var root; │ │ │ │ - for (var i = 0, len = roots.length; i < len; ++i) { │ │ │ │ - root = roots[i]; │ │ │ │ - │ │ │ │ - var size = this.size.w + " " + this.size.h; │ │ │ │ - root.coordsize = size; │ │ │ │ - │ │ │ │ - } │ │ │ │ - // flip the VML display Y axis upside down so it │ │ │ │ - // matches the display Y axis of the map │ │ │ │ - this.root.style.flip = "y"; │ │ │ │ + setRatio: function(ratio) { │ │ │ │ + this.ratio = ratio; │ │ │ │ + this.updateGrid(true); │ │ │ │ + }, │ │ │ │ │ │ │ │ - return coordSysUnchanged; │ │ │ │ + /** │ │ │ │ + * APIMethod: setMaxFeatures │ │ │ │ + * Set the grid <maxFeatures> property and update the grid. Can only be │ │ │ │ + * called after the layer has been added to a map with a center/extent. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * maxFeatures - {Number} │ │ │ │ + */ │ │ │ │ + setMaxFeatures: function(maxFeatures) { │ │ │ │ + this.maxFeatures = maxFeatures; │ │ │ │ + this.updateGrid(true); │ │ │ │ }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * APIMethod: setSpacing │ │ │ │ + * Set the grid <dx> and <dy> properties and update the grid. If only one │ │ │ │ + * argument is provided, it will be set as <dx> and <dy>. Can only be │ │ │ │ + * called after the layer has been added to a map with a center/extent. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * dx - {Number} │ │ │ │ + * dy - {Number} │ │ │ │ + */ │ │ │ │ + setSpacing: function(dx, dy) { │ │ │ │ + this.dx = dx; │ │ │ │ + this.dy = dy || dx; │ │ │ │ + this.updateGrid(true); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setSize │ │ │ │ - * Set the size of the drawing surface │ │ │ │ + * APIMethod: setOrigin │ │ │ │ + * Set the grid <origin> property and update the grid. Can only be called │ │ │ │ + * after the layer has been added to a map with a center/extent. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * size - {<OpenLayers.Size>} the size of the drawing surface │ │ │ │ + * origin - {<OpenLayers.LonLat>} │ │ │ │ */ │ │ │ │ - setSize: function(size) { │ │ │ │ - OpenLayers.Renderer.prototype.setSize.apply(this, arguments); │ │ │ │ + setOrigin: function(origin) { │ │ │ │ + this.origin = origin; │ │ │ │ + this.updateGrid(true); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // 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; │ │ │ │ + /** │ │ │ │ + * APIMethod: getOrigin │ │ │ │ + * Get the grid <origin> property. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.LonLat>} The grid origin. │ │ │ │ + */ │ │ │ │ + getOrigin: function() { │ │ │ │ + if (!this.origin) { │ │ │ │ + this.origin = this.map.getExtent().getCenterLonLat(); │ │ │ │ } │ │ │ │ + return this.origin; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getNodeType │ │ │ │ - * Get the node type for a geometry and style │ │ │ │ + * APIMethod: setRotation │ │ │ │ + * Set the grid <rotation> property and update the grid. Rotation values │ │ │ │ + * are in degrees clockwise from the positive x-axis (negative values │ │ │ │ + * for counter-clockwise rotation). Can only be called after the layer │ │ │ │ + * has been added to a map with a center/extent. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ + * rotation - {Number} Degrees clockwise from the positive x-axis. │ │ │ │ + */ │ │ │ │ + setRotation: function(rotation) { │ │ │ │ + this.rotation = rotation; │ │ │ │ + this.updateGrid(true); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: onMoveEnd │ │ │ │ + * Listener for map "moveend" events. │ │ │ │ + */ │ │ │ │ + onMoveEnd: function() { │ │ │ │ + this.updateGrid(); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getViewBounds │ │ │ │ + * Gets the (potentially rotated) view bounds for grid calculations. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} The corresponding node type for the specified geometry │ │ │ │ + * {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + getViewBounds: function() { │ │ │ │ + var bounds = this.map.getExtent(); │ │ │ │ + if (this.rotation) { │ │ │ │ + var origin = this.getOrigin(); │ │ │ │ + var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat); │ │ │ │ + var rect = bounds.toGeometry(); │ │ │ │ + rect.rotate(-this.rotation, rotationOrigin); │ │ │ │ + bounds = rect.getBounds(); │ │ │ │ } │ │ │ │ - return nodeType; │ │ │ │ + return bounds; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setStyle │ │ │ │ - * Use to set all the style attributes to a VML node. │ │ │ │ + * Method: updateGrid │ │ │ │ + * Update the grid. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} An VML element to decorate │ │ │ │ - * style - {Object} │ │ │ │ - * options - {Object} Currently supported options include │ │ │ │ - * 'isFilled' {Boolean} and │ │ │ │ - * 'isStroked' {Boolean} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * force - {Boolean} Update the grid even if the previous bounds are still │ │ │ │ + * valid. │ │ │ │ */ │ │ │ │ - setStyle: function(node, style, options, geometry) { │ │ │ │ - style = style || node._style; │ │ │ │ - options = options || node._options; │ │ │ │ - var fillColor = style.fillColor; │ │ │ │ - │ │ │ │ - var title = style.title || style.graphicTitle; │ │ │ │ - if (title) { │ │ │ │ - node.title = title; │ │ │ │ + updateGrid: function(force) { │ │ │ │ + if (force || this.invalidBounds()) { │ │ │ │ + var viewBounds = this.getViewBounds(); │ │ │ │ + var origin = this.getOrigin(); │ │ │ │ + var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat); │ │ │ │ + var viewBoundsWidth = viewBounds.getWidth(); │ │ │ │ + var viewBoundsHeight = viewBounds.getHeight(); │ │ │ │ + var aspectRatio = viewBoundsWidth / viewBoundsHeight; │ │ │ │ + var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio); │ │ │ │ + var maxWidth = maxHeight * aspectRatio; │ │ │ │ + var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth); │ │ │ │ + var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight); │ │ │ │ + var center = viewBounds.getCenterLonLat(); │ │ │ │ + this.gridBounds = new OpenLayers.Bounds( │ │ │ │ + center.lon - (gridWidth / 2), │ │ │ │ + center.lat - (gridHeight / 2), │ │ │ │ + center.lon + (gridWidth / 2), │ │ │ │ + center.lat + (gridHeight / 2) │ │ │ │ + ); │ │ │ │ + var rows = Math.floor(gridHeight / this.dy); │ │ │ │ + var cols = Math.floor(gridWidth / this.dx); │ │ │ │ + var gridLeft = origin.lon + (this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx)); │ │ │ │ + var gridBottom = origin.lat + (this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy)); │ │ │ │ + var features = new Array(rows * cols); │ │ │ │ + var x, y, point; │ │ │ │ + for (var i = 0; i < cols; ++i) { │ │ │ │ + x = gridLeft + (i * this.dx); │ │ │ │ + for (var j = 0; j < rows; ++j) { │ │ │ │ + y = gridBottom + (j * this.dy); │ │ │ │ + point = new OpenLayers.Geometry.Point(x, y); │ │ │ │ + if (this.rotation) { │ │ │ │ + point.rotate(this.rotation, rotationOrigin); │ │ │ │ + } │ │ │ │ + features[(i * rows) + j] = new OpenLayers.Feature.Vector(point); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.destroyFeatures(this.features, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.addFeatures(features, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ - if (style.externalGraphic) { │ │ │ │ - options.isFilled = true; │ │ │ │ - var width = style.graphicWidth || style.graphicHeight; │ │ │ │ - var height = style.graphicHeight || style.graphicWidth; │ │ │ │ - width = width ? width : style.pointRadius * 2; │ │ │ │ - height = height ? height : style.pointRadius * 2; │ │ │ │ - │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var xOffset = (style.graphicXOffset != undefined) ? │ │ │ │ - style.graphicXOffset : -(0.5 * width); │ │ │ │ - var yOffset = (style.graphicYOffset != undefined) ? │ │ │ │ - style.graphicYOffset : -(0.5 * height); │ │ │ │ - │ │ │ │ - node.style.left = ((((geometry.x - this.featureDx) / resolution - this.offset.x) + xOffset) | 0) + "px"; │ │ │ │ - node.style.top = (((geometry.y / resolution - this.offset.y) - (yOffset + height)) | 0) + "px"; │ │ │ │ - node.style.width = width + "px"; │ │ │ │ - node.style.height = height + "px"; │ │ │ │ - node.style.flip = "y"; │ │ │ │ + /** │ │ │ │ + * Method: invalidBounds │ │ │ │ + * Determine whether the previously generated point grid is invalid. │ │ │ │ + * This occurs when the map bounds extends beyond the previously │ │ │ │ + * generated grid bounds. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} │ │ │ │ + */ │ │ │ │ + invalidBounds: function() { │ │ │ │ + return !this.gridBounds || !this.gridBounds.containsBounds(this.getViewBounds()); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // 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); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.PointGrid" │ │ │ │ │ │ │ │ - // 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; │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/Boxes.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - if (node._geometryClass === "OpenLayers.Geometry.Point" && │ │ │ │ - style.externalGraphic) { │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - // override fillOpacity │ │ │ │ - if (style.graphicOpacity) { │ │ │ │ - fill.opacity = style.graphicOpacity; │ │ │ │ - } │ │ │ │ │ │ │ │ - fill.src = style.externalGraphic; │ │ │ │ - fill.type = "frame"; │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ + * @requires OpenLayers/Layer/Markers.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ - fill.aspect = "atmost"; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (fill.parentNode != node) { │ │ │ │ - node.appendChild(fill); │ │ │ │ - } │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.Boxes │ │ │ │ + * Draw divs as 'boxes' on the layer. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Markers> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, { │ │ │ │ │ │ │ │ - // 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; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Boxes │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + */ │ │ │ │ │ │ │ │ - // 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; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: drawMarker │ │ │ │ + * Calculate the pixel location for the marker, create it, and │ │ │ │ + * add it to the layer's div │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * marker - {<OpenLayers.Marker.Box>} │ │ │ │ + */ │ │ │ │ + drawMarker: function(marker) { │ │ │ │ + var topleft = this.map.getLayerPxFromLonLat({ │ │ │ │ + lon: marker.bounds.left, │ │ │ │ + lat: marker.bounds.top │ │ │ │ + }); │ │ │ │ + var botright = this.map.getLayerPxFromLonLat({ │ │ │ │ + lon: marker.bounds.right, │ │ │ │ + lat: marker.bounds.bottom │ │ │ │ + }); │ │ │ │ + if (botright == null || topleft == null) { │ │ │ │ + marker.display(false); │ │ │ │ } else { │ │ │ │ - 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); │ │ │ │ + var markerDiv = marker.draw(topleft, { │ │ │ │ + w: Math.max(1, botright.x - topleft.x), │ │ │ │ + h: Math.max(1, botright.y - topleft.y) │ │ │ │ + }); │ │ │ │ + if (!marker.drawn) { │ │ │ │ + this.div.appendChild(markerDiv); │ │ │ │ + marker.drawn = true; │ │ │ │ } │ │ │ │ } │ │ │ │ - │ │ │ │ - if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ - node.style.cursor = style.cursor; │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: graphicRotate │ │ │ │ - * If a point is to be styled with externalGraphic and rotation, VML fills │ │ │ │ - * cannot be used to display the graphic, because rotation of graphic │ │ │ │ - * fills is not supported by the VML implementation of Internet Explorer. │ │ │ │ - * This method creates a olv:imagedata element inside the VML node, │ │ │ │ - * DXImageTransform.Matrix and BasicImage filters for rotation and │ │ │ │ - * opacity, and a 3-step hack to remove rendering artefacts from the │ │ │ │ - * graphic and preserve the ability of graphics to trigger events. │ │ │ │ - * Finally, OpenLayers methods are used to determine the correct │ │ │ │ - * insertion point of the rotated image, because DXImageTransform.Matrix │ │ │ │ - * does the rotation without the ability to specify a rotation center │ │ │ │ - * point. │ │ │ │ + * APIMethod: removeMarker │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * xOffset - {Number} rotation center relative to image, x coordinate │ │ │ │ - * yOffset - {Number} rotation center relative to image, y coordinate │ │ │ │ - * style - {Object} │ │ │ │ + * marker - {<OpenLayers.Marker.Box>} │ │ │ │ */ │ │ │ │ - graphicRotate: function(node, xOffset, yOffset, style) { │ │ │ │ - var style = style || node._style; │ │ │ │ - var rotation = style.rotation || 0; │ │ │ │ - │ │ │ │ - var aspectRatio, size; │ │ │ │ - if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ - // load the image to determine its size │ │ │ │ - var img = new Image(); │ │ │ │ - img.onreadystatechange = OpenLayers.Function.bind(function() { │ │ │ │ - if (img.readyState == "complete" || │ │ │ │ - img.readyState == "interactive") { │ │ │ │ - aspectRatio = img.width / img.height; │ │ │ │ - size = Math.max(style.pointRadius * 2, │ │ │ │ - style.graphicWidth || 0, │ │ │ │ - style.graphicHeight || 0); │ │ │ │ - xOffset = xOffset * aspectRatio; │ │ │ │ - style.graphicWidth = size * aspectRatio; │ │ │ │ - style.graphicHeight = size; │ │ │ │ - this.graphicRotate(node, xOffset, yOffset, style); │ │ │ │ - } │ │ │ │ - }, this); │ │ │ │ - img.src = style.externalGraphic; │ │ │ │ - │ │ │ │ - // will be called again by the onreadystate handler │ │ │ │ - return; │ │ │ │ - } else { │ │ │ │ - size = Math.max(style.graphicWidth, style.graphicHeight); │ │ │ │ - aspectRatio = style.graphicWidth / style.graphicHeight; │ │ │ │ + removeMarker: function(marker) { │ │ │ │ + OpenLayers.Util.removeItem(this.markers, marker); │ │ │ │ + if ((marker.div != null) && │ │ │ │ + (marker.div.parentNode == this.div)) { │ │ │ │ + this.div.removeChild(marker.div); │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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')"; │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Boxes" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/UTFGrid.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - var rot = rotation * Math.PI / 180; │ │ │ │ - var sintheta = Math.sin(rot); │ │ │ │ - var costheta = Math.cos(rot); │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - // do the rotation on the image │ │ │ │ - var filter = │ │ │ │ - "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta + │ │ │ │ - ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta + │ │ │ │ - ",SizingMethod='auto expand')\n"; │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/XYZ.js │ │ │ │ + * @requires OpenLayers/Tile/UTFGrid.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - // 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; │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.UTFGrid │ │ │ │ + * This Layer reads from UTFGrid tiled data sources. Since UTFGrids are │ │ │ │ + * essentially JSON-based ASCII art with attached attributes, they are not │ │ │ │ + * visibly rendered. In order to use them in the map, you must add a │ │ │ │ + * <OpenLayers.Control.UTFGrid> control as well. │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * │ │ │ │ + * (start code) │ │ │ │ + * var world_utfgrid = new OpenLayers.Layer.UTFGrid({ │ │ │ │ + * url: "/tiles/world_utfgrid/${z}/${x}/${y}.json", │ │ │ │ + * utfgridResolution: 4, │ │ │ │ + * displayInLayerSwitcher: false │ │ │ │ + * ); │ │ │ │ + * map.addLayer(world_utfgrid); │ │ │ │ + * │ │ │ │ + * var control = new OpenLayers.Control.UTFGrid({ │ │ │ │ + * layers: [world_utfgrid], │ │ │ │ + * handlerMode: 'move', │ │ │ │ + * callback: function(dataLookup) { │ │ │ │ + * // do something with returned data │ │ │ │ + * } │ │ │ │ + * }) │ │ │ │ + * (end code) │ │ │ │ + * │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.XYZ> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.UTFGrid = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ │ │ │ │ - // 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(); │ │ │ │ + /** │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * Default is false, as UTFGrids are designed to be a transparent overlay layer. │ │ │ │ + */ │ │ │ │ + isBaseLayer: false, │ │ │ │ │ │ │ │ - node.style.left = Math.round( │ │ │ │ - parseInt(node.style.left) + imgBounds.left) + "px"; │ │ │ │ - node.style.top = Math.round( │ │ │ │ - parseInt(node.style.top) - imgBounds.bottom) + "px"; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: projection │ │ │ │ + * {<OpenLayers.Projection>} │ │ │ │ + * Source projection for the UTFGrids. Default is "EPSG:900913". │ │ │ │ + */ │ │ │ │ + projection: new OpenLayers.Projection("EPSG:900913"), │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Property: useJSONP │ │ │ │ + * {Boolean} │ │ │ │ + * Should we use a JSONP script approach instead of a standard AJAX call? │ │ │ │ + * │ │ │ │ + * Set to true for using utfgrids from another server. │ │ │ │ + * Avoids same-domain policy restrictions. │ │ │ │ + * Note that this only works if the server accepts │ │ │ │ + * the callback GET parameter and dynamically │ │ │ │ + * wraps the returned json in a function call. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ + * Default is false │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ + useJSONP: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setNodeDimension │ │ │ │ - * Get the geometry's bounds, convert it to our vml coordinate system, │ │ │ │ - * then set the node's position, size, and local coordinate system. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * APIProperty: url │ │ │ │ + * {String} │ │ │ │ + * URL tempate for UTFGrid tiles. Include x, y, and z parameters. │ │ │ │ + * E.g. "/tiles/${z}/${x}/${y}.json" │ │ │ │ */ │ │ │ │ - setNodeDimension: function(node, geometry) { │ │ │ │ - │ │ │ │ - var bbox = geometry.getBounds(); │ │ │ │ - if (bbox) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - │ │ │ │ - var scaledBox = │ │ │ │ - new OpenLayers.Bounds(((bbox.left - this.featureDx) / resolution - this.offset.x) | 0, │ │ │ │ - (bbox.bottom / resolution - this.offset.y) | 0, │ │ │ │ - ((bbox.right - this.featureDx) / resolution - this.offset.x) | 0, │ │ │ │ - (bbox.top / resolution - this.offset.y) | 0); │ │ │ │ - │ │ │ │ - // Set the internal coordinate system to draw the path │ │ │ │ - node.style.left = scaledBox.left + "px"; │ │ │ │ - node.style.top = scaledBox.top + "px"; │ │ │ │ - node.style.width = scaledBox.getWidth() + "px"; │ │ │ │ - node.style.height = scaledBox.getHeight() + "px"; │ │ │ │ │ │ │ │ - node.coordorigin = scaledBox.left + " " + scaledBox.top; │ │ │ │ - node.coordsize = scaledBox.getWidth() + " " + scaledBox.getHeight(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: utfgridResolution │ │ │ │ + * {Number} │ │ │ │ + * Ratio of the pixel width to the width of a UTFGrid data point. If an │ │ │ │ + * entry in the grid represents a 4x4 block of pixels, the │ │ │ │ + * utfgridResolution would be 4. Default is 2 (specified in │ │ │ │ + * <OpenLayers.Tile.UTFGrid>). │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: dashStyle │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * style - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A VML compliant 'stroke-dasharray' value │ │ │ │ + /** │ │ │ │ + * Property: tileClass │ │ │ │ + * {<OpenLayers.Tile>} The tile class to use for this layer. │ │ │ │ + * Defaults is <OpenLayers.Tile.UTFGrid>. │ │ │ │ */ │ │ │ │ - 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"; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + tileClass: OpenLayers.Tile.UTFGrid, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createNode │ │ │ │ - * Create a new node │ │ │ │ + * Constructor: OpenLayers.Layer.UTFGrid │ │ │ │ + * Create a new UTFGrid layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * type - {String} Kind of node to draw │ │ │ │ - * id - {String} Id for node │ │ │ │ + * config - {Object} Configuration properties for the layer. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A new node of the given type and id │ │ │ │ + * Required configuration properties: │ │ │ │ + * url - {String} The url template for UTFGrid tiles. See the <url> property. │ │ │ │ */ │ │ │ │ - createNode: function(type, id) { │ │ │ │ - var node = document.createElement(type); │ │ │ │ - if (id) { │ │ │ │ - node.id = id; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // IE hack to make elements unselectable, to prevent 'blue flash' │ │ │ │ - // while dragging vectors; #1410 │ │ │ │ - node.unselectable = 'on'; │ │ │ │ - node.onselectstart = OpenLayers.Function.False; │ │ │ │ - │ │ │ │ - return node; │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply( │ │ │ │ + this, [options.name, options.url, {}, options] │ │ │ │ + ); │ │ │ │ + this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ + utfgridResolution: this.utfgridResolution │ │ │ │ + }, this.tileOptions); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: nodeTypeCompare │ │ │ │ - * Determine whether a node is of a given type │ │ │ │ + * Method: createBackBuffer │ │ │ │ + * The UTFGrid cannot create a back buffer, so this method is overriden. │ │ │ │ + */ │ │ │ │ + createBackBuffer: function() {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} An VML element │ │ │ │ - * type - {String} Kind of node │ │ │ │ - * │ │ │ │ + * obj - {Object} Only used by a subclass of this layer. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ + * {<OpenLayers.Layer.UTFGrid>} An exact clone of this OpenLayers.Layer.UTFGrid │ │ │ │ */ │ │ │ │ - nodeTypeCompare: function(node, type) { │ │ │ │ - │ │ │ │ - //split type │ │ │ │ - var subType = type; │ │ │ │ - var splitIndex = subType.indexOf(":"); │ │ │ │ - if (splitIndex != -1) { │ │ │ │ - subType = subType.substr(splitIndex + 1); │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.UTFGrid(this.getOptions()); │ │ │ │ } │ │ │ │ │ │ │ │ - //split nodeName │ │ │ │ - var nodeName = node.nodeName; │ │ │ │ - splitIndex = nodeName.indexOf(":"); │ │ │ │ - if (splitIndex != -1) { │ │ │ │ - nodeName = nodeName.substr(splitIndex + 1); │ │ │ │ - } │ │ │ │ + // get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ │ │ │ │ - return (subType == nodeName); │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createRenderRoot │ │ │ │ - * Create the renderer root │ │ │ │ + * APIProperty: getFeatureInfo │ │ │ │ + * Get details about a feature associated with a map location. The object │ │ │ │ + * returned will have id and data properties. If the given location │ │ │ │ + * doesn't correspond to a feature, null will be returned. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} The specific render engine's root element │ │ │ │ - */ │ │ │ │ - createRenderRoot: function() { │ │ │ │ - return this.nodeFactory(this.container.id + "_vmlRoot", "div"); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: createRoot │ │ │ │ - * Create the main root element │ │ │ │ - * │ │ │ │ * Parameters: │ │ │ │ - * suffix - {String} suffix to append to the id │ │ │ │ + * location - {<OpenLayers.LonLat>} map location │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * {Object} Object representing the feature id and UTFGrid data │ │ │ │ + * corresponding to the given map location. Returns null if the given │ │ │ │ + * location doesn't hit a feature. │ │ │ │ */ │ │ │ │ - createRoot: function(suffix) { │ │ │ │ - return this.nodeFactory(this.container.id + suffix, "olv:group"); │ │ │ │ + getFeatureInfo: function(location) { │ │ │ │ + var info = null; │ │ │ │ + var tileInfo = this.getTileData(location); │ │ │ │ + if (tileInfo && tileInfo.tile) { │ │ │ │ + info = tileInfo.tile.getFeatureInfo(tileInfo.i, tileInfo.j); │ │ │ │ + } │ │ │ │ + return info; │ │ │ │ }, │ │ │ │ │ │ │ │ - /************************************** │ │ │ │ - * * │ │ │ │ - * GEOMETRY DRAWING FUNCTIONS * │ │ │ │ - * * │ │ │ │ - **************************************/ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: drawPoint │ │ │ │ - * Render a point │ │ │ │ - * │ │ │ │ + * APIMethod: getFeatureId │ │ │ │ + * Get the identifier for the feature associated with a map location. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ + * location - {<OpenLayers.LonLat>} map location │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} or false if the point could not be drawn │ │ │ │ + * {String} The feature identifier corresponding to the given map location. │ │ │ │ + * Returns null if the location doesn't hit a feature. │ │ │ │ */ │ │ │ │ - drawPoint: function(node, geometry) { │ │ │ │ - return this.drawCircle(node, geometry, 1); │ │ │ │ + getFeatureId: function(location) { │ │ │ │ + var id = null; │ │ │ │ + var info = this.getTileData(location); │ │ │ │ + if (info.tile) { │ │ │ │ + id = info.tile.getFeatureId(info.i, info.j); │ │ │ │ + } │ │ │ │ + return id; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawCircle │ │ │ │ - * Render a circle. │ │ │ │ - * Size and Center a circle given geometry (x,y center) and radius │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * radius - {float} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or false if the circle could not ne drawn │ │ │ │ - */ │ │ │ │ - drawCircle: function(node, geometry, radius) { │ │ │ │ - if (!isNaN(geometry.x) && !isNaN(geometry.y)) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.UTFGrid" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/WMS.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - 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"; │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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 diameter = radius * 2; │ │ │ │ │ │ │ │ - node.style.width = diameter + "px"; │ │ │ │ - node.style.height = diameter + "px"; │ │ │ │ - return node; │ │ │ │ - } │ │ │ │ - return false; │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + */ │ │ │ │ │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.WMS │ │ │ │ + * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web │ │ │ │ + * Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS> │ │ │ │ + * constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawLineString │ │ │ │ - * Render a linestring. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * Constant: DEFAULT_PARAMS │ │ │ │ + * {Object} Hashtable of default parameter key/value pairs │ │ │ │ */ │ │ │ │ - drawLineString: function(node, geometry) { │ │ │ │ - return this.drawLine(node, geometry, false); │ │ │ │ + DEFAULT_PARAMS: { │ │ │ │ + service: "WMS", │ │ │ │ + version: "1.1.1", │ │ │ │ + request: "GetMap", │ │ │ │ + styles: "", │ │ │ │ + format: "image/jpeg" │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawLinearRing │ │ │ │ - * Render a linearring │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} Default is true for WMS layer │ │ │ │ */ │ │ │ │ - drawLinearRing: function(node, geometry) { │ │ │ │ - return this.drawLine(node, geometry, true); │ │ │ │ - }, │ │ │ │ + isBaseLayer: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: DrawLine │ │ │ │ - * Render a line. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * closeLine - {Boolean} Close the line? (make it a ring?) │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * APIProperty: encodeBBOX │ │ │ │ + * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', │ │ │ │ + * but some services want it that way. Default false. │ │ │ │ */ │ │ │ │ - drawLine: function(node, geometry, closeLine) { │ │ │ │ - │ │ │ │ - this.setNodeDimension(node, geometry); │ │ │ │ + encodeBBOX: false, │ │ │ │ │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var numComponents = geometry.components.length; │ │ │ │ - var parts = new Array(numComponents); │ │ │ │ + /** │ │ │ │ + * APIProperty: noMagic │ │ │ │ + * {Boolean} If true, the image format will not be automagicaly switched │ │ │ │ + * from image/jpeg to image/png or image/gif when using │ │ │ │ + * TRANSPARENT=TRUE. Also isBaseLayer will not changed by the │ │ │ │ + * constructor. Default false. │ │ │ │ + */ │ │ │ │ + noMagic: false, │ │ │ │ │ │ │ │ - var comp, x, y; │ │ │ │ - for (var i = 0; i < numComponents; i++) { │ │ │ │ - comp = geometry.components[i]; │ │ │ │ - x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0; │ │ │ │ - y = (comp.y / resolution - this.offset.y) | 0; │ │ │ │ - parts[i] = " " + x + "," + y + " l "; │ │ │ │ - } │ │ │ │ - var end = (closeLine) ? " x e" : " e"; │ │ │ │ - node.path = "m" + parts.join("") + end; │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: yx │ │ │ │ + * {Object} Keys in this object are EPSG codes for which the axis order │ │ │ │ + * is to be reversed (yx instead of xy, LatLon instead of LonLat), with │ │ │ │ + * true as value. This is only relevant for WMS versions >= 1.3.0, and │ │ │ │ + * only if yx is not set in <OpenLayers.Projection.defaults> for the │ │ │ │ + * used projection. │ │ │ │ + */ │ │ │ │ + yx: {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawPolygon │ │ │ │ - * Render a polygon │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Layer.WMS │ │ │ │ + * Create a new WMS layer object │ │ │ │ + * │ │ │ │ + * Examples: │ │ │ │ + * │ │ │ │ + * The code below creates a simple WMS layer using the image/jpeg format. │ │ │ │ + * (code) │ │ │ │ + * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic", │ │ │ │ + * "http://wms.jpl.nasa.gov/wms.cgi", │ │ │ │ + * {layers: "modis,global_mosaic"}); │ │ │ │ + * (end) │ │ │ │ + * Note the 3rd argument (params). Properties added to this object will be │ │ │ │ + * added to the WMS GetMap requests used for this layer's tiles. The only │ │ │ │ + * mandatory parameter is "layers". Other common WMS params include │ │ │ │ + * "transparent", "styles" and "format". Note that the "srs" param will │ │ │ │ + * always be ignored. Instead, it will be derived from the baseLayer's or │ │ │ │ + * map's projection. │ │ │ │ + * │ │ │ │ + * The code below creates a transparent WMS layer with additional options. │ │ │ │ + * (code) │ │ │ │ + * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic", │ │ │ │ + * "http://wms.jpl.nasa.gov/wms.cgi", │ │ │ │ + * { │ │ │ │ + * layers: "modis,global_mosaic", │ │ │ │ + * transparent: true │ │ │ │ + * }, { │ │ │ │ + * opacity: 0.5, │ │ │ │ + * singleTile: true │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ + * Note that by default, a WMS layer is configured as baseLayer. Setting │ │ │ │ + * the "transparent" param to true will apply some magic (see <noMagic>). │ │ │ │ + * The default image format changes from image/jpeg to image/png, and the │ │ │ │ + * layer is not configured as baseLayer. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * name - {String} A name for the layer │ │ │ │ + * url - {String} Base url for the WMS │ │ │ │ + * (e.g. http://wms.jpl.nasa.gov/wms.cgi) │ │ │ │ + * params - {Object} An object with key/value pairs representing the │ │ │ │ + * GetMap query string parameters and parameter values. │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer. │ │ │ │ + * These options include all properties listed above, plus the ones │ │ │ │ + * inherited from superclasses. │ │ │ │ */ │ │ │ │ - drawPolygon: function(node, geometry) { │ │ │ │ - this.setNodeDimension(node, geometry); │ │ │ │ + initialize: function(name, url, params, options) { │ │ │ │ + var newArguments = []; │ │ │ │ + //uppercase params │ │ │ │ + params = OpenLayers.Util.upperCaseObject(params); │ │ │ │ + if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) { │ │ │ │ + params.EXCEPTIONS = "INIMAGE"; │ │ │ │ + } │ │ │ │ + newArguments.push(name, url, params, options); │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ + OpenLayers.Util.applyDefaults( │ │ │ │ + this.params, │ │ │ │ + OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS) │ │ │ │ + ); │ │ │ │ │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ │ │ │ │ - var path = []; │ │ │ │ - var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y; │ │ │ │ - for (j = 0, jj = geometry.components.length; j < jj; j++) { │ │ │ │ - path.push("m"); │ │ │ │ - points = geometry.components[j].components; │ │ │ │ - // we only close paths of interior rings with area │ │ │ │ - area = (j === 0); │ │ │ │ - first = null; │ │ │ │ - second = null; │ │ │ │ - for (i = 0, ii = points.length; i < ii; i++) { │ │ │ │ - comp = points[i]; │ │ │ │ - x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0; │ │ │ │ - y = (comp.y / resolution - this.offset.y) | 0; │ │ │ │ - pathComp = " " + x + "," + y; │ │ │ │ - path.push(pathComp); │ │ │ │ - if (i == 0) { │ │ │ │ - path.push(" l"); │ │ │ │ - } │ │ │ │ - if (!area) { │ │ │ │ - // IE improperly renders sub-paths that have no area. │ │ │ │ - // Instead of checking the area of every ring, we confirm │ │ │ │ - // the ring has at least three distinct points. This does │ │ │ │ - // not catch all non-zero area cases, but it greatly improves │ │ │ │ - // interior ring digitizing and is a minor performance hit │ │ │ │ - // when rendering rings with many points. │ │ │ │ - if (!first) { │ │ │ │ - first = pathComp; │ │ │ │ - } else if (first != pathComp) { │ │ │ │ - if (!second) { │ │ │ │ - second = pathComp; │ │ │ │ - } else if (second != pathComp) { │ │ │ │ - // stop looking │ │ │ │ - area = true; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + //layer is transparent │ │ │ │ + if (!this.noMagic && this.params.TRANSPARENT && │ │ │ │ + this.params.TRANSPARENT.toString().toLowerCase() == "true") { │ │ │ │ + │ │ │ │ + // unless explicitly set in options, make layer an overlay │ │ │ │ + if ((options == null) || (!options.isBaseLayer)) { │ │ │ │ + this.isBaseLayer = false; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // jpegs can never be transparent, so intelligently switch the │ │ │ │ + // format, depending on the browser's capabilities │ │ │ │ + if (this.params.FORMAT == "image/jpeg") { │ │ │ │ + this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif" : │ │ │ │ + "image/png"; │ │ │ │ } │ │ │ │ - path.push(area ? " x " : " "); │ │ │ │ } │ │ │ │ - path.push("e"); │ │ │ │ - node.path = path.join(""); │ │ │ │ - return node; │ │ │ │ + │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawRectangle │ │ │ │ - * Render a rectangle │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ + * Method: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * {<OpenLayers.Layer.WMS>} An exact clone of this layer │ │ │ │ */ │ │ │ │ - drawRectangle: function(node, geometry) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ + clone: function(obj) { │ │ │ │ │ │ │ │ - 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"; │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.WMS(this.name, │ │ │ │ + this.url, │ │ │ │ + this.params, │ │ │ │ + this.getOptions()); │ │ │ │ + } │ │ │ │ │ │ │ │ - return node; │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawText │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ + * APIMethod: reverseAxisOrder │ │ │ │ + * Returns true if the axis order is reversed for the WMS version and │ │ │ │ + * projection of the layer. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * featureId - {String} │ │ │ │ - * style - │ │ │ │ - * location - {<OpenLayers.Geometry.Point>} │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true if the axis order is reversed, false otherwise. │ │ │ │ */ │ │ │ │ - drawText: function(featureId, style, location) { │ │ │ │ - var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect"); │ │ │ │ - var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox"); │ │ │ │ - │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - label.style.left = (((location.x - this.featureDx) / resolution - this.offset.x) | 0) + "px"; │ │ │ │ - label.style.top = ((location.y / resolution - this.offset.y) | 0) + "px"; │ │ │ │ - label.style.flip = "y"; │ │ │ │ - │ │ │ │ - textbox.innerText = style.label; │ │ │ │ - │ │ │ │ - if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ - textbox.style.cursor = style.cursor; │ │ │ │ - } │ │ │ │ - if (style.fontColor) { │ │ │ │ - textbox.style.color = style.fontColor; │ │ │ │ - } │ │ │ │ - if (style.fontOpacity) { │ │ │ │ - textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')'; │ │ │ │ - } │ │ │ │ - if (style.fontFamily) { │ │ │ │ - textbox.style.fontFamily = style.fontFamily; │ │ │ │ - } │ │ │ │ - if (style.fontSize) { │ │ │ │ - textbox.style.fontSize = style.fontSize; │ │ │ │ - } │ │ │ │ - if (style.fontWeight) { │ │ │ │ - textbox.style.fontWeight = style.fontWeight; │ │ │ │ - } │ │ │ │ - if (style.fontStyle) { │ │ │ │ - textbox.style.fontStyle = style.fontStyle; │ │ │ │ - } │ │ │ │ - if (style.labelSelect === true) { │ │ │ │ - label._featureId = featureId; │ │ │ │ - textbox._featureId = featureId; │ │ │ │ - textbox._geometry = location; │ │ │ │ - textbox._geometryClass = location.CLASS_NAME; │ │ │ │ - } │ │ │ │ - textbox.style.whiteSpace = "nowrap"; │ │ │ │ - // fun with IE: IE7 in standards compliant mode does not display any │ │ │ │ - // text with a left inset of 0. So we set this to 1px and subtract one │ │ │ │ - // pixel later when we set label.style.left │ │ │ │ - textbox.inset = "1px,0px,0px,0px"; │ │ │ │ - │ │ │ │ - if (!label.parentNode) { │ │ │ │ - label.appendChild(textbox); │ │ │ │ - this.textRoot.appendChild(label); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var align = style.labelAlign || "cm"; │ │ │ │ - if (align.length == 1) { │ │ │ │ - align += "m"; │ │ │ │ - } │ │ │ │ - var xshift = textbox.clientWidth * │ │ │ │ - (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0, 1)]); │ │ │ │ - var yshift = textbox.clientHeight * │ │ │ │ - (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1, 1)]); │ │ │ │ - label.style.left = parseInt(label.style.left) - xshift - 1 + "px"; │ │ │ │ - label.style.top = parseInt(label.style.top) + yshift + "px"; │ │ │ │ - │ │ │ │ + reverseAxisOrder: function() { │ │ │ │ + var projCode = this.projection.getCode(); │ │ │ │ + return parseFloat(this.params.VERSION) >= 1.3 && │ │ │ │ + !!(this.yx[projCode] || (OpenLayers.Projection.defaults[projCode] && │ │ │ │ + OpenLayers.Projection.defaults[projCode].yx)); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveRoot │ │ │ │ - * moves this renderer's root to a different renderer. │ │ │ │ - * │ │ │ │ + * Method: getURL │ │ │ │ + * Return a GetMap query string for this layer │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * renderer - {<OpenLayers.Renderer>} target renderer for the moved root │ │ │ │ - * root - {DOMElement} optional root node. To be used when this renderer │ │ │ │ - * holds roots from multiple layers to tell this method which one to │ │ │ │ - * detach │ │ │ │ - * │ │ │ │ + * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the │ │ │ │ + * request. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} true if successful, false otherwise │ │ │ │ + * {String} A string with the layer's url and parameters and also the │ │ │ │ + * passed-in bounds and appropriate tile size specified as │ │ │ │ + * parameters. │ │ │ │ */ │ │ │ │ - moveRoot: function(renderer) { │ │ │ │ - var layer = this.map.getLayer(renderer.container.id); │ │ │ │ - if (layer instanceof OpenLayers.Layer.Vector.RootContainer) { │ │ │ │ - layer = this.map.getLayer(this.container.id); │ │ │ │ - } │ │ │ │ - layer && layer.renderer.clear(); │ │ │ │ - OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments); │ │ │ │ - layer && layer.redraw(); │ │ │ │ + getURL: function(bounds) { │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ + │ │ │ │ + var imageSize = this.getImageSize(); │ │ │ │ + var newParams = {}; │ │ │ │ + // WMS 1.3 introduced axis order │ │ │ │ + var reverseAxisOrder = this.reverseAxisOrder(); │ │ │ │ + newParams.BBOX = this.encodeBBOX ? │ │ │ │ + bounds.toBBOX(null, reverseAxisOrder) : │ │ │ │ + bounds.toArray(reverseAxisOrder); │ │ │ │ + newParams.WIDTH = imageSize.w; │ │ │ │ + newParams.HEIGHT = imageSize.h; │ │ │ │ + var requestString = this.getFullRequestString(newParams); │ │ │ │ + return requestString; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: importSymbol │ │ │ │ - * add a new symbol definition from the rendererer's symbol hash │ │ │ │ + * APIMethod: mergeNewParams │ │ │ │ + * Catch changeParams and uppercase the new params to be merged in │ │ │ │ + * before calling changeParams on the super class. │ │ │ │ + * │ │ │ │ + * Once params have been changed, the tiles will be reloaded with │ │ │ │ + * the new parameters. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * graphicName - {String} name of the symbol to import │ │ │ │ + * newParams - {Object} Hashtable of new params to use │ │ │ │ + */ │ │ │ │ + mergeNewParams: function(newParams) { │ │ │ │ + var upperParams = OpenLayers.Util.upperCaseObject(newParams); │ │ │ │ + var newArguments = [upperParams]; │ │ │ │ + return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, │ │ │ │ + newArguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getFullRequestString │ │ │ │ + * Combine the layer's url with its params and these newParams. │ │ │ │ + * │ │ │ │ + * Add the SRS parameter from projection -- this is probably │ │ │ │ + * more eloquently done via a setProjection() method, but this │ │ │ │ + * works for now and always. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * newParams - {Object} │ │ │ │ + * altUrl - {String} Use this as the url instead of the layer's url │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} - hash of {DOMElement} "symbol" and {Number} "size" │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - importSymbol: function(graphicName) { │ │ │ │ - var id = this.container.id + "-" + graphicName; │ │ │ │ - │ │ │ │ - // check if symbol already exists in the cache │ │ │ │ - var cache = this.symbolCache[id]; │ │ │ │ - if (cache) { │ │ │ │ - return cache; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ - if (!symbol) { │ │ │ │ - throw new Error(graphicName + ' is not a valid symbol name'); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var symbolExtent = new OpenLayers.Bounds( │ │ │ │ - Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ - │ │ │ │ - var pathitems = ["m"]; │ │ │ │ - for (var i = 0; i < symbol.length; i = i + 2) { │ │ │ │ - var x = symbol[i]; │ │ │ │ - var y = symbol[i + 1]; │ │ │ │ - symbolExtent.left = Math.min(symbolExtent.left, x); │ │ │ │ - symbolExtent.bottom = Math.min(symbolExtent.bottom, y); │ │ │ │ - symbolExtent.right = Math.max(symbolExtent.right, x); │ │ │ │ - symbolExtent.top = Math.max(symbolExtent.top, y); │ │ │ │ - │ │ │ │ - pathitems.push(x); │ │ │ │ - pathitems.push(y); │ │ │ │ - if (i == 0) { │ │ │ │ - pathitems.push("l"); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - pathitems.push("x e"); │ │ │ │ - var path = pathitems.join(" "); │ │ │ │ - │ │ │ │ - var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2; │ │ │ │ - if (diff > 0) { │ │ │ │ - symbolExtent.bottom = symbolExtent.bottom - diff; │ │ │ │ - symbolExtent.top = symbolExtent.top + diff; │ │ │ │ + getFullRequestString: function(newParams, altUrl) { │ │ │ │ + var mapProjection = this.map.getProjectionObject(); │ │ │ │ + var projectionCode = this.projection && this.projection.equals(mapProjection) ? │ │ │ │ + this.projection.getCode() : │ │ │ │ + mapProjection.getCode(); │ │ │ │ + var value = (projectionCode == "none") ? null : projectionCode; │ │ │ │ + if (parseFloat(this.params.VERSION) >= 1.3) { │ │ │ │ + this.params.CRS = value; │ │ │ │ } else { │ │ │ │ - symbolExtent.left = symbolExtent.left + diff; │ │ │ │ - symbolExtent.right = symbolExtent.right - diff; │ │ │ │ + this.params.SRS = value; │ │ │ │ } │ │ │ │ │ │ │ │ - cache = { │ │ │ │ - path: path, │ │ │ │ - size: symbolExtent.getWidth(), // equals getHeight() now │ │ │ │ - left: symbolExtent.left, │ │ │ │ - bottom: symbolExtent.bottom │ │ │ │ - }; │ │ │ │ - this.symbolCache[id] = cache; │ │ │ │ + if (typeof this.params.TRANSPARENT == "boolean") { │ │ │ │ + newParams.TRANSPARENT = this.params.TRANSPARENT ? "TRUE" : "FALSE"; │ │ │ │ + } │ │ │ │ │ │ │ │ - return cache; │ │ │ │ + return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply( │ │ │ │ + this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer.VML" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.WMS" │ │ │ │ }); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * 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/Box.js │ │ │ │ + OpenLayers/Layer/KaMapCache.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Marker.js │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + * @requires OpenLayers/Layer/KaMap.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Marker.Box │ │ │ │ + * Class: OpenLayers.Layer.KaMapCache │ │ │ │ + * │ │ │ │ + * This class is designed to talk directly to a web-accessible ka-Map │ │ │ │ + * cache generated by the precache2.php script. │ │ │ │ + * │ │ │ │ + * To create a a new KaMapCache layer, you must indicate also the "i" parameter │ │ │ │ + * (that will be used to calculate the file extension), and another special │ │ │ │ + * parameter, object names "metaTileSize", with "h" (height) and "w" (width) │ │ │ │ + * properties. │ │ │ │ + * │ │ │ │ + * // Create a new kaMapCache layer. │ │ │ │ + * var kamap_base = new OpenLayers.Layer.KaMapCache( │ │ │ │ + * "Satellite", │ │ │ │ + * "http://www.example.org/web/acessible/cache", │ │ │ │ + * {g: "satellite", map: "world", i: 'png24', metaTileSize: {w: 5, h: 5} } │ │ │ │ + * ); │ │ │ │ + * │ │ │ │ + * // Create an kaMapCache overlay layer (using "isBaseLayer: false"). │ │ │ │ + * // Forces the output to be a "gif", using the "i" parameter. │ │ │ │ + * var kamap_overlay = new OpenLayers.Layer.KaMapCache( │ │ │ │ + * "Streets", │ │ │ │ + * "http://www.example.org/web/acessible/cache", │ │ │ │ + * {g: "streets", map: "world", i: "gif", metaTileSize: {w: 5, h: 5} }, │ │ │ │ + * {isBaseLayer: false} │ │ │ │ + * ); │ │ │ │ * │ │ │ │ + * The cache URLs must look like: │ │ │ │ + * var/cache/World/50000/Group_Name/def/t-440320/l20480 │ │ │ │ + * │ │ │ │ + * This means that the cache generated via tile.php will *not* work with │ │ │ │ + * this class, and should instead use the KaMap layer. │ │ │ │ + * │ │ │ │ + * More information is available in Ticket #1518. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Marker> │ │ │ │ + * - <OpenLayers.Layer.KaMap> │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ */ │ │ │ │ -OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: bounds │ │ │ │ - * {<OpenLayers.Bounds>} │ │ │ │ - */ │ │ │ │ - bounds: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: div │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - div: null, │ │ │ │ +OpenLayers.Layer.KaMapCache = OpenLayers.Class(OpenLayers.Layer.KaMap, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Marker.Box │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * borderColor - {String} │ │ │ │ - * borderWidth - {int} │ │ │ │ + /** │ │ │ │ + * Constant: IMAGE_EXTENSIONS │ │ │ │ + * {Object} Simple hash map to convert format to extension. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + IMAGE_EXTENSIONS: { │ │ │ │ + 'jpeg': 'jpg', │ │ │ │ + 'gif': 'gif', │ │ │ │ + 'png': 'png', │ │ │ │ + 'png8': 'png', │ │ │ │ + 'png24': 'png', │ │ │ │ + 'dithered': 'png' │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ + * Constant: DEFAULT_FORMAT │ │ │ │ + * {Object} Simple hash map to convert format to extension. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - │ │ │ │ - this.bounds = null; │ │ │ │ - this.div = null; │ │ │ │ - │ │ │ │ - OpenLayers.Marker.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + DEFAULT_FORMAT: 'jpeg', │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setBorder │ │ │ │ - * Allow the user to change the box's color and border width │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.KaMapCache │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * color - {String} Default is "red" │ │ │ │ - * width - {int} Default is 2 │ │ │ │ + * name - {String} │ │ │ │ + * url - {String} │ │ │ │ + * params - {Object} Parameters to be sent to the HTTP server in the │ │ │ │ + * query string for the tile. The format can be set via the 'i' │ │ │ │ + * parameter (defaults to jpg) , and the map should be set via │ │ │ │ + * the 'map' parameter. It has been reported that ka-Map may behave │ │ │ │ + * inconsistently if your format parameter does not match the format │ │ │ │ + * parameter configured in your config.php. (See ticket #327 for more │ │ │ │ + * information.) │ │ │ │ + * options - {Object} Additional options for the layer. Any of the │ │ │ │ + * APIProperties listed on this layer, and any layer types it │ │ │ │ + * extends, can be overridden through the options parameter. │ │ │ │ */ │ │ │ │ - setBorder: function(color, width) { │ │ │ │ - if (!color) { │ │ │ │ - color = "red"; │ │ │ │ - } │ │ │ │ - if (!width) { │ │ │ │ - width = 2; │ │ │ │ - } │ │ │ │ - this.div.style.border = width + "px solid " + color; │ │ │ │ + initialize: function(name, url, params, options) { │ │ │ │ + OpenLayers.Layer.KaMap.prototype.initialize.apply(this, arguments); │ │ │ │ + this.extension = this.IMAGE_EXTENSIONS[this.params.i.toLowerCase() || this.DEFAULT_FORMAT]; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ + /** │ │ │ │ + * Method: getURL │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ - * sz - {<OpenLayers.Size>} │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A new DOM Image with this marker's icon set at the │ │ │ │ - * location passed-in │ │ │ │ + * Returns: │ │ │ │ + * {String} A string with the layer's url and parameters and also the │ │ │ │ + * passed-in bounds and appropriate tile size specified as │ │ │ │ + * parameters │ │ │ │ */ │ │ │ │ - draw: function(px, sz) { │ │ │ │ - OpenLayers.Util.modifyDOMElement(this.div, null, px, sz); │ │ │ │ - return this.div; │ │ │ │ - }, │ │ │ │ + getURL: function(bounds) { │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ + var mapRes = this.map.getResolution(); │ │ │ │ + var scale = Math.round((this.map.getScale() * 10000)) / 10000; │ │ │ │ + var pX = Math.round(bounds.left / mapRes); │ │ │ │ + var pY = -Math.round(bounds.top / mapRes); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: onScreen │ │ │ │ - * │ │ │ │ - * Rreturn: │ │ │ │ - * {Boolean} Whether or not the marker is currently visible on screen. │ │ │ │ - */ │ │ │ │ - onScreen: function() { │ │ │ │ - var onScreen = false; │ │ │ │ - if (this.map) { │ │ │ │ - var screenBounds = this.map.getExtent(); │ │ │ │ - onScreen = screenBounds.containsBounds(this.bounds, true, true); │ │ │ │ - } │ │ │ │ - return onScreen; │ │ │ │ - }, │ │ │ │ + var metaX = Math.floor(pX / this.tileSize.w / this.params.metaTileSize.w) * this.tileSize.w * this.params.metaTileSize.w; │ │ │ │ + var metaY = Math.floor(pY / this.tileSize.h / this.params.metaTileSize.h) * this.tileSize.h * this.params.metaTileSize.h; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: display │ │ │ │ - * Hide or show the icon │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * display - {Boolean} │ │ │ │ - */ │ │ │ │ - display: function(display) { │ │ │ │ - this.div.style.display = (display) ? "" : "none"; │ │ │ │ + var components = [ │ │ │ │ + "/", │ │ │ │ + this.params.map, │ │ │ │ + "/", │ │ │ │ + scale, │ │ │ │ + "/", │ │ │ │ + this.params.g.replace(/\s/g, '_'), │ │ │ │ + "/def/t", │ │ │ │ + metaY, │ │ │ │ + "/l", │ │ │ │ + metaX, │ │ │ │ + "/t", │ │ │ │ + pY, │ │ │ │ + "l", │ │ │ │ + pX, │ │ │ │ + ".", │ │ │ │ + this.extension │ │ │ │ + ]; │ │ │ │ + │ │ │ │ + var url = this.url; │ │ │ │ + │ │ │ │ + if (OpenLayers.Util.isArray(url)) { │ │ │ │ + url = this.selectUrl(components.join(''), url); │ │ │ │ + } │ │ │ │ + return url + components.join(""); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Marker.Box" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.KaMapCache" │ │ │ │ }); │ │ │ │ - │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/QueryStringFilter.js │ │ │ │ + OpenLayers/Layer/ArcGISCache.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Console.js │ │ │ │ - * @requires OpenLayers/Format.js │ │ │ │ - * @requires OpenLayers/Filter/Spatial.js │ │ │ │ - * @requires OpenLayers/Filter/Comparison.js │ │ │ │ - * @requires OpenLayers/Filter/Logical.js │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/XYZ.js │ │ │ │ */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.QueryStringFilter │ │ │ │ - * Parser for reading a query string and creating a simple filter. │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.ArcGISCache │ │ │ │ + * Layer for accessing cached map tiles from an ArcGIS Server style mapcache. │ │ │ │ + * Tile must already be cached for this layer to access it. This does not require │ │ │ │ + * ArcGIS Server itself. │ │ │ │ + * │ │ │ │ + * A few attempts have been made at this kind of layer before. See │ │ │ │ + * http://trac.osgeo.org/openlayers/ticket/1967 │ │ │ │ + * and │ │ │ │ + * http://trac.osgeo.org/openlayers/browser/sandbox/tschaub/arcgiscache/lib/OpenLayers/Layer/ArcGISCache.js │ │ │ │ * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format> │ │ │ │ + * Typically the problem encountered is that the tiles seem to "jump around". │ │ │ │ + * This is due to the fact that the actual max extent for the tiles on AGS layers │ │ │ │ + * changes at each zoom level due to the way these caches are constructed. │ │ │ │ + * We have attempted to use the resolutions, tile size, and tile origin │ │ │ │ + * from the cache meta data to make the appropriate changes to the max extent │ │ │ │ + * of the tile to compensate for this behavior. This must be done as zoom levels change │ │ │ │ + * and before tiles are requested, which is why methods from base classes are overridden. │ │ │ │ + * │ │ │ │ + * For reference, you can access mapcache meta data in two ways. For accessing a │ │ │ │ + * mapcache through ArcGIS Server, you can simply go to the landing page for the │ │ │ │ + * layer. (ie. http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer) │ │ │ │ + * For accessing it directly through HTTP, there should always be a conf.xml file │ │ │ │ + * in the root directory. │ │ │ │ + * (ie. http://serverx.esri.com/arcgiscache/DG_County_roads_yesA_backgroundDark/Layers/conf.xml) │ │ │ │ + * │ │ │ │ + *Inherits from: │ │ │ │ + * - <OpenLayers.Layer.XYZ> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.QueryStringFilter = (function() { │ │ │ │ +OpenLayers.Layer.ArcGISCache = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Map the OpenLayers.Filter.Comparison types to the operation strings of │ │ │ │ - * the protocol. │ │ │ │ + /** │ │ │ │ + * APIProperty: url │ │ │ │ + * {String | Array} The base URL for the layer cache. You can also │ │ │ │ + * provide a list of URL strings for the layer if your cache is │ │ │ │ + * available from multiple origins. This must be set before the layer │ │ │ │ + * is drawn. │ │ │ │ */ │ │ │ │ - var cmpToStr = {}; │ │ │ │ - cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = "eq"; │ │ │ │ - cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = "ne"; │ │ │ │ - cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = "lt"; │ │ │ │ - cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = "lte"; │ │ │ │ - cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = "gt"; │ │ │ │ - cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = "gte"; │ │ │ │ - cmpToStr[OpenLayers.Filter.Comparison.LIKE] = "ilike"; │ │ │ │ + url: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: regex2value │ │ │ │ - * Convert the value from a regular expression string to a LIKE/ILIKE │ │ │ │ - * string known to the web service. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * value - {String} The regex string. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The converted string. │ │ │ │ + * APIProperty: tileOrigin │ │ │ │ + * {<OpenLayers.LonLat>} The location of the tile origin for the cache. │ │ │ │ + * An ArcGIS cache has it's origin at the upper-left (lowest x value │ │ │ │ + * and highest y value of the coordinate system). The units for the │ │ │ │ + * tile origin should be the same as the units for the cached data. │ │ │ │ */ │ │ │ │ - function regex2value(value) { │ │ │ │ + tileOrigin: null, │ │ │ │ │ │ │ │ - // highly sensitive!! Do not change this without running the │ │ │ │ - // Protocol/HTTP.html unit tests │ │ │ │ + /** │ │ │ │ + * APIProperty: tileSize │ │ │ │ + * {<OpenLayers.Size>} This size of each tile. Defaults to 256 by 256 pixels. │ │ │ │ + */ │ │ │ │ + tileSize: new OpenLayers.Size(256, 256), │ │ │ │ │ │ │ │ - // convert % to \% │ │ │ │ - value = value.replace(/%/g, "\\%"); │ │ │ │ + /** │ │ │ │ + * APIProperty: useAGS │ │ │ │ + * {Boolean} Indicates if we are going to be accessing the ArcGIS Server (AGS) │ │ │ │ + * cache via an AGS MapServer or directly through HTTP. When accessing via │ │ │ │ + * AGS the path structure uses a standard z/y/x structure. But AGS actually │ │ │ │ + * stores the tile images on disk using a hex based folder structure that looks │ │ │ │ + * like "http://example.com/mylayer/L00/R00000000/C00000000.png". Learn more │ │ │ │ + * about this here: │ │ │ │ + * http://blogs.esri.com/Support/blogs/mappingcenter/archive/2010/08/20/Checking-Your-Local-Cache-Folders.aspx │ │ │ │ + * Defaults to true; │ │ │ │ + */ │ │ │ │ + useArcGISServer: true, │ │ │ │ │ │ │ │ - // convert \\. to \\_ (\\.* occurences converted later) │ │ │ │ - value = value.replace(/\\\\\.(\*)?/g, function($0, $1) { │ │ │ │ - return $1 ? $0 : "\\\\_"; │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * APIProperty: type │ │ │ │ + * {String} Image type for the layer. This becomes the filename extension │ │ │ │ + * in tile requests. Default is "png" (generating a url like │ │ │ │ + * "http://example.com/mylayer/L00/R00000000/C00000000.png"). │ │ │ │ + */ │ │ │ │ + type: 'png', │ │ │ │ │ │ │ │ - // convert \\.* to \\% │ │ │ │ - value = value.replace(/\\\\\.\*/g, "\\\\%"); │ │ │ │ + /** │ │ │ │ + * APIProperty: useScales │ │ │ │ + * {Boolean} Optional override to indicate that the layer should use 'scale' information │ │ │ │ + * returned from the server capabilities object instead of 'resolution' information. │ │ │ │ + * This can be important if your tile server uses an unusual DPI for the tiles. │ │ │ │ + */ │ │ │ │ + useScales: false, │ │ │ │ │ │ │ │ - // convert . to _ (\. and .* occurences converted later) │ │ │ │ - value = value.replace(/(\\)?\.(\*)?/g, function($0, $1, $2) { │ │ │ │ - return $1 || $2 ? $0 : "_"; │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * APIProperty: overrideDPI │ │ │ │ + * {Boolean} Optional override to change the OpenLayers.DOTS_PER_INCH setting based │ │ │ │ + * on the tile information in the server capabilities object. This can be useful │ │ │ │ + * if your server has a non-standard DPI setting on its tiles, and you're only using │ │ │ │ + * tiles with that DPI. This value is used while OpenLayers is calculating resolution │ │ │ │ + * using scales, and is not necessary if you have resolution information. (This is │ │ │ │ + * typically the case) Regardless, this setting can be useful, but is dangerous │ │ │ │ + * because it will impact other layers while calculating resolution. Only use this │ │ │ │ + * if you know what you are doing. (See OpenLayers.Util.getResolutionFromScale) │ │ │ │ + */ │ │ │ │ + overrideDPI: false, │ │ │ │ │ │ │ │ - // convert .* to % (\.* occurnces converted later) │ │ │ │ - value = value.replace(/(\\)?\.\*/g, function($0, $1) { │ │ │ │ - return $1 ? $0 : "%"; │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.ArcGISCache │ │ │ │ + * Creates a new instance of this class │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} │ │ │ │ + * url - {String} │ │ │ │ + * options - {Object} extra layer options │ │ │ │ + */ │ │ │ │ + initialize: function(name, url, options) { │ │ │ │ + OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); │ │ │ │ │ │ │ │ - // convert \. to . │ │ │ │ - value = value.replace(/\\\./g, "."); │ │ │ │ + if (this.resolutions) { │ │ │ │ + this.serverResolutions = this.resolutions; │ │ │ │ + this.maxExtent = this.getMaxExtentForResolution(this.resolutions[0]); │ │ │ │ + } │ │ │ │ │ │ │ │ - // replace \* with * (watching out for \\*) │ │ │ │ - value = value.replace(/(\\)?\\\*/g, function($0, $1) { │ │ │ │ - return $1 ? $0 : "*"; │ │ │ │ - }); │ │ │ │ + // this block steps through translating the values from the server layer JSON │ │ │ │ + // capabilities object into values that we can use. This is also a helpful │ │ │ │ + // reference when configuring this layer directly. │ │ │ │ + if (this.layerInfo) { │ │ │ │ + // alias the object │ │ │ │ + var info = this.layerInfo; │ │ │ │ │ │ │ │ - return value; │ │ │ │ - } │ │ │ │ + // build our extents │ │ │ │ + var startingTileExtent = new OpenLayers.Bounds( │ │ │ │ + info.fullExtent.xmin, │ │ │ │ + info.fullExtent.ymin, │ │ │ │ + info.fullExtent.xmax, │ │ │ │ + info.fullExtent.ymax │ │ │ │ + ); │ │ │ │ │ │ │ │ - return OpenLayers.Class(OpenLayers.Format, { │ │ │ │ + // set our projection based on the given spatial reference. │ │ │ │ + // esri uses slightly different IDs, so this may not be comprehensive │ │ │ │ + this.projection = 'EPSG:' + info.spatialReference.wkid; │ │ │ │ + this.sphericalMercator = (info.spatialReference.wkid == 102100); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: wildcarded. │ │ │ │ - * {Boolean} If true percent signs are added around values │ │ │ │ - * read from LIKE filters, for example if the protocol │ │ │ │ - * read method is passed a LIKE filter whose property │ │ │ │ - * is "foo" and whose value is "bar" the string │ │ │ │ - * "foo__ilike=%bar%" will be sent in the query string; │ │ │ │ - * defaults to false. │ │ │ │ - */ │ │ │ │ - wildcarded: false, │ │ │ │ + // convert esri units into openlayers units (basic feet or meters only) │ │ │ │ + this.units = (info.units == "esriFeet") ? 'ft' : 'm'; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: srsInBBOX │ │ │ │ - * {Boolean} Include the SRS identifier in BBOX query string parameter. │ │ │ │ - * Default is false. If true and the layer has a projection object set, │ │ │ │ - * any BBOX filter will be serialized with a fifth item identifying the │ │ │ │ - * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913 │ │ │ │ - */ │ │ │ │ - srsInBBOX: false, │ │ │ │ + // optional extended section based on whether or not the server returned │ │ │ │ + // specific tile information │ │ │ │ + if (!!info.tileInfo) { │ │ │ │ + // either set the tiles based on rows/columns, or specific width/height │ │ │ │ + this.tileSize = new OpenLayers.Size( │ │ │ │ + info.tileInfo.width || info.tileInfo.cols, │ │ │ │ + info.tileInfo.height || info.tileInfo.rows │ │ │ │ + ); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Serialize an <OpenLayers.Filter> objects using the "simple" filter syntax for │ │ │ │ - * query string parameters. This function must be called as a method of │ │ │ │ - * a protocol instance. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * filter - {<OpenLayers.Filter>} filter to convert. │ │ │ │ - * params - {Object} The parameters object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} The resulting parameters object. │ │ │ │ - */ │ │ │ │ - write: function(filter, params) { │ │ │ │ - params = params || {}; │ │ │ │ - var className = filter.CLASS_NAME; │ │ │ │ - var filterType = className.substring(className.lastIndexOf(".") + 1); │ │ │ │ - switch (filterType) { │ │ │ │ - case "Spatial": │ │ │ │ - switch (filter.type) { │ │ │ │ - case OpenLayers.Filter.Spatial.BBOX: │ │ │ │ - params.bbox = filter.value.toArray(); │ │ │ │ - if (this.srsInBBOX && filter.projection) { │ │ │ │ - params.bbox.push(filter.projection.getCode()); │ │ │ │ - } │ │ │ │ - break; │ │ │ │ - case OpenLayers.Filter.Spatial.DWITHIN: │ │ │ │ - params.tolerance = filter.distance; │ │ │ │ - // no break here │ │ │ │ - case OpenLayers.Filter.Spatial.WITHIN: │ │ │ │ - params.lon = filter.value.x; │ │ │ │ - params.lat = filter.value.y; │ │ │ │ - break; │ │ │ │ - default: │ │ │ │ - OpenLayers.Console.warn( │ │ │ │ - "Unknown spatial filter type " + filter.type); │ │ │ │ - } │ │ │ │ - break; │ │ │ │ - case "Comparison": │ │ │ │ - var op = cmpToStr[filter.type]; │ │ │ │ - if (op !== undefined) { │ │ │ │ - var value = filter.value; │ │ │ │ - if (filter.type == OpenLayers.Filter.Comparison.LIKE) { │ │ │ │ - value = regex2value(value); │ │ │ │ - if (this.wildcarded) { │ │ │ │ - value = "%" + value + "%"; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - params[filter.property + "__" + op] = value; │ │ │ │ - params.queryable = params.queryable || []; │ │ │ │ - params.queryable.push(filter.property); │ │ │ │ - } else { │ │ │ │ - OpenLayers.Console.warn( │ │ │ │ - "Unknown comparison filter type " + filter.type); │ │ │ │ - } │ │ │ │ - break; │ │ │ │ - case "Logical": │ │ │ │ - if (filter.type === OpenLayers.Filter.Logical.AND) { │ │ │ │ - for (var i = 0, len = filter.filters.length; i < len; i++) { │ │ │ │ - params = this.write(filter.filters[i], params); │ │ │ │ + // this must be set when manually configuring this layer │ │ │ │ + this.tileOrigin = new OpenLayers.LonLat( │ │ │ │ + info.tileInfo.origin.x, │ │ │ │ + info.tileInfo.origin.y │ │ │ │ + ); │ │ │ │ + │ │ │ │ + var upperLeft = new OpenLayers.Geometry.Point( │ │ │ │ + startingTileExtent.left, │ │ │ │ + startingTileExtent.top │ │ │ │ + ); │ │ │ │ + │ │ │ │ + var bottomRight = new OpenLayers.Geometry.Point( │ │ │ │ + startingTileExtent.right, │ │ │ │ + startingTileExtent.bottom │ │ │ │ + ); │ │ │ │ + │ │ │ │ + if (this.useScales) { │ │ │ │ + this.scales = []; │ │ │ │ + } else { │ │ │ │ + this.resolutions = []; │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.lods = []; │ │ │ │ + for (var key in info.tileInfo.lods) { │ │ │ │ + if (info.tileInfo.lods.hasOwnProperty(key)) { │ │ │ │ + var lod = info.tileInfo.lods[key]; │ │ │ │ + if (this.useScales) { │ │ │ │ + this.scales.push(lod.scale); │ │ │ │ + } else { │ │ │ │ + this.resolutions.push(lod.resolution); │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - OpenLayers.Console.warn( │ │ │ │ - "Unsupported logical filter type " + filter.type); │ │ │ │ + │ │ │ │ + var start = this.getContainingTileCoords(upperLeft, lod.resolution); │ │ │ │ + lod.startTileCol = start.x; │ │ │ │ + lod.startTileRow = start.y; │ │ │ │ + │ │ │ │ + var end = this.getContainingTileCoords(bottomRight, lod.resolution); │ │ │ │ + lod.endTileCol = end.x; │ │ │ │ + lod.endTileRow = end.y; │ │ │ │ + this.lods.push(lod); │ │ │ │ } │ │ │ │ - break; │ │ │ │ - default: │ │ │ │ - OpenLayers.Console.warn("Unknown filter type " + filterType); │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.maxExtent = this.calculateMaxExtentWithLOD(this.lods[0]); │ │ │ │ + this.serverResolutions = this.resolutions; │ │ │ │ + if (this.overrideDPI && info.tileInfo.dpi) { │ │ │ │ + // see comment above for 'overrideDPI' │ │ │ │ + OpenLayers.DOTS_PER_INCH = info.tileInfo.dpi; │ │ │ │ + } │ │ │ │ } │ │ │ │ - return params; │ │ │ │ - }, │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.QueryStringFilter" │ │ │ │ + /** │ │ │ │ + * Method: getContainingTileCoords │ │ │ │ + * Calculates the x/y pixel corresponding to the position of the tile │ │ │ │ + * that contains the given point and for the for the given resolution. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} │ │ │ │ + * res - {Float} The resolution for which to compute the extent. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position │ │ │ │ + * of the upper left tile for the given resolution. │ │ │ │ + */ │ │ │ │ + getContainingTileCoords: function(point, res) { │ │ │ │ + return new OpenLayers.Pixel( │ │ │ │ + Math.max(Math.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)), 0), │ │ │ │ + Math.max(Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res)), 0) │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * Method: calculateMaxExtentWithLOD │ │ │ │ + * Given a Level of Detail object from the server, this function │ │ │ │ + * calculates the actual max extent │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * lod - {Object} a Level of Detail Object from the server capabilities object │ │ │ │ + representing a particular zoom level │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level │ │ │ │ + */ │ │ │ │ + calculateMaxExtentWithLOD: function(lod) { │ │ │ │ + // the max extent we're provided with just overlaps some tiles │ │ │ │ + // our real extent is the bounds of all the tiles we touch │ │ │ │ │ │ │ │ + var numTileCols = (lod.endTileCol - lod.startTileCol) + 1; │ │ │ │ + var numTileRows = (lod.endTileRow - lod.startTileRow) + 1; │ │ │ │ │ │ │ │ -})(); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WFSDescribeFeatureType.js │ │ │ │ - ====================================================================== */ │ │ │ │ + var minX = this.tileOrigin.lon + (lod.startTileCol * this.tileSize.w * lod.resolution); │ │ │ │ + var maxX = minX + (numTileCols * this.tileSize.w * lod.resolution); │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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 maxY = this.tileOrigin.lat - (lod.startTileRow * this.tileSize.h * lod.resolution); │ │ │ │ + var minY = maxY - (numTileRows * this.tileSize.h * lod.resolution); │ │ │ │ + return new OpenLayers.Bounds(minX, minY, maxX, maxY); │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Format/OGCExceptionReport.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Method: calculateMaxExtentWithExtent │ │ │ │ + * Given a 'suggested' max extent from the server, this function uses │ │ │ │ + * information about the actual tile sizes to determine the actual │ │ │ │ + * extent of the layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * extent - {<OpenLayers.Bounds>} The 'suggested' extent for the layer │ │ │ │ + * res - {Float} The resolution for which to compute the extent. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level │ │ │ │ + */ │ │ │ │ + calculateMaxExtentWithExtent: function(extent, res) { │ │ │ │ + var upperLeft = new OpenLayers.Geometry.Point(extent.left, extent.top); │ │ │ │ + var bottomRight = new OpenLayers.Geometry.Point(extent.right, extent.bottom); │ │ │ │ + var start = this.getContainingTileCoords(upperLeft, res); │ │ │ │ + var end = this.getContainingTileCoords(bottomRight, res); │ │ │ │ + var lod = { │ │ │ │ + resolution: res, │ │ │ │ + startTileCol: start.x, │ │ │ │ + startTileRow: start.y, │ │ │ │ + endTileCol: end.x, │ │ │ │ + endTileRow: end.y │ │ │ │ + }; │ │ │ │ + return this.calculateMaxExtentWithLOD(lod); │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WFSDescribeFeatureType │ │ │ │ - * Read WFS DescribeFeatureType response │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WFSDescribeFeatureType = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.XML, { │ │ │ │ + /** │ │ │ │ + * Method: getUpperLeftTileCoord │ │ │ │ + * Calculates the x/y pixel corresponding to the position │ │ │ │ + * of the upper left tile for the given resolution. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * res - {Float} The resolution for which to compute the extent. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position │ │ │ │ + * of the upper left tile for the given resolution. │ │ │ │ + */ │ │ │ │ + getUpperLeftTileCoord: function(res) { │ │ │ │ + var upperLeft = new OpenLayers.Geometry.Point( │ │ │ │ + this.maxExtent.left, │ │ │ │ + this.maxExtent.top); │ │ │ │ + return this.getContainingTileCoords(upperLeft, res); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: regExes │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ - */ │ │ │ │ - regExes: { │ │ │ │ - trimSpace: (/^\s*|\s*$/g) │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: getLowerRightTileCoord │ │ │ │ + * Calculates the x/y pixel corresponding to the position │ │ │ │ + * of the lower right tile for the given resolution. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * res - {Float} The resolution for which to compute the extent. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position │ │ │ │ + * of the lower right tile for the given resolution. │ │ │ │ + */ │ │ │ │ + getLowerRightTileCoord: function(res) { │ │ │ │ + var bottomRight = new OpenLayers.Geometry.Point( │ │ │ │ + this.maxExtent.right, │ │ │ │ + this.maxExtent.bottom); │ │ │ │ + return this.getContainingTileCoords(bottomRight, res); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ - */ │ │ │ │ - namespaces: { │ │ │ │ - xsd: "http://www.w3.org/2001/XMLSchema" │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: getMaxExtentForResolution │ │ │ │ + * Since the max extent of a set of tiles can change from zoom level │ │ │ │ + * to zoom level, we need to be able to calculate that max extent │ │ │ │ + * for a given resolution. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * res - {Float} The resolution for which to compute the extent. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} The extent for this resolution │ │ │ │ + */ │ │ │ │ + getMaxExtentForResolution: function(res) { │ │ │ │ + var start = this.getUpperLeftTileCoord(res); │ │ │ │ + var end = this.getLowerRightTileCoord(res); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WFSDescribeFeatureType │ │ │ │ - * Create a new parser for WFS DescribeFeatureType responses. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + var numTileCols = (end.x - start.x) + 1; │ │ │ │ + var numTileRows = (end.y - start.y) + 1; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "xsd": { │ │ │ │ - "schema": function(node, obj) { │ │ │ │ - var complexTypes = []; │ │ │ │ - var customTypes = {}; │ │ │ │ - var schema = { │ │ │ │ - complexTypes: complexTypes, │ │ │ │ - customTypes: customTypes │ │ │ │ - }; │ │ │ │ - var i, len; │ │ │ │ + var minX = this.tileOrigin.lon + (start.x * this.tileSize.w * res); │ │ │ │ + var maxX = minX + (numTileCols * this.tileSize.w * res); │ │ │ │ │ │ │ │ - this.readChildNodes(node, schema); │ │ │ │ + var maxY = this.tileOrigin.lat - (start.y * this.tileSize.h * res); │ │ │ │ + var minY = maxY - (numTileRows * this.tileSize.h * res); │ │ │ │ + return new OpenLayers.Bounds(minX, minY, maxX, maxY); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var attributes = node.attributes; │ │ │ │ - var attr, name; │ │ │ │ - for (i = 0, len = attributes.length; i < len; ++i) { │ │ │ │ - attr = attributes[i]; │ │ │ │ - name = attr.name; │ │ │ │ - if (name.indexOf("xmlns") === 0) { │ │ │ │ - this.setNamespace(name.split(":")[1] || "", attr.value); │ │ │ │ - } else { │ │ │ │ - obj[name] = attr.value; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - obj.featureTypes = complexTypes; │ │ │ │ - obj.targetPrefix = this.namespaceAlias[obj.targetNamespace]; │ │ │ │ + /** │ │ │ │ + * APIMethod: clone │ │ │ │ + * Returns an exact clone of this OpenLayers.Layer.ArcGISCache │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * [obj] - {Object} optional object to assign the cloned instance to. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.ArcGISCache>} clone of this instance │ │ │ │ + */ │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.ArcGISCache(this.name, this.url, this.options); │ │ │ │ + } │ │ │ │ + return OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // map complexTypes to names of customTypes │ │ │ │ - var complexType, customType; │ │ │ │ - for (i = 0, len = complexTypes.length; i < len; ++i) { │ │ │ │ - complexType = complexTypes[i]; │ │ │ │ - customType = customTypes[complexType.typeName]; │ │ │ │ - if (customTypes[complexType.typeName]) { │ │ │ │ - complexType.typeName = customType.name; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "complexType": function(node, obj) { │ │ │ │ - var complexType = { │ │ │ │ - // this is a temporary typeName, it will be overwritten by │ │ │ │ - // the schema reader with the metadata found in the │ │ │ │ - // customTypes hash │ │ │ │ - "typeName": node.getAttribute("name") │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, complexType); │ │ │ │ - obj.complexTypes.push(complexType); │ │ │ │ - }, │ │ │ │ - "complexContent": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "extension": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "sequence": function(node, obj) { │ │ │ │ - var sequence = { │ │ │ │ - elements: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, sequence); │ │ │ │ - obj.properties = sequence.elements; │ │ │ │ - }, │ │ │ │ - "element": function(node, obj) { │ │ │ │ - var type; │ │ │ │ - if (obj.elements) { │ │ │ │ - var element = {}; │ │ │ │ - var attributes = node.attributes; │ │ │ │ - var attr; │ │ │ │ - for (var i = 0, len = attributes.length; i < len; ++i) { │ │ │ │ - attr = attributes[i]; │ │ │ │ - element[attr.name] = attr.value; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: initGriddedTiles │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + */ │ │ │ │ + initGriddedTiles: function(bounds) { │ │ │ │ + delete this._tileOrigin; │ │ │ │ + OpenLayers.Layer.XYZ.prototype.initGriddedTiles.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - type = element.type; │ │ │ │ - if (!type) { │ │ │ │ - type = {}; │ │ │ │ - this.readChildNodes(node, type); │ │ │ │ - element.restriction = type; │ │ │ │ - element.type = type.base; │ │ │ │ - } │ │ │ │ - var fullType = type.base || type; │ │ │ │ - element.localType = fullType.split(":").pop(); │ │ │ │ - obj.elements.push(element); │ │ │ │ - this.readChildNodes(node, element); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: getMaxExtent │ │ │ │ + * Get this layer's maximum extent. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} │ │ │ │ + */ │ │ │ │ + getMaxExtent: function() { │ │ │ │ + var resolution = this.map.getResolution(); │ │ │ │ + return this.maxExtent = this.getMaxExtentForResolution(resolution); │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (obj.complexTypes) { │ │ │ │ - type = node.getAttribute("type"); │ │ │ │ - var localType = type.split(":").pop(); │ │ │ │ - obj.customTypes[localType] = { │ │ │ │ - "name": node.getAttribute("name"), │ │ │ │ - "type": type │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "annotation": function(node, obj) { │ │ │ │ - obj.annotation = {}; │ │ │ │ - this.readChildNodes(node, obj.annotation); │ │ │ │ - }, │ │ │ │ - "appinfo": function(node, obj) { │ │ │ │ - if (!obj.appinfo) { │ │ │ │ - obj.appinfo = []; │ │ │ │ - } │ │ │ │ - obj.appinfo.push(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "documentation": function(node, obj) { │ │ │ │ - if (!obj.documentation) { │ │ │ │ - obj.documentation = []; │ │ │ │ - } │ │ │ │ - var value = this.getChildValue(node); │ │ │ │ - obj.documentation.push({ │ │ │ │ - lang: node.getAttribute("xml:lang"), │ │ │ │ - textContent: value.replace(this.regExes.trimSpace, "") │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "simpleType": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "restriction": function(node, obj) { │ │ │ │ - obj.base = node.getAttribute("base"); │ │ │ │ - this.readRestriction(node, obj); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: getTileOrigin │ │ │ │ + * Determine the origin for aligning the grid of tiles. │ │ │ │ + * The origin will be derived from the layer's <maxExtent> property. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.LonLat>} The tile origin. │ │ │ │ + */ │ │ │ │ + getTileOrigin: function() { │ │ │ │ + if (!this._tileOrigin) { │ │ │ │ + var extent = this.getMaxExtent(); │ │ │ │ + this._tileOrigin = new OpenLayers.LonLat(extent.left, extent.bottom); │ │ │ │ + } │ │ │ │ + return this._tileOrigin; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: readRestriction │ │ │ │ - * Reads restriction defined in the child nodes of a restriction element │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} the node to parse │ │ │ │ - * obj - {Object} the object that receives the read result │ │ │ │ - */ │ │ │ │ - readRestriction: function(node, obj) { │ │ │ │ - var children = node.childNodes; │ │ │ │ - var child, nodeName, value; │ │ │ │ - for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ - child = children[i]; │ │ │ │ - if (child.nodeType == 1) { │ │ │ │ - nodeName = child.nodeName.split(":").pop(); │ │ │ │ - value = child.getAttribute("value"); │ │ │ │ - if (!obj[nodeName]) { │ │ │ │ - obj[nodeName] = value; │ │ │ │ - } else { │ │ │ │ - if (typeof obj[nodeName] == "string") { │ │ │ │ - obj[nodeName] = [obj[nodeName]]; │ │ │ │ - } │ │ │ │ - obj[nodeName].push(value); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: getURL │ │ │ │ + * Determine the URL for a tile given the tile bounds. This is should support │ │ │ │ + * urls that access tiles through an ArcGIS Server MapServer or directly through │ │ │ │ + * the hex folder structure using HTTP. Just be sure to set the useArcGISServer │ │ │ │ + * property appropriately! This is basically the same as │ │ │ │ + * 'OpenLayers.Layer.TMS.getURL', but with the addition of hex addressing, │ │ │ │ + * and tile rounding. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The URL for a tile based on given bounds. │ │ │ │ + */ │ │ │ │ + getURL: function(bounds) { │ │ │ │ + var res = this.getResolution(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: read │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {DOMElement|String} A WFS DescribeFeatureType document. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object representing the WFS DescribeFeatureType response. │ │ │ │ - */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - } │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ - data = data.documentElement; │ │ │ │ + // tile center │ │ │ │ + var originTileX = (this.tileOrigin.lon + (res * this.tileSize.w / 2)); │ │ │ │ + var originTileY = (this.tileOrigin.lat - (res * this.tileSize.h / 2)); │ │ │ │ + │ │ │ │ + var center = bounds.getCenterLonLat(); │ │ │ │ + var point = { │ │ │ │ + x: center.lon, │ │ │ │ + y: center.lat │ │ │ │ + }; │ │ │ │ + var x = (Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w)))); │ │ │ │ + var y = (Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h)))); │ │ │ │ + var z = this.map.getZoom(); │ │ │ │ + │ │ │ │ + // this prevents us from getting pink tiles (non-existant tiles) │ │ │ │ + if (this.lods) { │ │ │ │ + var lod = this.lods[this.map.getZoom()]; │ │ │ │ + if ((x < lod.startTileCol || x > lod.endTileCol) || │ │ │ │ + (y < lod.startTileRow || y > lod.endTileRow)) { │ │ │ │ + return null; │ │ │ │ } │ │ │ │ - var schema = {}; │ │ │ │ - if (data.nodeName.split(":").pop() === 'ExceptionReport') { │ │ │ │ - // an exception must have occurred, so parse it │ │ │ │ - var parser = new OpenLayers.Format.OGCExceptionReport(); │ │ │ │ - schema.error = parser.read(data); │ │ │ │ - } else { │ │ │ │ - this.readNode(data, schema); │ │ │ │ + } else { │ │ │ │ + var start = this.getUpperLeftTileCoord(res); │ │ │ │ + var end = this.getLowerRightTileCoord(res); │ │ │ │ + if ((x < start.x || x >= end.x) || │ │ │ │ + (y < start.y || y >= end.y)) { │ │ │ │ + return null; │ │ │ │ } │ │ │ │ - return schema; │ │ │ │ - }, │ │ │ │ + } │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WFSDescribeFeatureType" │ │ │ │ + // Construct the url string │ │ │ │ + var url = this.url; │ │ │ │ + var s = '' + x + y + z; │ │ │ │ │ │ │ │ - }); │ │ │ │ + if (OpenLayers.Util.isArray(url)) { │ │ │ │ + url = this.selectUrl(s, url); │ │ │ │ + } │ │ │ │ + │ │ │ │ + // Accessing tiles through ArcGIS Server uses a different path │ │ │ │ + // structure than direct access via the folder structure. │ │ │ │ + if (this.useArcGISServer) { │ │ │ │ + // AGS MapServers have pretty url access to tiles │ │ │ │ + url = url + '/tile/${z}/${y}/${x}'; │ │ │ │ + } else { │ │ │ │ + // The tile images are stored using hex values on disk. │ │ │ │ + x = 'C' + OpenLayers.Number.zeroPad(x, 8, 16); │ │ │ │ + y = 'R' + OpenLayers.Number.zeroPad(y, 8, 16); │ │ │ │ + z = 'L' + OpenLayers.Number.zeroPad(z, 2, 10); │ │ │ │ + url = url + '/${z}/${y}/${x}.' + this.type; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // Write the values into our formatted url │ │ │ │ + url = OpenLayers.String.format(url, { │ │ │ │ + 'x': x, │ │ │ │ + 'y': y, │ │ │ │ + 'z': z │ │ │ │ + }); │ │ │ │ + │ │ │ │ + return OpenLayers.Util.urlAppend( │ │ │ │ + url, OpenLayers.Util.getParameterString(this.params) │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: 'OpenLayers.Layer.ArcGISCache' │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/WFS.js │ │ │ │ + OpenLayers/Layer/Google/v3.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/GML.js │ │ │ │ - * @requires OpenLayers/Console.js │ │ │ │ - * @requires OpenLayers/Lang.js │ │ │ │ + * @requires OpenLayers/Layer/Google.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.WFS │ │ │ │ - * Read/Write WFS. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.GML> │ │ │ │ + * Constant: OpenLayers.Layer.Google.v3 │ │ │ │ + * │ │ │ │ + * Mixin providing functionality specific to the Google Maps API v3. │ │ │ │ + * │ │ │ │ + * To use this layer, you must include the GMaps v3 API in your html. │ │ │ │ + * │ │ │ │ + * Note that this layer configures the google.maps.map object with the │ │ │ │ + * "disableDefaultUI" option set to true. Using UI controls that the Google │ │ │ │ + * Maps API provides is not supported by the OpenLayers API. │ │ │ │ */ │ │ │ │ -OpenLayers.Format.WFS = OpenLayers.Class(OpenLayers.Format.GML, { │ │ │ │ +OpenLayers.Layer.Google.v3 = { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: layer │ │ │ │ - * {<OpenLayers.Layer>} │ │ │ │ + /** │ │ │ │ + * Constant: DEFAULTS │ │ │ │ + * {Object} It is not recommended to change the properties set here. Note │ │ │ │ + * that Google.v3 layers only work when sphericalMercator is set to true. │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * { │ │ │ │ + * sphericalMercator: true, │ │ │ │ + * projection: "EPSG:900913" │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ */ │ │ │ │ - layer: null, │ │ │ │ + DEFAULTS: { │ │ │ │ + sphericalMercator: true, │ │ │ │ + projection: "EPSG:900913" │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: wfsns │ │ │ │ - * {String} │ │ │ │ + * APIProperty: animationEnabled │ │ │ │ + * {Boolean} If set to true, the transition between zoom levels will be │ │ │ │ + * animated (if supported by the GMaps API for the device used). Set to │ │ │ │ + * false to match the zooming experience of other layer types. Default │ │ │ │ + * is true. Note that the GMaps API does not give us control over zoom │ │ │ │ + * animation, so if set to false, when zooming, this will make the │ │ │ │ + * layer temporarily invisible, wait until GMaps reports the map being │ │ │ │ + * idle, and make it visible again. The result will be a blank layer │ │ │ │ + * for a few moments while zooming. │ │ │ │ */ │ │ │ │ - wfsns: "http://www.opengis.net/wfs", │ │ │ │ + animationEnabled: true, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: ogcns │ │ │ │ - * {String} │ │ │ │ + /** │ │ │ │ + * Method: loadMapObject │ │ │ │ + * Load the GMap and register appropriate event listeners. │ │ │ │ */ │ │ │ │ - ogcns: "http://www.opengis.net/ogc", │ │ │ │ + loadMapObject: function() { │ │ │ │ + if (!this.type) { │ │ │ │ + this.type = google.maps.MapTypeId.ROADMAP; │ │ │ │ + } │ │ │ │ + var mapObject; │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache) { │ │ │ │ + // there are already Google layers added to this map │ │ │ │ + mapObject = cache.mapObject; │ │ │ │ + // increment the layer count │ │ │ │ + ++cache.count; │ │ │ │ + } else { │ │ │ │ + // this is the first Google layer for this map │ │ │ │ + // create GMap │ │ │ │ + var center = this.map.getCenter(); │ │ │ │ + var container = document.createElement('div'); │ │ │ │ + container.className = "olForeignContainer"; │ │ │ │ + container.style.width = '100%'; │ │ │ │ + container.style.height = '100%'; │ │ │ │ + mapObject = new google.maps.Map(container, { │ │ │ │ + center: center ? │ │ │ │ + new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0), │ │ │ │ + zoom: this.map.getZoom() || 0, │ │ │ │ + mapTypeId: this.type, │ │ │ │ + disableDefaultUI: true, │ │ │ │ + keyboardShortcuts: false, │ │ │ │ + draggable: false, │ │ │ │ + disableDoubleClickZoom: true, │ │ │ │ + scrollwheel: false, │ │ │ │ + streetViewControl: false │ │ │ │ + }); │ │ │ │ + var googleControl = document.createElement('div'); │ │ │ │ + googleControl.style.width = '100%'; │ │ │ │ + googleControl.style.height = '100%'; │ │ │ │ + mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl); │ │ │ │ + │ │ │ │ + // cache elements for use by any other google layers added to │ │ │ │ + // this same map │ │ │ │ + cache = { │ │ │ │ + googleControl: googleControl, │ │ │ │ + mapObject: mapObject, │ │ │ │ + count: 1 │ │ │ │ + }; │ │ │ │ + OpenLayers.Layer.Google.cache[this.map.id] = cache; │ │ │ │ + } │ │ │ │ + this.mapObject = mapObject; │ │ │ │ + this.setGMapVisibility(this.visibility); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.WFS │ │ │ │ - * Create a WFS-T formatter. This requires a layer: that layer should │ │ │ │ - * have two properties: geometry_column and typename. The parser │ │ │ │ - * for this format is subclassed entirely from GML: There is a writer │ │ │ │ - * only, which uses most of the code from the GML layer, and wraps │ │ │ │ - * it in transactional elements. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} │ │ │ │ - * layer - {<OpenLayers.Layer>} │ │ │ │ + * APIMethod: onMapResize │ │ │ │ */ │ │ │ │ - initialize: function(options, layer) { │ │ │ │ - OpenLayers.Format.GML.prototype.initialize.apply(this, [options]); │ │ │ │ - this.layer = layer; │ │ │ │ - if (this.layer.featureNS) { │ │ │ │ - this.featureNS = this.layer.featureNS; │ │ │ │ - } │ │ │ │ - if (this.layer.options.geometry_column) { │ │ │ │ - this.geometryName = this.layer.options.geometry_column; │ │ │ │ - } │ │ │ │ - if (this.layer.options.typename) { │ │ │ │ - this.featureName = this.layer.options.typename; │ │ │ │ + onMapResize: function() { │ │ │ │ + if (this.visibility) { │ │ │ │ + google.maps.event.trigger(this.mapObject, "resize"); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write │ │ │ │ - * Takes a feature list, and generates a WFS-T Transaction │ │ │ │ - * │ │ │ │ + * Method: setGMapVisibility │ │ │ │ + * Display the GMap container and associated elements. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + * visible - {Boolean} Display the GMap elements. │ │ │ │ */ │ │ │ │ - write: function(features) { │ │ │ │ - │ │ │ │ - var transaction = this.createElementNS(this.wfsns, 'wfs:Transaction'); │ │ │ │ - transaction.setAttribute("version", "1.0.0"); │ │ │ │ - transaction.setAttribute("service", "WFS"); │ │ │ │ - for (var i = 0; i < features.length; i++) { │ │ │ │ - switch (features[i].state) { │ │ │ │ - case OpenLayers.State.INSERT: │ │ │ │ - transaction.appendChild(this.insert(features[i])); │ │ │ │ - break; │ │ │ │ - case OpenLayers.State.UPDATE: │ │ │ │ - transaction.appendChild(this.update(features[i])); │ │ │ │ - break; │ │ │ │ - case OpenLayers.State.DELETE: │ │ │ │ - transaction.appendChild(this.remove(features[i])); │ │ │ │ + setGMapVisibility: function(visible) { │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + var map = this.map; │ │ │ │ + if (cache) { │ │ │ │ + var type = this.type; │ │ │ │ + var layers = map.layers; │ │ │ │ + var layer; │ │ │ │ + for (var i = layers.length - 1; i >= 0; --i) { │ │ │ │ + layer = layers[i]; │ │ │ │ + if (layer instanceof OpenLayers.Layer.Google && │ │ │ │ + layer.visibility === true && layer.inRange === true) { │ │ │ │ + type = layer.type; │ │ │ │ + visible = true; │ │ │ │ break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var container = this.mapObject.getDiv(); │ │ │ │ + if (visible === true) { │ │ │ │ + if (container.parentNode !== map.div) { │ │ │ │ + if (!cache.rendered) { │ │ │ │ + var me = this; │ │ │ │ + google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() { │ │ │ │ + cache.rendered = true; │ │ │ │ + me.setGMapVisibility(me.getVisibility()); │ │ │ │ + me.moveTo(me.map.getCenter()); │ │ │ │ + }); │ │ │ │ + } else { │ │ │ │ + map.div.appendChild(container); │ │ │ │ + cache.googleControl.appendChild(map.viewPortDiv); │ │ │ │ + google.maps.event.trigger(this.mapObject, 'resize'); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.mapObject.setMapTypeId(type); │ │ │ │ + } else if (cache.googleControl.hasChildNodes()) { │ │ │ │ + map.div.appendChild(map.viewPortDiv); │ │ │ │ + map.div.removeChild(container); │ │ │ │ } │ │ │ │ } │ │ │ │ - │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [transaction]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createFeatureXML │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * Method: getMapContainer │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} the GMap container's div │ │ │ │ */ │ │ │ │ - createFeatureXML: function(feature) { │ │ │ │ - var geometryNode = this.buildGeometryNode(feature.geometry); │ │ │ │ - var geomContainer = this.createElementNS(this.featureNS, "feature:" + this.geometryName); │ │ │ │ - geomContainer.appendChild(geometryNode); │ │ │ │ - var featureContainer = this.createElementNS(this.featureNS, "feature:" + this.featureName); │ │ │ │ - featureContainer.appendChild(geomContainer); │ │ │ │ - for (var attr in feature.attributes) { │ │ │ │ - var attrText = this.createTextNode(feature.attributes[attr]); │ │ │ │ - var nodename = attr; │ │ │ │ - if (attr.search(":") != -1) { │ │ │ │ - nodename = attr.split(":")[1]; │ │ │ │ - } │ │ │ │ - var attrContainer = this.createElementNS(this.featureNS, "feature:" + nodename); │ │ │ │ - attrContainer.appendChild(attrText); │ │ │ │ - featureContainer.appendChild(attrContainer); │ │ │ │ - } │ │ │ │ - return featureContainer; │ │ │ │ + getMapContainer: function() { │ │ │ │ + return this.mapObject.getDiv(); │ │ │ │ }, │ │ │ │ │ │ │ │ + // │ │ │ │ + // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds │ │ │ │ + // │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: insert │ │ │ │ - * Takes a feature, and generates a WFS-T Transaction "Insert" │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * APIMethod: getMapObjectBoundsFromOLBounds │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * olBounds - {<OpenLayers.Bounds>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} A MapObject Bounds, translated from olBounds │ │ │ │ + * Returns null if null value is passed in │ │ │ │ */ │ │ │ │ - insert: function(feature) { │ │ │ │ - var insertNode = this.createElementNS(this.wfsns, 'wfs:Insert'); │ │ │ │ - insertNode.appendChild(this.createFeatureXML(feature)); │ │ │ │ - return insertNode; │ │ │ │ + getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ + var moBounds = null; │ │ │ │ + if (olBounds != null) { │ │ │ │ + var sw = this.sphericalMercator ? │ │ │ │ + this.inverseMercator(olBounds.bottom, olBounds.left) : │ │ │ │ + new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ + var ne = this.sphericalMercator ? │ │ │ │ + this.inverseMercator(olBounds.top, olBounds.right) : │ │ │ │ + new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ + moBounds = new google.maps.LatLngBounds( │ │ │ │ + new google.maps.LatLng(sw.lat, sw.lon), │ │ │ │ + new google.maps.LatLng(ne.lat, ne.lon) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return moBounds; │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ + /************************************ │ │ │ │ + * * │ │ │ │ + * MapObject Interface Controls * │ │ │ │ + * * │ │ │ │ + ************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // LonLat - Pixel Translation │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: update │ │ │ │ - * Takes a feature, and generates a WFS-T Transaction "Update" │ │ │ │ - * │ │ │ │ + * APIMethod: getMapObjectLonLatFromMapObjectPixel │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * moPixel - {Object} MapObject Pixel format │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} MapObject LonLat translated from MapObject Pixel │ │ │ │ */ │ │ │ │ - update: function(feature) { │ │ │ │ - if (!feature.fid) { │ │ │ │ - OpenLayers.Console.userError(OpenLayers.i18n("noFID")); │ │ │ │ - } │ │ │ │ - var updateNode = this.createElementNS(this.wfsns, 'wfs:Update'); │ │ │ │ - updateNode.setAttribute("typeName", this.featurePrefix + ':' + this.featureName); │ │ │ │ - updateNode.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); │ │ │ │ + getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ + var size = this.map.getSize(); │ │ │ │ + var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ + var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ + var res = this.map.getResolution(); │ │ │ │ │ │ │ │ - var propertyNode = this.createElementNS(this.wfsns, 'wfs:Property'); │ │ │ │ - var nameNode = this.createElementNS(this.wfsns, 'wfs:Name'); │ │ │ │ + var delta_x = moPixel.x - (size.w / 2); │ │ │ │ + var delta_y = moPixel.y - (size.h / 2); │ │ │ │ │ │ │ │ - var txtNode = this.createTextNode(this.geometryName); │ │ │ │ - nameNode.appendChild(txtNode); │ │ │ │ - propertyNode.appendChild(nameNode); │ │ │ │ + var lonlat = new OpenLayers.LonLat( │ │ │ │ + lon + delta_x * res, │ │ │ │ + lat - delta_y * res │ │ │ │ + ); │ │ │ │ │ │ │ │ - var valueNode = this.createElementNS(this.wfsns, 'wfs:Value'); │ │ │ │ + if (this.wrapDateLine) { │ │ │ │ + lonlat = lonlat.wrapDateLine(this.maxExtent); │ │ │ │ + } │ │ │ │ + return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var geometryNode = this.buildGeometryNode(feature.geometry); │ │ │ │ + /** │ │ │ │ + * APIMethod: getMapObjectPixelFromMapObjectLonLat │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moLonLat - {Object} MapObject LonLat format │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} MapObject Pixel transtlated from MapObject LonLat │ │ │ │ + */ │ │ │ │ + getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ + var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ + var res = this.map.getResolution(); │ │ │ │ + var extent = this.map.getExtent(); │ │ │ │ + return this.getMapObjectPixelFromXY((1 / res * (lon - extent.left)), │ │ │ │ + (1 / res * (extent.top - lat))); │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (feature.layer) { │ │ │ │ - geometryNode.setAttribute( │ │ │ │ - "srsName", feature.layer.projection.getCode() │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: setMapObjectCenter │ │ │ │ + * Set the mapObject to the specified center and zoom │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * center - {Object} MapObject LonLat format │ │ │ │ + * zoom - {int} MapObject zoom format │ │ │ │ + */ │ │ │ │ + setMapObjectCenter: function(center, zoom) { │ │ │ │ + if (this.animationEnabled === false && zoom != this.mapObject.zoom) { │ │ │ │ + var mapContainer = this.getMapContainer(); │ │ │ │ + google.maps.event.addListenerOnce( │ │ │ │ + this.mapObject, │ │ │ │ + "idle", │ │ │ │ + function() { │ │ │ │ + mapContainer.style.visibility = ""; │ │ │ │ + } │ │ │ │ ); │ │ │ │ + mapContainer.style.visibility = "hidden"; │ │ │ │ } │ │ │ │ + this.mapObject.setOptions({ │ │ │ │ + center: center, │ │ │ │ + zoom: zoom │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ │ │ │ │ - valueNode.appendChild(geometryNode); │ │ │ │ │ │ │ │ - propertyNode.appendChild(valueNode); │ │ │ │ - updateNode.appendChild(propertyNode); │ │ │ │ + // Bounds │ │ │ │ │ │ │ │ - // add in attributes │ │ │ │ - for (var propName in feature.attributes) { │ │ │ │ - propertyNode = this.createElementNS(this.wfsns, 'wfs:Property'); │ │ │ │ - nameNode = this.createElementNS(this.wfsns, 'wfs:Name'); │ │ │ │ - nameNode.appendChild(this.createTextNode(propName)); │ │ │ │ - propertyNode.appendChild(nameNode); │ │ │ │ - valueNode = this.createElementNS(this.wfsns, 'wfs:Value'); │ │ │ │ - valueNode.appendChild(this.createTextNode(feature.attributes[propName])); │ │ │ │ - propertyNode.appendChild(valueNode); │ │ │ │ - updateNode.appendChild(propertyNode); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIMethod: getMapObjectZoomFromMapObjectBounds │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moBounds - {Object} MapObject Bounds format │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} MapObject Zoom for specified MapObject Bounds │ │ │ │ + */ │ │ │ │ + getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ + return this.mapObject.getBoundsZoomLevel(moBounds); │ │ │ │ + }, │ │ │ │ │ │ │ │ + /************************************ │ │ │ │ + * * │ │ │ │ + * MapObject Primitives * │ │ │ │ + * * │ │ │ │ + ************************************/ │ │ │ │ │ │ │ │ - var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter'); │ │ │ │ - var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId'); │ │ │ │ - filterIdNode.setAttribute("fid", feature.fid); │ │ │ │ - filterNode.appendChild(filterIdNode); │ │ │ │ - updateNode.appendChild(filterNode); │ │ │ │ │ │ │ │ - return updateNode; │ │ │ │ - }, │ │ │ │ + // LonLat │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: remove │ │ │ │ - * Takes a feature, and generates a WFS-T Transaction "Delete" │ │ │ │ - * │ │ │ │ + * APIMethod: getMapObjectLonLatFromLonLat │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * lon - {Float} │ │ │ │ + * lat - {Float} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} MapObject LonLat built from lon and lat params │ │ │ │ */ │ │ │ │ - remove: function(feature) { │ │ │ │ - if (!feature.fid) { │ │ │ │ - OpenLayers.Console.userError(OpenLayers.i18n("noFID")); │ │ │ │ - return false; │ │ │ │ + getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ + var gLatLng; │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + var lonlat = this.inverseMercator(lon, lat); │ │ │ │ + gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon); │ │ │ │ + } else { │ │ │ │ + gLatLng = new google.maps.LatLng(lat, lon); │ │ │ │ } │ │ │ │ - var deleteNode = this.createElementNS(this.wfsns, 'wfs:Delete'); │ │ │ │ - deleteNode.setAttribute("typeName", this.featurePrefix + ':' + this.featureName); │ │ │ │ - deleteNode.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); │ │ │ │ - │ │ │ │ - var filterNode = this.createElementNS(this.ogcns, 'ogc:Filter'); │ │ │ │ - var filterIdNode = this.createElementNS(this.ogcns, 'ogc:FeatureId'); │ │ │ │ - filterIdNode.setAttribute("fid", feature.fid); │ │ │ │ - filterNode.appendChild(filterIdNode); │ │ │ │ - deleteNode.appendChild(filterNode); │ │ │ │ - │ │ │ │ - return deleteNode; │ │ │ │ + return gLatLng; │ │ │ │ }, │ │ │ │ │ │ │ │ + // Pixel │ │ │ │ + │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Remove ciruclar ref to layer │ │ │ │ + * APIMethod: getMapObjectPixelFromXY │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * x - {Integer} │ │ │ │ + * y - {Integer} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} MapObject Pixel from x and y parameters │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.layer = null; │ │ │ │ - }, │ │ │ │ + getMapObjectPixelFromXY: function(x, y) { │ │ │ │ + return new google.maps.Point(x, y); │ │ │ │ + } │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WFS" │ │ │ │ -}); │ │ │ │ +}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/WMTSCapabilities.js │ │ │ │ + OpenLayers/Layer/Vector/RootContainer.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ + * @requires OpenLayers/Layer/Vector.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.WMTSCapabilities │ │ │ │ - * Read WMTS Capabilities. │ │ │ │ + * Class: OpenLayers.Layer.Vector.RootContainer │ │ │ │ + * A special layer type to combine multiple vector layers inside a single │ │ │ │ + * renderer root container. This class is not supposed to be instantiated │ │ │ │ + * from user space, it is a helper class for controls that require event │ │ │ │ + * processing for multiple vector layers. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ + * - <OpenLayers.Layer.Vector> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ +OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ - * {String} Version number to assume if none found. Default is "1.0.0". │ │ │ │ + * Property: displayInLayerSwitcher │ │ │ │ + * Set to false for this layer type │ │ │ │ */ │ │ │ │ - defaultVersion: "1.0.0", │ │ │ │ + displayInLayerSwitcher: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: yx │ │ │ │ - * {Object} Members in the yx object are used to determine if a CRS URN │ │ │ │ - * corresponds to a CRS with y,x axis order. Member names are CRS URNs │ │ │ │ - * and values are boolean. By default, the following CRS URN are │ │ │ │ - * assumed to correspond to a CRS with y,x axis order: │ │ │ │ - * │ │ │ │ - * * urn:ogc:def:crs:EPSG::4326 │ │ │ │ + * APIProperty: layers │ │ │ │ + * Layers that are attached to this container. Required config option. │ │ │ │ */ │ │ │ │ - yx: { │ │ │ │ - "urn:ogc:def:crs:EPSG::4326": true │ │ │ │ - }, │ │ │ │ + layers: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.WMTSCapabilities │ │ │ │ - * Create a new parser for WMTS capabilities. │ │ │ │ + * Constructor: OpenLayers.Layer.Vector.RootContainer │ │ │ │ + * Create a new root container for multiple vector layer. This constructor │ │ │ │ + * is not supposed to be used from user space, it is only to be used by │ │ │ │ + * controls that need feature selection across multiple vector layers. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * name - {String} A name for the layer │ │ │ │ + * options - {Object} Optional object with non-default properties to set on │ │ │ │ + * the layer. │ │ │ │ + * │ │ │ │ + * Required options properties: │ │ │ │ + * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this │ │ │ │ + * container │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root │ │ │ │ + * container │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read capabilities data from a string, and return information about │ │ │ │ - * the service (offering and observedProperty mostly). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Info about the WMTS Capabilities │ │ │ │ + * Method: display │ │ │ │ */ │ │ │ │ + display: function() {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: createLayer │ │ │ │ - * Create a WMTS layer given a capabilities object. │ │ │ │ - * │ │ │ │ + * Method: getFeatureFromEvent │ │ │ │ + * walk through the layers to find the feature returned by the event │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * capabilities - {Object} The object returned from a <read> call to this │ │ │ │ - * format. │ │ │ │ - * config - {Object} Configuration properties for the layer. Defaults for │ │ │ │ - * the layer will apply if not provided. │ │ │ │ - * │ │ │ │ - * Required config properties: │ │ │ │ - * layer - {String} The layer identifier. │ │ │ │ - * │ │ │ │ - * Optional config properties: │ │ │ │ - * matrixSet - {String} The matrix set identifier, required if there is │ │ │ │ - * more than one matrix set in the layer capabilities. │ │ │ │ - * style - {String} The name of the style │ │ │ │ - * format - {String} Image format for the layer. Default is the first │ │ │ │ - * format returned in the GetCapabilities response. │ │ │ │ - * param - {Object} The dimensions values eg: {"Year": "2012"} │ │ │ │ - * │ │ │ │ + * evt - {Object} event object with a feature property │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer.WMTS>} A properly configured WMTS layer. Throws an │ │ │ │ - * error if an incomplete config is provided. Returns undefined if no │ │ │ │ - * layer could be created with the provided config. │ │ │ │ + * {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - createLayer: function(capabilities, config) { │ │ │ │ - var layer; │ │ │ │ - │ │ │ │ - // confirm required properties are supplied in config │ │ │ │ - if (!('layer' in config)) { │ │ │ │ - throw new Error("Missing property 'layer' in configuration."); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var contents = capabilities.contents; │ │ │ │ - │ │ │ │ - // find the layer definition with the given identifier │ │ │ │ - var layers = contents.layers; │ │ │ │ - var layerDef; │ │ │ │ - for (var i = 0, ii = contents.layers.length; i < ii; ++i) { │ │ │ │ - if (contents.layers[i].identifier === config.layer) { │ │ │ │ - layerDef = contents.layers[i]; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (!layerDef) { │ │ │ │ - throw new Error("Layer not found"); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var format = config.format; │ │ │ │ - if (!format && layerDef.formats && layerDef.formats.length) { │ │ │ │ - format = layerDef.formats[0]; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // find the matrixSet definition │ │ │ │ - var matrixSet; │ │ │ │ - if (config.matrixSet) { │ │ │ │ - matrixSet = contents.tileMatrixSets[config.matrixSet]; │ │ │ │ - } else if (layerDef.tileMatrixSetLinks.length >= 1) { │ │ │ │ - matrixSet = contents.tileMatrixSets[ │ │ │ │ - layerDef.tileMatrixSetLinks[0].tileMatrixSet]; │ │ │ │ - } │ │ │ │ - if (!matrixSet) { │ │ │ │ - throw new Error("matrixSet not found"); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // get the default style for the layer │ │ │ │ - var style; │ │ │ │ - for (var i = 0, ii = layerDef.styles.length; i < ii; ++i) { │ │ │ │ - style = layerDef.styles[i]; │ │ │ │ - if (style.isDefault) { │ │ │ │ - break; │ │ │ │ + getFeatureFromEvent: function(evt) { │ │ │ │ + var layers = this.layers; │ │ │ │ + var feature; │ │ │ │ + for (var i = 0; i < layers.length; i++) { │ │ │ │ + feature = layers[i].getFeatureFromEvent(evt); │ │ │ │ + if (feature) { │ │ │ │ + return feature; │ │ │ │ } │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - var requestEncoding = config.requestEncoding; │ │ │ │ - if (!requestEncoding) { │ │ │ │ - requestEncoding = "KVP"; │ │ │ │ - if (capabilities.operationsMetadata.GetTile.dcp.http) { │ │ │ │ - var http = capabilities.operationsMetadata.GetTile.dcp.http; │ │ │ │ - // Get first get method │ │ │ │ - if (http.get[0].constraints) { │ │ │ │ - var constraints = http.get[0].constraints; │ │ │ │ - var allowedValues = constraints.GetEncoding.allowedValues; │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); │ │ │ │ + this.collectRoots(); │ │ │ │ + map.events.register("changelayer", this, this.handleChangeLayer); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // The OGC documentation is not clear if we should use │ │ │ │ - // REST or RESTful, ArcGis use RESTful, │ │ │ │ - // and OpenLayers use REST. │ │ │ │ - if (!allowedValues.KVP && │ │ │ │ - (allowedValues.REST || allowedValues.RESTful)) { │ │ │ │ - requestEncoding = "REST"; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: removeMap │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + removeMap: function(map) { │ │ │ │ + map.events.unregister("changelayer", this, this.handleChangeLayer); │ │ │ │ + this.resetRoots(); │ │ │ │ + OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var dimensions = []; │ │ │ │ - var params = config.params || {}; │ │ │ │ - // to don't overwrite the changes in the applyDefaults │ │ │ │ - delete config.params; │ │ │ │ - for (var id = 0, ld = layerDef.dimensions.length; id < ld; id++) { │ │ │ │ - var dimension = layerDef.dimensions[id]; │ │ │ │ - dimensions.push(dimension.identifier); │ │ │ │ - if (!params.hasOwnProperty(dimension.identifier)) { │ │ │ │ - params[dimension.identifier] = dimension['default']; │ │ │ │ + /** │ │ │ │ + * Method: collectRoots │ │ │ │ + * Collects the root nodes of all layers this control is configured with │ │ │ │ + * and moveswien the nodes to this control's layer │ │ │ │ + */ │ │ │ │ + collectRoots: function() { │ │ │ │ + var layer; │ │ │ │ + // walk through all map layers, because we want to keep the order │ │ │ │ + for (var i = 0; i < this.map.layers.length; ++i) { │ │ │ │ + layer = this.map.layers[i]; │ │ │ │ + if (OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ + layer.renderer.moveRoot(this.renderer); │ │ │ │ } │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - var projection = config.projection || matrixSet.supportedCRS.replace( │ │ │ │ - /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, "$1:$3"); │ │ │ │ - var units = config.units || │ │ │ │ - (projection === "EPSG:4326" ? "degrees" : "m"); │ │ │ │ - │ │ │ │ - var resolutions = []; │ │ │ │ - for (var mid in matrixSet.matrixIds) { │ │ │ │ - if (matrixSet.matrixIds.hasOwnProperty(mid)) { │ │ │ │ - resolutions.push( │ │ │ │ - matrixSet.matrixIds[mid].scaleDenominator * 0.28E-3 / │ │ │ │ - OpenLayers.METERS_PER_INCH / │ │ │ │ - OpenLayers.INCHES_PER_UNIT[units]); │ │ │ │ + /** │ │ │ │ + * Method: resetRoots │ │ │ │ + * Resets the root nodes back into the layers they belong to. │ │ │ │ + */ │ │ │ │ + resetRoots: function() { │ │ │ │ + var layer; │ │ │ │ + for (var i = 0; i < this.layers.length; ++i) { │ │ │ │ + layer = this.layers[i]; │ │ │ │ + if (this.renderer && layer.renderer.getRenderLayerId() == this.id) { │ │ │ │ + this.renderer.moveRoot(layer.renderer); │ │ │ │ } │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - var url; │ │ │ │ - if (requestEncoding === "REST" && layerDef.resourceUrls) { │ │ │ │ - url = []; │ │ │ │ - var resourceUrls = layerDef.resourceUrls, │ │ │ │ - resourceUrl; │ │ │ │ - for (var t = 0, tt = layerDef.resourceUrls.length; t < tt; ++t) { │ │ │ │ - resourceUrl = layerDef.resourceUrls[t]; │ │ │ │ - if (resourceUrl.format === format && resourceUrl.resourceType === "tile") { │ │ │ │ - url.push(resourceUrl.template); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - var httpGet = capabilities.operationsMetadata.GetTile.dcp.http.get; │ │ │ │ - url = []; │ │ │ │ - var constraint; │ │ │ │ - for (var i = 0, ii = httpGet.length; i < ii; i++) { │ │ │ │ - constraint = httpGet[i].constraints; │ │ │ │ - if (!constraint || (constraint && constraint.GetEncoding.allowedValues[requestEncoding])) { │ │ │ │ - url.push(httpGet[i].url); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: handleChangeLayer │ │ │ │ + * Event handler for the map's changelayer event. We need to rebuild │ │ │ │ + * this container's layer dom if order of one of its layers changes. │ │ │ │ + * This handler is added with the setMap method, and removed with the │ │ │ │ + * removeMap method. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} │ │ │ │ + */ │ │ │ │ + handleChangeLayer: function(evt) { │ │ │ │ + var layer = evt.layer; │ │ │ │ + if (evt.property == "order" && │ │ │ │ + OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ + this.resetRoots(); │ │ │ │ + this.collectRoots(); │ │ │ │ } │ │ │ │ - │ │ │ │ - return new OpenLayers.Layer.WMTS( │ │ │ │ - OpenLayers.Util.applyDefaults(config, { │ │ │ │ - url: url, │ │ │ │ - requestEncoding: requestEncoding, │ │ │ │ - name: layerDef.title, │ │ │ │ - style: style.identifier, │ │ │ │ - format: format, │ │ │ │ - matrixIds: matrixSet.matrixIds, │ │ │ │ - matrixSet: matrixSet.identifier, │ │ │ │ - projection: projection, │ │ │ │ - units: units, │ │ │ │ - resolutions: config.isBaseLayer === false ? undefined : resolutions, │ │ │ │ - serverResolutions: resolutions, │ │ │ │ - tileFullExtent: matrixSet.bounds, │ │ │ │ - dimensions: dimensions, │ │ │ │ - params: params │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMTSCapabilities" │ │ │ │ - │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/CSWGetRecords.js │ │ │ │ + OpenLayers/Marker/Box.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.CSWGetRecords │ │ │ │ - * Default version is 2.0.2. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Format>} A CSWGetRecords format of the given version. │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.CSWGetRecords = function(options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults( │ │ │ │ - options, OpenLayers.Format.CSWGetRecords.DEFAULTS │ │ │ │ - ); │ │ │ │ - var cls = OpenLayers.Format.CSWGetRecords["v" + options.version.replace(/\./g, "_")]; │ │ │ │ - if (!cls) { │ │ │ │ - throw "Unsupported CSWGetRecords version: " + options.version; │ │ │ │ - } │ │ │ │ - return new cls(options); │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: DEFAULTS │ │ │ │ - * {Object} Default properties for the CSWGetRecords format. │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.CSWGetRecords.DEFAULTS = { │ │ │ │ - "version": "2.0.2" │ │ │ │ -}; │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/Context.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ + * @requires OpenLayers/Marker.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.Context │ │ │ │ - * Base class for both Format.WMC and Format.OWSContext │ │ │ │ + * Class: OpenLayers.Marker.Box │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ + * - <OpenLayers.Marker> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: layerOptions │ │ │ │ - * {Object} Default options for layers created by the parser. These │ │ │ │ - * options are overridden by the options which are read from the │ │ │ │ - * capabilities document. │ │ │ │ - */ │ │ │ │ - layerOptions: null, │ │ │ │ +OpenLayers.Marker.Box = OpenLayers.Class(OpenLayers.Marker, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: layerParams │ │ │ │ - * {Object} Default parameters for layers created by the parser. This │ │ │ │ - * can be used e.g. to override DEFAULT_PARAMS for │ │ │ │ - * OpenLayers.Layer.WMS. │ │ │ │ + /** │ │ │ │ + * Property: bounds │ │ │ │ + * {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - layerParams: null, │ │ │ │ + bounds: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.Context │ │ │ │ - * Create a new parser for Context documents. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + /** │ │ │ │ + * Property: div │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ + div: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read Context data from a string, and return an object with map │ │ │ │ - * properties and a list of layers. │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Marker.Box │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * options - {Object} The options object must contain a map property. If │ │ │ │ - * the map property is a string, it must be the id of a dom element │ │ │ │ - * where the new map will be placed. If the map property is an │ │ │ │ - * <OpenLayers.Map>, the layers from the context document will be added │ │ │ │ - * to the map. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Map>} A map based on the context. │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * borderColor - {String} │ │ │ │ + * borderWidth - {int} │ │ │ │ */ │ │ │ │ - read: function(data, options) { │ │ │ │ - var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this, │ │ │ │ - arguments); │ │ │ │ - var map; │ │ │ │ - if (options && options.map) { │ │ │ │ - this.context = context; │ │ │ │ - if (options.map instanceof OpenLayers.Map) { │ │ │ │ - map = this.mergeContextToMap(context, options.map); │ │ │ │ - } else { │ │ │ │ - var mapOptions = options.map; │ │ │ │ - if (OpenLayers.Util.isElement(mapOptions) || │ │ │ │ - typeof mapOptions == "string") { │ │ │ │ - // we assume mapOptions references a div │ │ │ │ - // element │ │ │ │ - mapOptions = { │ │ │ │ - div: mapOptions │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - map = this.contextToMap(context, mapOptions); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - // not documented as part of the API, provided as a non-API option │ │ │ │ - map = context; │ │ │ │ - } │ │ │ │ - return map; │ │ │ │ + initialize: function(bounds, borderColor, borderWidth) { │ │ │ │ + this.bounds = bounds; │ │ │ │ + this.div = OpenLayers.Util.createDiv(); │ │ │ │ + this.div.style.overflow = 'hidden'; │ │ │ │ + this.events = new OpenLayers.Events(this, this.div); │ │ │ │ + this.setBorder(borderColor, borderWidth); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getLayerFromContext │ │ │ │ - * Create a WMS layer from a layerContext object. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layerContext - {Object} An object representing a WMS layer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.WMS>} A WMS layer. │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - getLayerFromContext: function(layerContext) { │ │ │ │ - var i, len; │ │ │ │ - // fill initial options object from layerContext │ │ │ │ - var options = { │ │ │ │ - queryable: layerContext.queryable, //keep queryable for api compatibility │ │ │ │ - visibility: layerContext.visibility, │ │ │ │ - maxExtent: layerContext.maxExtent, │ │ │ │ - metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, { │ │ │ │ - styles: layerContext.styles, │ │ │ │ - formats: layerContext.formats, │ │ │ │ - "abstract": layerContext["abstract"], │ │ │ │ - dataURL: layerContext.dataURL │ │ │ │ - }), │ │ │ │ - numZoomLevels: layerContext.numZoomLevels, │ │ │ │ - units: layerContext.units, │ │ │ │ - isBaseLayer: layerContext.isBaseLayer, │ │ │ │ - opacity: layerContext.opacity, │ │ │ │ - displayInLayerSwitcher: layerContext.displayInLayerSwitcher, │ │ │ │ - singleTile: layerContext.singleTile, │ │ │ │ - tileSize: (layerContext.tileSize) ? │ │ │ │ - new OpenLayers.Size( │ │ │ │ - layerContext.tileSize.width, │ │ │ │ - layerContext.tileSize.height │ │ │ │ - ) : undefined, │ │ │ │ - minScale: layerContext.minScale || layerContext.maxScaleDenominator, │ │ │ │ - maxScale: layerContext.maxScale || layerContext.minScaleDenominator, │ │ │ │ - srs: layerContext.srs, │ │ │ │ - dimensions: layerContext.dimensions, │ │ │ │ - metadataURL: layerContext.metadataURL │ │ │ │ - }; │ │ │ │ - if (this.layerOptions) { │ │ │ │ - OpenLayers.Util.applyDefaults(options, this.layerOptions); │ │ │ │ - } │ │ │ │ + destroy: function() { │ │ │ │ │ │ │ │ - var params = { │ │ │ │ - layers: layerContext.name, │ │ │ │ - transparent: layerContext.transparent, │ │ │ │ - version: layerContext.version │ │ │ │ - }; │ │ │ │ - if (layerContext.formats && layerContext.formats.length > 0) { │ │ │ │ - // set default value for params if current attribute is not positionned │ │ │ │ - params.format = layerContext.formats[0].value; │ │ │ │ - for (i = 0, len = layerContext.formats.length; i < len; i++) { │ │ │ │ - var format = layerContext.formats[i]; │ │ │ │ - if (format.current == true) { │ │ │ │ - params.format = format.value; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (layerContext.styles && layerContext.styles.length > 0) { │ │ │ │ - for (i = 0, len = layerContext.styles.length; i < len; i++) { │ │ │ │ - var style = layerContext.styles[i]; │ │ │ │ - if (style.current == true) { │ │ │ │ - // three style types to consider │ │ │ │ - // 1) linked SLD │ │ │ │ - // 2) inline SLD │ │ │ │ - // 3) named style │ │ │ │ - if (style.href) { │ │ │ │ - params.sld = style.href; │ │ │ │ - } else if (style.body) { │ │ │ │ - params.sld_body = style.body; │ │ │ │ - } else { │ │ │ │ - params.styles = style.name; │ │ │ │ - } │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.layerParams) { │ │ │ │ - OpenLayers.Util.applyDefaults(params, this.layerParams); │ │ │ │ - } │ │ │ │ + this.bounds = null; │ │ │ │ + this.div = null; │ │ │ │ │ │ │ │ - var layer = null; │ │ │ │ - var service = layerContext.service; │ │ │ │ - if (service == OpenLayers.Format.Context.serviceTypes.WFS) { │ │ │ │ - options.strategies = [new OpenLayers.Strategy.BBOX()]; │ │ │ │ - options.protocol = new OpenLayers.Protocol.WFS({ │ │ │ │ - url: layerContext.url, │ │ │ │ - // since we do not know featureNS, let the protocol │ │ │ │ - // determine it automagically using featurePrefix │ │ │ │ - featurePrefix: layerContext.name.split(":")[0], │ │ │ │ - featureType: layerContext.name.split(":").pop() │ │ │ │ - }); │ │ │ │ - layer = new OpenLayers.Layer.Vector( │ │ │ │ - layerContext.title || layerContext.name, │ │ │ │ - options │ │ │ │ - ); │ │ │ │ - } else if (service == OpenLayers.Format.Context.serviceTypes.KML) { │ │ │ │ - // use a vector layer with an HTTP Protcol and a Fixed strategy │ │ │ │ - options.strategies = [new OpenLayers.Strategy.Fixed()]; │ │ │ │ - options.protocol = new OpenLayers.Protocol.HTTP({ │ │ │ │ - url: layerContext.url, │ │ │ │ - format: new OpenLayers.Format.KML() │ │ │ │ - }); │ │ │ │ - layer = new OpenLayers.Layer.Vector( │ │ │ │ - layerContext.title || layerContext.name, │ │ │ │ - options │ │ │ │ - ); │ │ │ │ - } else if (service == OpenLayers.Format.Context.serviceTypes.GML) { │ │ │ │ - // use a vector layer with a HTTP Protocol and a Fixed strategy │ │ │ │ - options.strategies = [new OpenLayers.Strategy.Fixed()]; │ │ │ │ - options.protocol = new OpenLayers.Protocol.HTTP({ │ │ │ │ - url: layerContext.url, │ │ │ │ - format: new OpenLayers.Format.GML() │ │ │ │ - }); │ │ │ │ - layer = new OpenLayers.Layer.Vector( │ │ │ │ - layerContext.title || layerContext.name, │ │ │ │ - options │ │ │ │ - ); │ │ │ │ - } else if (layerContext.features) { │ │ │ │ - // inline GML or KML features │ │ │ │ - layer = new OpenLayers.Layer.Vector( │ │ │ │ - layerContext.title || layerContext.name, │ │ │ │ - options │ │ │ │ - ); │ │ │ │ - layer.addFeatures(layerContext.features); │ │ │ │ - } else if (layerContext.categoryLayer !== true) { │ │ │ │ - layer = new OpenLayers.Layer.WMS( │ │ │ │ - layerContext.title || layerContext.name, │ │ │ │ - layerContext.url, │ │ │ │ - params, │ │ │ │ - options │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - return layer; │ │ │ │ + OpenLayers.Marker.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getLayersFromContext │ │ │ │ - * Create an array of layers from an array of layerContext objects. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: setBorder │ │ │ │ + * Allow the user to change the box's color and border width │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * layersContext - {Array(Object)} An array of objects representing layers. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array(<OpenLayers.Layer>)} An array of layers. │ │ │ │ + * color - {String} Default is "red" │ │ │ │ + * width - {int} Default is 2 │ │ │ │ */ │ │ │ │ - getLayersFromContext: function(layersContext) { │ │ │ │ - var layers = []; │ │ │ │ - for (var i = 0, len = layersContext.length; i < len; i++) { │ │ │ │ - var layer = this.getLayerFromContext(layersContext[i]); │ │ │ │ - if (layer !== null) { │ │ │ │ - layers.push(layer); │ │ │ │ - } │ │ │ │ + setBorder: function(color, width) { │ │ │ │ + if (!color) { │ │ │ │ + color = "red"; │ │ │ │ } │ │ │ │ - return layers; │ │ │ │ + if (!width) { │ │ │ │ + width = 2; │ │ │ │ + } │ │ │ │ + this.div.style.border = width + "px solid " + color; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: contextToMap │ │ │ │ - * Create a map given a context object. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * context - {Object} The context object. │ │ │ │ - * options - {Object} Default map options. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Map>} A map based on the context object. │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ + * sz - {<OpenLayers.Size>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A new DOM Image with this marker's icon set at the │ │ │ │ + * location passed-in │ │ │ │ */ │ │ │ │ - contextToMap: function(context, options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults({ │ │ │ │ - maxExtent: context.maxExtent, │ │ │ │ - projection: context.projection, │ │ │ │ - units: context.units │ │ │ │ - }, options); │ │ │ │ - │ │ │ │ - if (options.maxExtent) { │ │ │ │ - options.maxResolution = │ │ │ │ - options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var metadata = { │ │ │ │ - contactInformation: context.contactInformation, │ │ │ │ - "abstract": context["abstract"], │ │ │ │ - keywords: context.keywords, │ │ │ │ - logo: context.logo, │ │ │ │ - descriptionURL: context.descriptionURL │ │ │ │ - }; │ │ │ │ - │ │ │ │ - options.metadata = metadata; │ │ │ │ - │ │ │ │ - var map = new OpenLayers.Map(options); │ │ │ │ - map.addLayers(this.getLayersFromContext(context.layersContext)); │ │ │ │ - map.setCenter( │ │ │ │ - context.bounds.getCenterLonLat(), │ │ │ │ - map.getZoomForExtent(context.bounds, true) │ │ │ │ - ); │ │ │ │ - return map; │ │ │ │ + draw: function(px, sz) { │ │ │ │ + OpenLayers.Util.modifyDOMElement(this.div, null, px, sz); │ │ │ │ + return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: mergeContextToMap │ │ │ │ - * Add layers from a context object to a map. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * context - {Object} The context object. │ │ │ │ - * map - {<OpenLayers.Map>} The map. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Map>} The same map with layers added. │ │ │ │ + * Method: onScreen │ │ │ │ + * │ │ │ │ + * Rreturn: │ │ │ │ + * {Boolean} Whether or not the marker is currently visible on screen. │ │ │ │ */ │ │ │ │ - mergeContextToMap: function(context, map) { │ │ │ │ - map.addLayers(this.getLayersFromContext(context.layersContext)); │ │ │ │ - return map; │ │ │ │ + onScreen: function() { │ │ │ │ + var onScreen = false; │ │ │ │ + if (this.map) { │ │ │ │ + var screenBounds = this.map.getExtent(); │ │ │ │ + onScreen = screenBounds.containsBounds(this.bounds, true, true); │ │ │ │ + } │ │ │ │ + return onScreen; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Write a context document given a map. │ │ │ │ - * │ │ │ │ + * Method: display │ │ │ │ + * Hide or show the icon │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * obj - {<OpenLayers.Map> | Object} A map or context object. │ │ │ │ - * options - {Object} Optional configuration object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A context document string. │ │ │ │ + * display - {Boolean} │ │ │ │ */ │ │ │ │ - write: function(obj, options) { │ │ │ │ - obj = this.toContext(obj); │ │ │ │ - return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this, │ │ │ │ - arguments); │ │ │ │ + display: function(display) { │ │ │ │ + this.div.style.display = (display) ? "" : "none"; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.Context" │ │ │ │ + CLASS_NAME: "OpenLayers.Marker.Box" │ │ │ │ }); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Format.Context.serviceTypes │ │ │ │ - * Enumeration for service types │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.Context.serviceTypes = { │ │ │ │ - "WMS": "urn:ogc:serviceType:WMS", │ │ │ │ - "WFS": "urn:ogc:serviceType:WFS", │ │ │ │ - "WCS": "urn:ogc:serviceType:WCS", │ │ │ │ - "GML": "urn:ogc:serviceType:GML", │ │ │ │ - "SLD": "urn:ogc:serviceType:SLD", │ │ │ │ - "FES": "urn:ogc:serviceType:FES", │ │ │ │ - "KML": "urn:ogc:serviceType:KML" │ │ │ │ -}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/SOSGetFeatureOfInterest.js │ │ │ │ + OpenLayers/Popup/Anchored.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Format/GML/v3.js │ │ │ │ + * @requires OpenLayers/Popup.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.SOSGetFeatureOfInterest │ │ │ │ - * Read and write SOS GetFeatureOfInterest. This is used to get to │ │ │ │ - * the location of the features (stations). The stations can have 1 or more │ │ │ │ - * sensors. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Popup.Anchored │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Popup> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.SOSGetFeatureOfInterest = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.XML, { │ │ │ │ +OpenLayers.Popup.Anchored = │ │ │ │ + OpenLayers.Class(OpenLayers.Popup, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: relativePosition │ │ │ │ + * {String} Relative position of the popup ("br", "tr", "tl" or "bl"). │ │ │ │ + */ │ │ │ │ + relativePosition: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: VERSION │ │ │ │ - * {String} 1.0.0 │ │ │ │ + * APIProperty: keepInMap │ │ │ │ + * {Boolean} If panMapIfOutOfView is false, and this property is true, │ │ │ │ + * contrain the popup such that it always fits in the available map │ │ │ │ + * space. By default, this is set. If you are creating popups that are │ │ │ │ + * near map edges and not allowing pannning, and especially if you have │ │ │ │ + * a popup which has a fixedRelativePosition, setting this to false may │ │ │ │ + * be a smart thing to do. │ │ │ │ + * │ │ │ │ + * For anchored popups, default is true, since subclasses will │ │ │ │ + * usually want this functionality. │ │ │ │ */ │ │ │ │ - VERSION: "1.0.0", │ │ │ │ + keepInMap: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + * Property: anchor │ │ │ │ + * {Object} Object to which we'll anchor the popup. Must expose a │ │ │ │ + * 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>). │ │ │ │ */ │ │ │ │ - namespaces: { │ │ │ │ - sos: "http://www.opengis.net/sos/1.0", │ │ │ │ - gml: "http://www.opengis.net/gml", │ │ │ │ - sa: "http://www.opengis.net/sampling/1.0", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + anchor: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Popup.Anchored │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * id - {String} │ │ │ │ + * lonlat - {<OpenLayers.LonLat>} │ │ │ │ + * contentSize - {<OpenLayers.Size>} │ │ │ │ + * contentHTML - {String} │ │ │ │ + * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size> │ │ │ │ + * and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>). │ │ │ │ + * closeBox - {Boolean} │ │ │ │ + * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ + */ │ │ │ │ + initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, │ │ │ │ + closeBoxCallback) { │ │ │ │ + var newArguments = [ │ │ │ │ + id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback │ │ │ │ + ]; │ │ │ │ + OpenLayers.Popup.prototype.initialize.apply(this, newArguments); │ │ │ │ + │ │ │ │ + this.anchor = (anchor != null) ? anchor : │ │ │ │ + { │ │ │ │ + size: new OpenLayers.Size(0, 0), │ │ │ │ + offset: new OpenLayers.Pixel(0, 0) │ │ │ │ + }; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} Schema location │ │ │ │ + * APIMethod: destroy │ │ │ │ */ │ │ │ │ - schemaLocation: "http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosAll.xsd", │ │ │ │ + destroy: function() { │ │ │ │ + this.anchor = null; │ │ │ │ + this.relativePosition = null; │ │ │ │ + │ │ │ │ + OpenLayers.Popup.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ + * APIMethod: show │ │ │ │ + * Overridden from Popup since user might hide popup and then show() it │ │ │ │ + * in a new location (meaning we might want to update the relative │ │ │ │ + * position on the show) │ │ │ │ */ │ │ │ │ - defaultPrefix: "sos", │ │ │ │ + show: function() { │ │ │ │ + this.updatePosition(); │ │ │ │ + OpenLayers.Popup.prototype.show.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: regExes │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ + * Method: moveTo │ │ │ │ + * Since the popup is moving to a new px, it might need also to be moved │ │ │ │ + * relative to where the marker is. We first calculate the new │ │ │ │ + * relativePosition, and then we calculate the new px where we will │ │ │ │ + * put the popup, based on the new relative position. │ │ │ │ + * │ │ │ │ + * If the relativePosition has changed, we must also call │ │ │ │ + * updateRelativePosition() to make any visual changes to the popup │ │ │ │ + * which are associated with putting it in a new relativePosition. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ */ │ │ │ │ - regExes: { │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ + moveTo: function(px) { │ │ │ │ + var oldRelativePosition = this.relativePosition; │ │ │ │ + this.relativePosition = this.calculateRelativePosition(px); │ │ │ │ + │ │ │ │ + OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px)); │ │ │ │ + │ │ │ │ + //if this move has caused the popup to change its relative position, │ │ │ │ + // we need to make the appropriate cosmetic changes. │ │ │ │ + if (this.relativePosition != oldRelativePosition) { │ │ │ │ + this.updateRelativePosition(); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.SOSGetFeatureOfInterest │ │ │ │ - * │ │ │ │ + * APIMethod: setSize │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * contentSize - {<OpenLayers.Size>} the new size for the popup's │ │ │ │ + * contents div (in pixels). │ │ │ │ */ │ │ │ │ + setSize: function(contentSize) { │ │ │ │ + OpenLayers.Popup.prototype.setSize.apply(this, arguments); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Parse a GetFeatureOfInterest response and return an array of features │ │ │ │ + if ((this.lonlat) && (this.map)) { │ │ │ │ + var px = this.map.getLayerPxFromLonLat(this.lonlat); │ │ │ │ + this.moveTo(px); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: calculateRelativePosition │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ * Returns: │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} An array of features. │ │ │ │ + * {String} The relative position ("br" "tr" "tl" "bl") at which the popup │ │ │ │ + * should be placed. │ │ │ │ */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - } │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ - data = data.documentElement; │ │ │ │ - } │ │ │ │ + calculateRelativePosition: function(px) { │ │ │ │ + var lonlat = this.map.getLonLatFromLayerPx(px); │ │ │ │ │ │ │ │ - var info = { │ │ │ │ - features: [] │ │ │ │ - }; │ │ │ │ - this.readNode(data, info); │ │ │ │ + var extent = this.map.getExtent(); │ │ │ │ + var quadrant = extent.determineQuadrant(lonlat); │ │ │ │ │ │ │ │ - var features = []; │ │ │ │ - for (var i = 0, len = info.features.length; i < len; i++) { │ │ │ │ - var container = info.features[i]; │ │ │ │ - // reproject features if needed │ │ │ │ - if (this.internalProjection && this.externalProjection && │ │ │ │ - container.components[0]) { │ │ │ │ - container.components[0].transform( │ │ │ │ - this.externalProjection, this.internalProjection │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - var feature = new OpenLayers.Feature.Vector( │ │ │ │ - container.components[0], container.attributes); │ │ │ │ - features.push(feature); │ │ │ │ - } │ │ │ │ - return features; │ │ │ │ + return OpenLayers.Bounds.oppositeQuadrant(quadrant); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: updateRelativePosition │ │ │ │ + * The popup has been moved to a new relative location, so we may want to │ │ │ │ + * make some cosmetic adjustments to it. │ │ │ │ + * │ │ │ │ + * Note that in the classic Anchored popup, there is nothing to do │ │ │ │ + * here, since the popup looks exactly the same in all four positions. │ │ │ │ + * Subclasses such as Framed, however, will want to do something │ │ │ │ + * special here. │ │ │ │ */ │ │ │ │ - readers: { │ │ │ │ - "sa": { │ │ │ │ - "SamplingPoint": function(node, obj) { │ │ │ │ - // sampling point can also be without a featureMember if │ │ │ │ - // there is only 1 │ │ │ │ - if (!obj.attributes) { │ │ │ │ - var feature = { │ │ │ │ - attributes: {} │ │ │ │ - }; │ │ │ │ - obj.features.push(feature); │ │ │ │ - obj = feature; │ │ │ │ - } │ │ │ │ - obj.attributes.id = this.getAttributeNS(node, │ │ │ │ - this.namespaces.gml, "id"); │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "position": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ - "FeatureCollection": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "featureMember": function(node, obj) { │ │ │ │ - var feature = { │ │ │ │ - attributes: {} │ │ │ │ - }; │ │ │ │ - obj.features.push(feature); │ │ │ │ - this.readChildNodes(node, feature); │ │ │ │ - }, │ │ │ │ - "name": function(node, obj) { │ │ │ │ - obj.attributes.name = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "pos": function(node, obj) { │ │ │ │ - // we need to parse the srsName to get to the │ │ │ │ - // externalProjection, that's why we cannot use │ │ │ │ - // GML v3 for this │ │ │ │ - if (!this.externalProjection) { │ │ │ │ - this.externalProjection = new OpenLayers.Projection( │ │ │ │ - node.getAttribute("srsName")); │ │ │ │ - } │ │ │ │ - OpenLayers.Format.GML.v3.prototype.readers.gml.pos.apply( │ │ │ │ - this, [node, obj]); │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.GML.v3.prototype.readers.gml) │ │ │ │ + updateRelativePosition: function() { │ │ │ │ + //to be overridden by subclasses │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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: calculateNewPx │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Pixel>} The the new px position of the popup on the screen │ │ │ │ + * relative to the passed-in px. │ │ │ │ */ │ │ │ │ - writers: { │ │ │ │ - "sos": { │ │ │ │ - "GetFeatureOfInterest": function(options) { │ │ │ │ - var node = this.createElementNSPlus("GetFeatureOfInterest", { │ │ │ │ - attributes: { │ │ │ │ - version: this.VERSION, │ │ │ │ - service: 'SOS', │ │ │ │ - "xsi:schemaLocation": this.schemaLocation │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - for (var i = 0, len = options.fois.length; i < len; i++) { │ │ │ │ - this.writeNode("FeatureOfInterestId", { │ │ │ │ - foi: options.fois[i] │ │ │ │ - }, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "FeatureOfInterestId": function(options) { │ │ │ │ - var node = this.createElementNSPlus("FeatureOfInterestId", { │ │ │ │ - value: options.foi │ │ │ │ - }); │ │ │ │ - return node; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + calculateNewPx: function(px) { │ │ │ │ + var newPx = px.offset(this.anchor.offset); │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.SOSGetFeatureOfInterest" │ │ │ │ + //use contentSize if size is not already set │ │ │ │ + var size = this.size || this.contentSize; │ │ │ │ + │ │ │ │ + var top = (this.relativePosition.charAt(0) == 't'); │ │ │ │ + newPx.y += (top) ? -size.h : this.anchor.size.h; │ │ │ │ + │ │ │ │ + var left = (this.relativePosition.charAt(1) == 'l'); │ │ │ │ + newPx.x += (left) ? -size.w : this.anchor.size.w; │ │ │ │ + │ │ │ │ + return newPx; │ │ │ │ + }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Popup.Anchored" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/SOSGetObservation.js │ │ │ │ + OpenLayers/Popup/Framed.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Format/SOSGetFeatureOfInterest.js │ │ │ │ + * @requires OpenLayers/Popup/Anchored.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.SOSGetObservation │ │ │ │ - * Read and write SOS GetObersation (to get the actual values from a sensor) │ │ │ │ - * version 1.0.0 │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Popup.Framed │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Popup.Anchored> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.SOSGetObservation = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ +OpenLayers.Popup.Framed = │ │ │ │ + OpenLayers.Class(OpenLayers.Popup.Anchored, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ - */ │ │ │ │ - namespaces: { │ │ │ │ - ows: "http://www.opengis.net/ows", │ │ │ │ - gml: "http://www.opengis.net/gml", │ │ │ │ - sos: "http://www.opengis.net/sos/1.0", │ │ │ │ - ogc: "http://www.opengis.net/ogc", │ │ │ │ - om: "http://www.opengis.net/om/1.0", │ │ │ │ - sa: "http://www.opengis.net/sampling/1.0", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: imageSrc │ │ │ │ + * {String} location of the image to be used as the popup frame │ │ │ │ + */ │ │ │ │ + imageSrc: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: regExes │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ - */ │ │ │ │ - regExes: { │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: imageSize │ │ │ │ + * {<OpenLayers.Size>} Size (measured in pixels) of the image located │ │ │ │ + * by the 'imageSrc' property. │ │ │ │ + */ │ │ │ │ + imageSize: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: VERSION │ │ │ │ - * {String} 1.0.0 │ │ │ │ - */ │ │ │ │ - VERSION: "1.0.0", │ │ │ │ + /** │ │ │ │ + * APIProperty: isAlphaImage │ │ │ │ + * {Boolean} The image has some alpha and thus needs to use the alpha │ │ │ │ + * image hack. Note that setting this to true will have no noticeable │ │ │ │ + * effect in FF or IE7 browsers, but will all but crush the ie6 │ │ │ │ + * browser. │ │ │ │ + * Default is false. │ │ │ │ + */ │ │ │ │ + isAlphaImage: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} Schema location │ │ │ │ - */ │ │ │ │ - schemaLocation: "http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosGetObservation.xsd", │ │ │ │ + /** │ │ │ │ + * Property: positionBlocks │ │ │ │ + * {Object} Hash of different position blocks (Object/Hashs). Each block │ │ │ │ + * will be keyed by a two-character 'relativePosition' │ │ │ │ + * code string (ie "tl", "tr", "bl", "br"). Block properties are │ │ │ │ + * 'offset', 'padding' (self-explanatory), and finally the 'blocks' │ │ │ │ + * parameter, which is an array of the block objects. │ │ │ │ + * │ │ │ │ + * Each block object must have 'size', 'anchor', and 'position' │ │ │ │ + * properties. │ │ │ │ + * │ │ │ │ + * Note that positionBlocks should never be modified at runtime. │ │ │ │ + */ │ │ │ │ + positionBlocks: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ - */ │ │ │ │ - defaultPrefix: "sos", │ │ │ │ + /** │ │ │ │ + * Property: blocks │ │ │ │ + * {Array[Object]} Array of objects, each of which is one "block" of the │ │ │ │ + * popup. Each block has a 'div' and an 'image' property, both of │ │ │ │ + * which are DOMElements, and the latter of which is appended to the │ │ │ │ + * former. These are reused as the popup goes changing positions for │ │ │ │ + * great economy and elegance. │ │ │ │ + */ │ │ │ │ + blocks: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.SOSGetObservation │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * APIProperty: fixedRelativePosition │ │ │ │ + * {Boolean} We want the framed popup to work dynamically placed relative │ │ │ │ + * to its anchor but also in just one fixed position. A well designed │ │ │ │ + * framed popup will have the pixels and logic to display itself in │ │ │ │ + * any of the four relative positions, but (understandably), this will │ │ │ │ + * not be the case for all of them. By setting this property to 'true', │ │ │ │ + * framed popup will not recalculate for the best placement each time │ │ │ │ + * it's open, but will always open the same way. │ │ │ │ + * Note that if this is set to true, it is generally advisable to also │ │ │ │ + * set the 'panIntoView' property to true so that the popup can be │ │ │ │ + * scrolled into view (since it will often be offscreen on open) │ │ │ │ + * Default is false. │ │ │ │ + */ │ │ │ │ + fixedRelativePosition: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: read │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object containing the measurements │ │ │ │ - */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - } │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ - data = data.documentElement; │ │ │ │ - } │ │ │ │ - var info = { │ │ │ │ - measurements: [], │ │ │ │ - observations: [] │ │ │ │ - }; │ │ │ │ - this.readNode(data, info); │ │ │ │ - return info; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Popup.Framed │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * id - {String} │ │ │ │ + * lonlat - {<OpenLayers.LonLat>} │ │ │ │ + * contentSize - {<OpenLayers.Size>} │ │ │ │ + * contentHTML - {String} │ │ │ │ + * anchor - {Object} Object to which we'll anchor the popup. Must expose │ │ │ │ + * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) │ │ │ │ + * (Note that this is generally an <OpenLayers.Icon>). │ │ │ │ + * closeBox - {Boolean} │ │ │ │ + * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ + */ │ │ │ │ + initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, │ │ │ │ + closeBoxCallback) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: write │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} An SOS GetObservation request XML string. │ │ │ │ - */ │ │ │ │ - write: function(options) { │ │ │ │ - var node = this.writeNode("sos:GetObservation", options); │ │ │ │ - node.setAttribute("xmlns:om", this.namespaces.om); │ │ │ │ - node.setAttribute("xmlns:ogc", this.namespaces.ogc); │ │ │ │ - this.setAttributeNS( │ │ │ │ - node, this.namespaces.xsi, │ │ │ │ - "xsi:schemaLocation", this.schemaLocation │ │ │ │ - ); │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ - }, │ │ │ │ + OpenLayers.Popup.Anchored.prototype.initialize.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: { │ │ │ │ - "om": { │ │ │ │ - "ObservationCollection": function(node, obj) { │ │ │ │ - obj.id = this.getAttributeNS(node, this.namespaces.gml, "id"); │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "member": function(node, observationCollection) { │ │ │ │ - this.readChildNodes(node, observationCollection); │ │ │ │ - }, │ │ │ │ - "Measurement": function(node, observationCollection) { │ │ │ │ - var measurement = {}; │ │ │ │ - observationCollection.measurements.push(measurement); │ │ │ │ - this.readChildNodes(node, measurement); │ │ │ │ - }, │ │ │ │ - "Observation": function(node, observationCollection) { │ │ │ │ - var observation = {}; │ │ │ │ - observationCollection.observations.push(observation); │ │ │ │ - this.readChildNodes(node, observation); │ │ │ │ - }, │ │ │ │ - "samplingTime": function(node, measurement) { │ │ │ │ - var samplingTime = {}; │ │ │ │ - measurement.samplingTime = samplingTime; │ │ │ │ - this.readChildNodes(node, samplingTime); │ │ │ │ - }, │ │ │ │ - "observedProperty": function(node, measurement) { │ │ │ │ - measurement.observedProperty = │ │ │ │ - this.getAttributeNS(node, this.namespaces.xlink, "href"); │ │ │ │ - this.readChildNodes(node, measurement); │ │ │ │ - }, │ │ │ │ - "procedure": function(node, measurement) { │ │ │ │ - measurement.procedure = │ │ │ │ - this.getAttributeNS(node, this.namespaces.xlink, "href"); │ │ │ │ - this.readChildNodes(node, measurement); │ │ │ │ - }, │ │ │ │ - "featureOfInterest": function(node, observation) { │ │ │ │ - var foi = { │ │ │ │ - features: [] │ │ │ │ - }; │ │ │ │ - observation.fois = []; │ │ │ │ - observation.fois.push(foi); │ │ │ │ - this.readChildNodes(node, foi); │ │ │ │ - // postprocessing to get actual features │ │ │ │ - var features = []; │ │ │ │ - for (var i = 0, len = foi.features.length; i < len; i++) { │ │ │ │ - var feature = foi.features[i]; │ │ │ │ - features.push(new OpenLayers.Feature.Vector( │ │ │ │ - feature.components[0], feature.attributes)); │ │ │ │ - } │ │ │ │ - foi.features = features; │ │ │ │ - }, │ │ │ │ - "result": function(node, measurement) { │ │ │ │ - var result = {}; │ │ │ │ - measurement.result = result; │ │ │ │ - if (this.getChildValue(node) !== '') { │ │ │ │ - result.value = this.getChildValue(node); │ │ │ │ - result.uom = node.getAttribute("uom"); │ │ │ │ - } else { │ │ │ │ - this.readChildNodes(node, result); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "sa": OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.sa, │ │ │ │ - "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ - "TimeInstant": function(node, samplingTime) { │ │ │ │ - var timeInstant = {}; │ │ │ │ - samplingTime.timeInstant = timeInstant; │ │ │ │ - this.readChildNodes(node, timeInstant); │ │ │ │ - }, │ │ │ │ - "timePosition": function(node, timeInstant) { │ │ │ │ - timeInstant.timePosition = this.getChildValue(node); │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.gml) │ │ │ │ - }, │ │ │ │ + if (this.fixedRelativePosition) { │ │ │ │ + //based on our decided relativePostion, set the current padding │ │ │ │ + // this keeps us from getting into trouble │ │ │ │ + this.updateRelativePosition(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: writers │ │ │ │ - * As a compliment to the readers property, this structure contains public │ │ │ │ - * writing functions grouped by namespace alias and named like the │ │ │ │ - * node names they produce. │ │ │ │ - */ │ │ │ │ - writers: { │ │ │ │ - "sos": { │ │ │ │ - "GetObservation": function(options) { │ │ │ │ - var node = this.createElementNSPlus("GetObservation", { │ │ │ │ - attributes: { │ │ │ │ - version: this.VERSION, │ │ │ │ - service: 'SOS' │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - this.writeNode("offering", options, node); │ │ │ │ - if (options.eventTime) { │ │ │ │ - this.writeNode("eventTime", options, node); │ │ │ │ - } │ │ │ │ - for (var procedure in options.procedures) { │ │ │ │ - this.writeNode("procedure", options.procedures[procedure], node); │ │ │ │ - } │ │ │ │ - for (var observedProperty in options.observedProperties) { │ │ │ │ - this.writeNode("observedProperty", options.observedProperties[observedProperty], node); │ │ │ │ - } │ │ │ │ - if (options.foi) { │ │ │ │ - this.writeNode("featureOfInterest", options.foi, node); │ │ │ │ - } │ │ │ │ - this.writeNode("responseFormat", options, node); │ │ │ │ - if (options.resultModel) { │ │ │ │ - this.writeNode("resultModel", options, node); │ │ │ │ - } │ │ │ │ - if (options.responseMode) { │ │ │ │ - this.writeNode("responseMode", options, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "featureOfInterest": function(foi) { │ │ │ │ - var node = this.createElementNSPlus("featureOfInterest"); │ │ │ │ - this.writeNode("ObjectID", foi.objectId, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "ObjectID": function(options) { │ │ │ │ - return this.createElementNSPlus("ObjectID", { │ │ │ │ - value: options │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "responseFormat": function(options) { │ │ │ │ - return this.createElementNSPlus("responseFormat", { │ │ │ │ - value: options.responseFormat │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "procedure": function(procedure) { │ │ │ │ - return this.createElementNSPlus("procedure", { │ │ │ │ - value: procedure │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "offering": function(options) { │ │ │ │ - return this.createElementNSPlus("offering", { │ │ │ │ - value: options.offering │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "observedProperty": function(observedProperty) { │ │ │ │ - return this.createElementNSPlus("observedProperty", { │ │ │ │ - value: observedProperty │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "eventTime": function(options) { │ │ │ │ - var node = this.createElementNSPlus("eventTime"); │ │ │ │ - if (options.eventTime === 'latest') { │ │ │ │ - this.writeNode("ogc:TM_Equals", options, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "resultModel": function(options) { │ │ │ │ - return this.createElementNSPlus("resultModel", { │ │ │ │ - value: options.resultModel │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "responseMode": function(options) { │ │ │ │ - return this.createElementNSPlus("responseMode", { │ │ │ │ - value: options.responseMode │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "ogc": { │ │ │ │ - "TM_Equals": function(options) { │ │ │ │ - var node = this.createElementNSPlus("ogc:TM_Equals"); │ │ │ │ - this.writeNode("ogc:PropertyName", { │ │ │ │ - property: "urn:ogc:data:time:iso8601" │ │ │ │ - }, node); │ │ │ │ - if (options.eventTime === 'latest') { │ │ │ │ - this.writeNode("gml:TimeInstant", { │ │ │ │ - value: 'latest' │ │ │ │ - }, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "PropertyName": function(options) { │ │ │ │ - return this.createElementNSPlus("ogc:PropertyName", { │ │ │ │ - value: options.property │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "gml": { │ │ │ │ - "TimeInstant": function(options) { │ │ │ │ - var node = this.createElementNSPlus("gml:TimeInstant"); │ │ │ │ - this.writeNode("gml:timePosition", options, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "timePosition": function(options) { │ │ │ │ - var node = this.createElementNSPlus("gml:timePosition", { │ │ │ │ - value: options.value │ │ │ │ - }); │ │ │ │ - return node; │ │ │ │ + //make calculateRelativePosition always return the specified │ │ │ │ + // fixed position. │ │ │ │ + this.calculateRelativePosition = function(px) { │ │ │ │ + return this.relativePosition; │ │ │ │ + }; │ │ │ │ } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.SOSGetObservation" │ │ │ │ - │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WMSCapabilities.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WMSCapabilities │ │ │ │ - * Read WMS Capabilities. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WMSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ - * {String} Version number to assume if none found. Default is "1.1.1". │ │ │ │ - */ │ │ │ │ - defaultVersion: "1.1.1", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: profile │ │ │ │ - * {String} If provided, use a custom profile. │ │ │ │ - * │ │ │ │ - * Currently supported profiles: │ │ │ │ - * - WMSC - parses vendor specific capabilities for WMS-C. │ │ │ │ - */ │ │ │ │ - profile: null, │ │ │ │ + this.contentDiv.style.position = "absolute"; │ │ │ │ + this.contentDiv.style.zIndex = 1; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WMSCapabilities │ │ │ │ - * Create a new parser for WMS capabilities. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + if (closeBox) { │ │ │ │ + this.closeDiv.style.zIndex = 1; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read capabilities data from a string, and return a list of layers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} List of named layers. │ │ │ │ - */ │ │ │ │ + this.groupDiv.style.position = "absolute"; │ │ │ │ + this.groupDiv.style.top = "0px"; │ │ │ │ + this.groupDiv.style.left = "0px"; │ │ │ │ + this.groupDiv.style.height = "100%"; │ │ │ │ + this.groupDiv.style.width = "100%"; │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMSCapabilities" │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.imageSrc = null; │ │ │ │ + this.imageSize = null; │ │ │ │ + this.isAlphaImage = null; │ │ │ │ │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/CQL.js │ │ │ │ - ====================================================================== */ │ │ │ │ + this.fixedRelativePosition = false; │ │ │ │ + this.positionBlocks = 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. */ │ │ │ │ + //remove our blocks │ │ │ │ + for (var i = 0; i < this.blocks.length; i++) { │ │ │ │ + var block = this.blocks[i]; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WKT.js │ │ │ │ - * @requires OpenLayers/Filter/Comparison.js │ │ │ │ - * @requires OpenLayers/Filter/Logical.js │ │ │ │ - * @requires OpenLayers/Filter/Spatial.js │ │ │ │ - */ │ │ │ │ + if (block.image) { │ │ │ │ + block.div.removeChild(block.image); │ │ │ │ + } │ │ │ │ + block.image = null; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.CQL │ │ │ │ - * Read CQL strings to get <OpenLayers.Filter> objects. Write │ │ │ │ - * <OpenLayers.Filter> objects to get CQL strings. Create a new parser with │ │ │ │ - * the <OpenLayers.Format.CQL> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.CQL = (function() { │ │ │ │ + if (block.div) { │ │ │ │ + this.groupDiv.removeChild(block.div); │ │ │ │ + } │ │ │ │ + block.div = null; │ │ │ │ + } │ │ │ │ + this.blocks = null; │ │ │ │ │ │ │ │ - var tokens = [ │ │ │ │ - "PROPERTY", "COMPARISON", "VALUE", "LOGICAL" │ │ │ │ - ], │ │ │ │ + OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - patterns = { │ │ │ │ - PROPERTY: /^[_a-zA-Z]\w*/, │ │ │ │ - COMPARISON: /^(=|<>|<=|<|>=|>|LIKE)/i, │ │ │ │ - IS_NULL: /^IS NULL/i, │ │ │ │ - COMMA: /^,/, │ │ │ │ - LOGICAL: /^(AND|OR)/i, │ │ │ │ - VALUE: /^('([^']|'')*'|\d+(\.\d*)?|\.\d+)/, │ │ │ │ - LPAREN: /^\(/, │ │ │ │ - RPAREN: /^\)/, │ │ │ │ - SPATIAL: /^(BBOX|INTERSECTS|DWITHIN|WITHIN|CONTAINS)/i, │ │ │ │ - NOT: /^NOT/i, │ │ │ │ - BETWEEN: /^BETWEEN/i, │ │ │ │ - GEOMETRY: function(text) { │ │ │ │ - var type = /^(POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)/.exec(text); │ │ │ │ - if (type) { │ │ │ │ - var len = text.length; │ │ │ │ - var idx = text.indexOf("(", type[0].length); │ │ │ │ - if (idx > -1) { │ │ │ │ - var depth = 1; │ │ │ │ - while (idx < len && depth > 0) { │ │ │ │ - idx++; │ │ │ │ - switch (text.charAt(idx)) { │ │ │ │ - case '(': │ │ │ │ - depth++; │ │ │ │ - break; │ │ │ │ - case ')': │ │ │ │ - depth--; │ │ │ │ - break; │ │ │ │ - default: │ │ │ │ - // in default case, do nothing │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return [text.substr(0, idx + 1)]; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - END: /^$/ │ │ │ │ + /** │ │ │ │ + * APIMethod: setBackgroundColor │ │ │ │ + */ │ │ │ │ + setBackgroundColor: function(color) { │ │ │ │ + //does nothing since the framed popup's entire scheme is based on a │ │ │ │ + // an image -- changing the background color makes no sense. │ │ │ │ }, │ │ │ │ │ │ │ │ - follows = { │ │ │ │ - LPAREN: ['GEOMETRY', 'SPATIAL', 'PROPERTY', 'VALUE', 'LPAREN'], │ │ │ │ - RPAREN: ['NOT', 'LOGICAL', 'END', 'RPAREN'], │ │ │ │ - PROPERTY: ['COMPARISON', 'BETWEEN', 'COMMA', 'IS_NULL'], │ │ │ │ - BETWEEN: ['VALUE'], │ │ │ │ - IS_NULL: ['END'], │ │ │ │ - COMPARISON: ['VALUE'], │ │ │ │ - COMMA: ['GEOMETRY', 'VALUE', 'PROPERTY'], │ │ │ │ - VALUE: ['LOGICAL', 'COMMA', 'RPAREN', 'END'], │ │ │ │ - SPATIAL: ['LPAREN'], │ │ │ │ - LOGICAL: ['NOT', 'VALUE', 'SPATIAL', 'PROPERTY', 'LPAREN'], │ │ │ │ - NOT: ['PROPERTY', 'LPAREN'], │ │ │ │ - GEOMETRY: ['COMMA', 'RPAREN'] │ │ │ │ + /** │ │ │ │ + * APIMethod: setBorder │ │ │ │ + */ │ │ │ │ + setBorder: function() { │ │ │ │ + //does nothing since the framed popup's entire scheme is based on a │ │ │ │ + // an image -- changing the popup's border makes no sense. │ │ │ │ }, │ │ │ │ │ │ │ │ - operators = { │ │ │ │ - '=': OpenLayers.Filter.Comparison.EQUAL_TO, │ │ │ │ - '<>': OpenLayers.Filter.Comparison.NOT_EQUAL_TO, │ │ │ │ - '<': OpenLayers.Filter.Comparison.LESS_THAN, │ │ │ │ - '<=': OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO, │ │ │ │ - '>': OpenLayers.Filter.Comparison.GREATER_THAN, │ │ │ │ - '>=': OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO, │ │ │ │ - 'LIKE': OpenLayers.Filter.Comparison.LIKE, │ │ │ │ - 'BETWEEN': OpenLayers.Filter.Comparison.BETWEEN, │ │ │ │ - 'IS NULL': OpenLayers.Filter.Comparison.IS_NULL │ │ │ │ + /** │ │ │ │ + * Method: setOpacity │ │ │ │ + * Sets the opacity of the popup. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). │ │ │ │ + */ │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ + //does nothing since we suppose that we'll never apply an opacity │ │ │ │ + // to a framed popup │ │ │ │ }, │ │ │ │ │ │ │ │ - operatorReverse = {}, │ │ │ │ + /** │ │ │ │ + * APIMethod: setSize │ │ │ │ + * Overridden here, because we need to update the blocks whenever the size │ │ │ │ + * of the popup has changed. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * contentSize - {<OpenLayers.Size>} the new size for the popup's │ │ │ │ + * contents div (in pixels). │ │ │ │ + */ │ │ │ │ + setSize: function(contentSize) { │ │ │ │ + OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments); │ │ │ │ │ │ │ │ - logicals = { │ │ │ │ - 'AND': OpenLayers.Filter.Logical.AND, │ │ │ │ - 'OR': OpenLayers.Filter.Logical.OR │ │ │ │ + this.updateBlocks(); │ │ │ │ }, │ │ │ │ │ │ │ │ - logicalReverse = {}, │ │ │ │ + /** │ │ │ │ + * Method: updateRelativePosition │ │ │ │ + * When the relative position changes, we need to set the new padding │ │ │ │ + * BBOX on the popup, reposition the close div, and update the blocks. │ │ │ │ + */ │ │ │ │ + updateRelativePosition: function() { │ │ │ │ │ │ │ │ - precedence = { │ │ │ │ - 'RPAREN': 3, │ │ │ │ - 'LOGICAL': 2, │ │ │ │ - 'COMPARISON': 1 │ │ │ │ - }; │ │ │ │ + //update the padding │ │ │ │ + this.padding = this.positionBlocks[this.relativePosition].padding; │ │ │ │ │ │ │ │ - var i; │ │ │ │ - for (i in operators) { │ │ │ │ - if (operators.hasOwnProperty(i)) { │ │ │ │ - operatorReverse[operators[i]] = i; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + //update the position of our close box to new padding │ │ │ │ + if (this.closeDiv) { │ │ │ │ + // use the content div's css padding to determine if we should │ │ │ │ + // padd the close div │ │ │ │ + var contentDivPadding = this.getContentDivPadding(); │ │ │ │ │ │ │ │ - for (i in logicals) { │ │ │ │ - if (logicals.hasOwnProperty(i)) { │ │ │ │ - logicalReverse[logicals[i]] = i; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + this.closeDiv.style.right = contentDivPadding.right + │ │ │ │ + this.padding.right + "px"; │ │ │ │ + this.closeDiv.style.top = contentDivPadding.top + │ │ │ │ + this.padding.top + "px"; │ │ │ │ + } │ │ │ │ │ │ │ │ - function tryToken(text, pattern) { │ │ │ │ - if (pattern instanceof RegExp) { │ │ │ │ - return pattern.exec(text); │ │ │ │ - } else { │ │ │ │ - return pattern(text); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + this.updateBlocks(); │ │ │ │ + }, │ │ │ │ │ │ │ │ - function nextToken(text, tokens) { │ │ │ │ - var i, token, len = tokens.length; │ │ │ │ - for (i = 0; i < len; i++) { │ │ │ │ - token = tokens[i]; │ │ │ │ - var pat = patterns[token]; │ │ │ │ - var matches = tryToken(text, pat); │ │ │ │ - if (matches) { │ │ │ │ - var match = matches[0]; │ │ │ │ - var remainder = text.substr(match.length).replace(/^\s*/, ""); │ │ │ │ - return { │ │ │ │ - type: token, │ │ │ │ - text: match, │ │ │ │ - remainder: remainder │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: calculateNewPx │ │ │ │ + * Besides the standard offset as determined by the Anchored class, our │ │ │ │ + * Framed popups have a special 'offset' property for each of their │ │ │ │ + * positions, which is used to offset the popup relative to its anchor. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Pixel>} The the new px position of the popup on the screen │ │ │ │ + * relative to the passed-in px. │ │ │ │ + */ │ │ │ │ + calculateNewPx: function(px) { │ │ │ │ + var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply( │ │ │ │ + this, arguments │ │ │ │ + ); │ │ │ │ │ │ │ │ - var msg = "ERROR: In parsing: [" + text + "], expected one of: "; │ │ │ │ - for (i = 0; i < len; i++) { │ │ │ │ - token = tokens[i]; │ │ │ │ - msg += "\n " + token + ": " + patterns[token]; │ │ │ │ - } │ │ │ │ + newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset); │ │ │ │ │ │ │ │ - throw new Error(msg); │ │ │ │ - } │ │ │ │ + return newPx; │ │ │ │ + }, │ │ │ │ │ │ │ │ - function tokenize(text) { │ │ │ │ - var results = []; │ │ │ │ - var token, expect = ["NOT", "GEOMETRY", "SPATIAL", "PROPERTY", "LPAREN"]; │ │ │ │ + /** │ │ │ │ + * Method: createBlocks │ │ │ │ + */ │ │ │ │ + createBlocks: function() { │ │ │ │ + this.blocks = []; │ │ │ │ │ │ │ │ - do { │ │ │ │ - token = nextToken(text, expect); │ │ │ │ - text = token.remainder; │ │ │ │ - expect = follows[token.type]; │ │ │ │ - if (token.type != "END" && !expect) { │ │ │ │ - throw new Error("No follows list for " + token.type); │ │ │ │ + //since all positions contain the same number of blocks, we can │ │ │ │ + // just pick the first position and use its blocks array to create │ │ │ │ + // our blocks array │ │ │ │ + var firstPosition = null; │ │ │ │ + for (var key in this.positionBlocks) { │ │ │ │ + firstPosition = key; │ │ │ │ + break; │ │ │ │ } │ │ │ │ - results.push(token); │ │ │ │ - } while (token.type != "END"); │ │ │ │ │ │ │ │ - return results; │ │ │ │ - } │ │ │ │ + var position = this.positionBlocks[firstPosition]; │ │ │ │ + for (var i = 0; i < position.blocks.length; i++) { │ │ │ │ │ │ │ │ - function buildAst(tokens) { │ │ │ │ - var operatorStack = [], │ │ │ │ - postfix = []; │ │ │ │ + var block = {}; │ │ │ │ + this.blocks.push(block); │ │ │ │ │ │ │ │ - while (tokens.length) { │ │ │ │ - var tok = tokens.shift(); │ │ │ │ - switch (tok.type) { │ │ │ │ - case "PROPERTY": │ │ │ │ - case "GEOMETRY": │ │ │ │ - case "VALUE": │ │ │ │ - postfix.push(tok); │ │ │ │ - break; │ │ │ │ - case "COMPARISON": │ │ │ │ - case "BETWEEN": │ │ │ │ - case "IS_NULL": │ │ │ │ - case "LOGICAL": │ │ │ │ - var p = precedence[tok.type]; │ │ │ │ + var divId = this.id + '_FrameDecorationDiv_' + i; │ │ │ │ + block.div = OpenLayers.Util.createDiv(divId, │ │ │ │ + null, null, null, "absolute", null, "hidden", null │ │ │ │ + ); │ │ │ │ │ │ │ │ - while (operatorStack.length > 0 && │ │ │ │ - (precedence[operatorStack[operatorStack.length - 1].type] <= p) │ │ │ │ - ) { │ │ │ │ - postfix.push(operatorStack.pop()); │ │ │ │ - } │ │ │ │ + var imgId = this.id + '_FrameDecorationImg_' + i; │ │ │ │ + var imageCreator = │ │ │ │ + (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv : │ │ │ │ + OpenLayers.Util.createImage; │ │ │ │ │ │ │ │ - operatorStack.push(tok); │ │ │ │ - break; │ │ │ │ - case "SPATIAL": │ │ │ │ - case "NOT": │ │ │ │ - case "LPAREN": │ │ │ │ - operatorStack.push(tok); │ │ │ │ - break; │ │ │ │ - case "RPAREN": │ │ │ │ - while (operatorStack.length > 0 && │ │ │ │ - (operatorStack[operatorStack.length - 1].type != "LPAREN") │ │ │ │ - ) { │ │ │ │ - postfix.push(operatorStack.pop()); │ │ │ │ - } │ │ │ │ - operatorStack.pop(); // toss out the LPAREN │ │ │ │ + block.image = imageCreator(imgId, │ │ │ │ + null, this.imageSize, this.imageSrc, │ │ │ │ + "absolute", null, null, null │ │ │ │ + ); │ │ │ │ │ │ │ │ - if (operatorStack.length > 0 && │ │ │ │ - operatorStack[operatorStack.length - 1].type == "SPATIAL") { │ │ │ │ - postfix.push(operatorStack.pop()); │ │ │ │ - } │ │ │ │ - case "COMMA": │ │ │ │ - case "END": │ │ │ │ - break; │ │ │ │ - default: │ │ │ │ - throw new Error("Unknown token type " + tok.type); │ │ │ │ + block.div.appendChild(block.image); │ │ │ │ + this.groupDiv.appendChild(block.div); │ │ │ │ } │ │ │ │ - } │ │ │ │ + }, │ │ │ │ │ │ │ │ - while (operatorStack.length > 0) { │ │ │ │ - postfix.push(operatorStack.pop()); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: updateBlocks │ │ │ │ + * Internal method, called on initialize and when the popup's relative │ │ │ │ + * position has changed. This function takes care of re-positioning │ │ │ │ + * the popup's blocks in their appropropriate places. │ │ │ │ + */ │ │ │ │ + updateBlocks: function() { │ │ │ │ + if (!this.blocks) { │ │ │ │ + this.createBlocks(); │ │ │ │ + } │ │ │ │ │ │ │ │ - function buildTree() { │ │ │ │ - var tok = postfix.pop(); │ │ │ │ - switch (tok.type) { │ │ │ │ - case "LOGICAL": │ │ │ │ - var rhs = buildTree(), │ │ │ │ - lhs = buildTree(); │ │ │ │ - return new OpenLayers.Filter.Logical({ │ │ │ │ - filters: [lhs, rhs], │ │ │ │ - type: logicals[tok.text.toUpperCase()] │ │ │ │ - }); │ │ │ │ - case "NOT": │ │ │ │ - var operand = buildTree(); │ │ │ │ - return new OpenLayers.Filter.Logical({ │ │ │ │ - filters: [operand], │ │ │ │ - type: OpenLayers.Filter.Logical.NOT │ │ │ │ - }); │ │ │ │ - case "BETWEEN": │ │ │ │ - var min, max, property; │ │ │ │ - postfix.pop(); // unneeded AND token here │ │ │ │ - max = buildTree(); │ │ │ │ - min = buildTree(); │ │ │ │ - property = buildTree(); │ │ │ │ - return new OpenLayers.Filter.Comparison({ │ │ │ │ - property: property, │ │ │ │ - lowerBoundary: min, │ │ │ │ - upperBoundary: max, │ │ │ │ - type: OpenLayers.Filter.Comparison.BETWEEN │ │ │ │ - }); │ │ │ │ - case "COMPARISON": │ │ │ │ - var value = buildTree(), │ │ │ │ - property = buildTree(); │ │ │ │ - return new OpenLayers.Filter.Comparison({ │ │ │ │ - property: property, │ │ │ │ - value: value, │ │ │ │ - type: operators[tok.text.toUpperCase()] │ │ │ │ - }); │ │ │ │ - case "IS_NULL": │ │ │ │ - var property = buildTree(); │ │ │ │ - return new OpenLayers.Filter.Comparison({ │ │ │ │ - property: property, │ │ │ │ - type: operators[tok.text.toUpperCase()] │ │ │ │ - }); │ │ │ │ - case "VALUE": │ │ │ │ - var match = tok.text.match(/^'(.*)'$/); │ │ │ │ - if (match) { │ │ │ │ - return match[1].replace(/''/g, "'"); │ │ │ │ - } else { │ │ │ │ - return Number(tok.text); │ │ │ │ - } │ │ │ │ - case "SPATIAL": │ │ │ │ - switch (tok.text.toUpperCase()) { │ │ │ │ - case "BBOX": │ │ │ │ - var maxy = buildTree(), │ │ │ │ - maxx = buildTree(), │ │ │ │ - miny = buildTree(), │ │ │ │ - minx = buildTree(), │ │ │ │ - prop = buildTree(); │ │ │ │ + if (this.size && this.relativePosition) { │ │ │ │ + var position = this.positionBlocks[this.relativePosition]; │ │ │ │ + for (var i = 0; i < position.blocks.length; i++) { │ │ │ │ │ │ │ │ - return new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ - property: prop, │ │ │ │ - value: OpenLayers.Bounds.fromArray( │ │ │ │ - [minx, miny, maxx, maxy] │ │ │ │ - ) │ │ │ │ - }); │ │ │ │ - case "INTERSECTS": │ │ │ │ - var value = buildTree(), │ │ │ │ - property = buildTree(); │ │ │ │ - return new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ - property: property, │ │ │ │ - value: value │ │ │ │ - }); │ │ │ │ - case "WITHIN": │ │ │ │ - var value = buildTree(), │ │ │ │ - property = buildTree(); │ │ │ │ - return new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.WITHIN, │ │ │ │ - property: property, │ │ │ │ - value: value │ │ │ │ - }); │ │ │ │ - case "CONTAINS": │ │ │ │ - var value = buildTree(), │ │ │ │ - property = buildTree(); │ │ │ │ - return new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.CONTAINS, │ │ │ │ - property: property, │ │ │ │ - value: value │ │ │ │ - }); │ │ │ │ - case "DWITHIN": │ │ │ │ - var distance = buildTree(), │ │ │ │ - value = buildTree(), │ │ │ │ - property = buildTree(); │ │ │ │ - return new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.DWITHIN, │ │ │ │ - value: value, │ │ │ │ - property: property, │ │ │ │ - distance: Number(distance) │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - case "GEOMETRY": │ │ │ │ - return OpenLayers.Geometry.fromWKT(tok.text); │ │ │ │ - default: │ │ │ │ - return tok.text; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + var positionBlock = position.blocks[i]; │ │ │ │ + var block = this.blocks[i]; │ │ │ │ │ │ │ │ - var result = buildTree(); │ │ │ │ - if (postfix.length > 0) { │ │ │ │ - var msg = "Remaining tokens after building AST: \n"; │ │ │ │ - for (var i = postfix.length - 1; i >= 0; i--) { │ │ │ │ - msg += postfix[i].type + ": " + postfix[i].text + "\n"; │ │ │ │ - } │ │ │ │ - throw new Error(msg); │ │ │ │ - } │ │ │ │ + // adjust sizes │ │ │ │ + var l = positionBlock.anchor.left; │ │ │ │ + var b = positionBlock.anchor.bottom; │ │ │ │ + var r = positionBlock.anchor.right; │ │ │ │ + var t = positionBlock.anchor.top; │ │ │ │ │ │ │ │ - return result; │ │ │ │ - } │ │ │ │ + //note that we use the isNaN() test here because if the │ │ │ │ + // size object is initialized with a "auto" parameter, the │ │ │ │ + // size constructor calls parseFloat() on the string, │ │ │ │ + // which will turn it into NaN │ │ │ │ + // │ │ │ │ + var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l) : │ │ │ │ + positionBlock.size.w; │ │ │ │ │ │ │ │ - return OpenLayers.Class(OpenLayers.Format, { │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Generate a filter from a CQL string. │ │ │ │ - │ │ │ │ - * Parameters: │ │ │ │ - * text - {String} The CQL text. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Filter>} A filter based on the CQL text. │ │ │ │ - */ │ │ │ │ - read: function(text) { │ │ │ │ - var result = buildAst(tokenize(text)); │ │ │ │ - if (this.keepData) { │ │ │ │ - this.data = result; │ │ │ │ - } │ │ │ │ - return result; │ │ │ │ - }, │ │ │ │ + var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t) : │ │ │ │ + positionBlock.size.h; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Convert a filter into a CQL string. │ │ │ │ - │ │ │ │ - * Parameters: │ │ │ │ - * filter - {<OpenLayers.Filter>} The filter. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A CQL string based on the filter. │ │ │ │ - */ │ │ │ │ - write: function(filter) { │ │ │ │ - if (filter instanceof OpenLayers.Geometry) { │ │ │ │ - return filter.toString(); │ │ │ │ - } │ │ │ │ - switch (filter.CLASS_NAME) { │ │ │ │ - case "OpenLayers.Filter.Spatial": │ │ │ │ - switch (filter.type) { │ │ │ │ - case OpenLayers.Filter.Spatial.BBOX: │ │ │ │ - return "BBOX(" + │ │ │ │ - filter.property + "," + │ │ │ │ - filter.value.toBBOX() + │ │ │ │ - ")"; │ │ │ │ - case OpenLayers.Filter.Spatial.DWITHIN: │ │ │ │ - return "DWITHIN(" + │ │ │ │ - filter.property + ", " + │ │ │ │ - this.write(filter.value) + ", " + │ │ │ │ - filter.distance + ")"; │ │ │ │ - case OpenLayers.Filter.Spatial.WITHIN: │ │ │ │ - return "WITHIN(" + │ │ │ │ - filter.property + ", " + │ │ │ │ - this.write(filter.value) + ")"; │ │ │ │ - case OpenLayers.Filter.Spatial.INTERSECTS: │ │ │ │ - return "INTERSECTS(" + │ │ │ │ - filter.property + ", " + │ │ │ │ - this.write(filter.value) + ")"; │ │ │ │ - case OpenLayers.Filter.Spatial.CONTAINS: │ │ │ │ - return "CONTAINS(" + │ │ │ │ - filter.property + ", " + │ │ │ │ - this.write(filter.value) + ")"; │ │ │ │ - default: │ │ │ │ - throw new Error("Unknown spatial filter type: " + filter.type); │ │ │ │ - } │ │ │ │ - case "OpenLayers.Filter.Logical": │ │ │ │ - if (filter.type == OpenLayers.Filter.Logical.NOT) { │ │ │ │ - // TODO: deal with precedence of logical operators to │ │ │ │ - // avoid extra parentheses (not urgent) │ │ │ │ - return "NOT (" + this.write(filter.filters[0]) + ")"; │ │ │ │ - } else { │ │ │ │ - var res = "("; │ │ │ │ - var first = true; │ │ │ │ - for (var i = 0; i < filter.filters.length; i++) { │ │ │ │ - if (first) { │ │ │ │ - first = false; │ │ │ │ - } else { │ │ │ │ - res += ") " + logicalReverse[filter.type] + " ("; │ │ │ │ - } │ │ │ │ - res += this.write(filter.filters[i]); │ │ │ │ - } │ │ │ │ - return res + ")"; │ │ │ │ - } │ │ │ │ - case "OpenLayers.Filter.Comparison": │ │ │ │ - if (filter.type == OpenLayers.Filter.Comparison.BETWEEN) { │ │ │ │ - return filter.property + " BETWEEN " + │ │ │ │ - this.write(filter.lowerBoundary) + " AND " + │ │ │ │ - this.write(filter.upperBoundary); │ │ │ │ - } else { │ │ │ │ - return (filter.value !== null) ? filter.property + │ │ │ │ - " " + operatorReverse[filter.type] + " " + │ │ │ │ - this.write(filter.value) : filter.property + │ │ │ │ - " " + operatorReverse[filter.type]; │ │ │ │ - } │ │ │ │ - case undefined: │ │ │ │ - if (typeof filter === "string") { │ │ │ │ - return "'" + filter.replace(/'/g, "''") + "'"; │ │ │ │ - } else if (typeof filter === "number") { │ │ │ │ - return String(filter); │ │ │ │ - } │ │ │ │ - default: │ │ │ │ - throw new Error("Can't encode: " + filter.CLASS_NAME + " " + filter); │ │ │ │ + block.div.style.width = (w < 0 ? 0 : w) + 'px'; │ │ │ │ + block.div.style.height = (h < 0 ? 0 : h) + 'px'; │ │ │ │ + │ │ │ │ + block.div.style.left = (l != null) ? l + 'px' : ''; │ │ │ │ + block.div.style.bottom = (b != null) ? b + 'px' : ''; │ │ │ │ + block.div.style.right = (r != null) ? r + 'px' : ''; │ │ │ │ + block.div.style.top = (t != null) ? t + 'px' : ''; │ │ │ │ + │ │ │ │ + block.image.style.left = positionBlock.position.x + 'px'; │ │ │ │ + block.image.style.top = positionBlock.position.y + 'px'; │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.contentDiv.style.left = this.padding.left + "px"; │ │ │ │ + this.contentDiv.style.top = this.padding.top + "px"; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.CQL" │ │ │ │ - │ │ │ │ + CLASS_NAME: "OpenLayers.Popup.Framed" │ │ │ │ }); │ │ │ │ -})(); │ │ │ │ - │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/WMC.js │ │ │ │ + OpenLayers/Popup/FramedCloud.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Format/Context.js │ │ │ │ + * @requires OpenLayers/Popup/Framed.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Bounds.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Pixel.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Size.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.WMC │ │ │ │ - * Read and write Web Map Context documents. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.Context> │ │ │ │ + * Class: OpenLayers.Popup.FramedCloud │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Popup.Framed> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.WMC = OpenLayers.Class(OpenLayers.Format.Context, { │ │ │ │ +OpenLayers.Popup.FramedCloud = │ │ │ │ + OpenLayers.Class(OpenLayers.Popup.Framed, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ - * {String} Version number to assume if none found. Default is "1.1.0". │ │ │ │ - */ │ │ │ │ - defaultVersion: "1.1.0", │ │ │ │ + /** │ │ │ │ + * Property: contentDisplayClass │ │ │ │ + * {String} The CSS class of the popup content div. │ │ │ │ + */ │ │ │ │ + contentDisplayClass: "olFramedCloudPopupContent", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WMC │ │ │ │ - * Create a new parser for Web Map Context documents. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * APIProperty: autoSize │ │ │ │ + * {Boolean} Framed Cloud is autosizing by default. │ │ │ │ + */ │ │ │ │ + autoSize: true, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: layerToContext │ │ │ │ - * Create a layer context object given a wms layer object. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.WMS>} The layer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} A layer context object. │ │ │ │ - */ │ │ │ │ - layerToContext: function(layer) { │ │ │ │ - var parser = this.getParser(); │ │ │ │ - var layerContext = { │ │ │ │ - queryable: layer.queryable, │ │ │ │ - visibility: layer.visibility, │ │ │ │ - name: layer.params["LAYERS"], │ │ │ │ - title: layer.name, │ │ │ │ - "abstract": layer.metadata["abstract"], │ │ │ │ - dataURL: layer.metadata.dataURL, │ │ │ │ - metadataURL: layer.metadataURL, │ │ │ │ - server: { │ │ │ │ - version: layer.params["VERSION"], │ │ │ │ - url: layer.url │ │ │ │ - }, │ │ │ │ - maxExtent: layer.maxExtent, │ │ │ │ - transparent: layer.params["TRANSPARENT"], │ │ │ │ - numZoomLevels: layer.numZoomLevels, │ │ │ │ - units: layer.units, │ │ │ │ - isBaseLayer: layer.isBaseLayer, │ │ │ │ - opacity: layer.opacity == 1 ? undefined : layer.opacity, │ │ │ │ - displayInLayerSwitcher: layer.displayInLayerSwitcher, │ │ │ │ - singleTile: layer.singleTile, │ │ │ │ - tileSize: (layer.singleTile || !layer.tileSize) ? │ │ │ │ - undefined : { │ │ │ │ - width: layer.tileSize.w, │ │ │ │ - height: layer.tileSize.h │ │ │ │ - }, │ │ │ │ - minScale: (layer.options.resolutions || │ │ │ │ - layer.options.scales || │ │ │ │ - layer.options.maxResolution || │ │ │ │ - layer.options.minScale) ? │ │ │ │ - layer.minScale : undefined, │ │ │ │ - maxScale: (layer.options.resolutions || │ │ │ │ - layer.options.scales || │ │ │ │ - layer.options.minResolution || │ │ │ │ - layer.options.maxScale) ? │ │ │ │ - layer.maxScale : undefined, │ │ │ │ - formats: [], │ │ │ │ - styles: [], │ │ │ │ - srs: layer.srs, │ │ │ │ - dimensions: layer.dimensions │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * APIProperty: panMapIfOutOfView │ │ │ │ + * {Boolean} Framed Cloud does pan into view by default. │ │ │ │ + */ │ │ │ │ + panMapIfOutOfView: true, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * APIProperty: imageSize │ │ │ │ + * {<OpenLayers.Size>} │ │ │ │ + */ │ │ │ │ + imageSize: new OpenLayers.Size(1276, 736), │ │ │ │ │ │ │ │ - if (layer.metadata.servertitle) { │ │ │ │ - layerContext.server.title = layer.metadata.servertitle; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: isAlphaImage │ │ │ │ + * {Boolean} The FramedCloud does not use an alpha image (in honor of the │ │ │ │ + * good ie6 folk out there) │ │ │ │ + */ │ │ │ │ + isAlphaImage: false, │ │ │ │ │ │ │ │ - if (layer.metadata.formats && layer.metadata.formats.length > 0) { │ │ │ │ - for (var i = 0, len = layer.metadata.formats.length; i < len; i++) { │ │ │ │ - var format = layer.metadata.formats[i]; │ │ │ │ - layerContext.formats.push({ │ │ │ │ - value: format.value, │ │ │ │ - current: (format.value == layer.params["FORMAT"]) │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - layerContext.formats.push({ │ │ │ │ - value: layer.params["FORMAT"], │ │ │ │ - current: true │ │ │ │ - }); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: fixedRelativePosition │ │ │ │ + * {Boolean} The Framed Cloud popup works in just one fixed position. │ │ │ │ + */ │ │ │ │ + fixedRelativePosition: false, │ │ │ │ │ │ │ │ - if (layer.metadata.styles && layer.metadata.styles.length > 0) { │ │ │ │ - for (var i = 0, len = layer.metadata.styles.length; i < len; i++) { │ │ │ │ - var style = layer.metadata.styles[i]; │ │ │ │ - if ((style.href == layer.params["SLD"]) || │ │ │ │ - (style.body == layer.params["SLD_BODY"]) || │ │ │ │ - (style.name == layer.params["STYLES"])) { │ │ │ │ - style.current = true; │ │ │ │ - } else { │ │ │ │ - style.current = false; │ │ │ │ - } │ │ │ │ - layerContext.styles.push(style); │ │ │ │ + /** │ │ │ │ + * Property: positionBlocks │ │ │ │ + * {Object} Hash of differen position blocks, keyed by relativePosition │ │ │ │ + * two-character code string (ie "tl", "tr", "bl", "br") │ │ │ │ + */ │ │ │ │ + positionBlocks: { │ │ │ │ + "tl": { │ │ │ │ + 'offset': new OpenLayers.Pixel(44, 0), │ │ │ │ + 'padding': new OpenLayers.Bounds(8, 40, 8, 9), │ │ │ │ + 'blocks': [{ // top-left │ │ │ │ + size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 51, 22, 0), │ │ │ │ + position: new OpenLayers.Pixel(0, 0) │ │ │ │ + }, { //top-right │ │ │ │ + size: new OpenLayers.Size(22, 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 50, 0, 0), │ │ │ │ + position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ + }, { //bottom-left │ │ │ │ + size: new OpenLayers.Size('auto', 19), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 32, 22, null), │ │ │ │ + position: new OpenLayers.Pixel(0, -631) │ │ │ │ + }, { //bottom-right │ │ │ │ + size: new OpenLayers.Size(22, 18), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 32, 0, null), │ │ │ │ + position: new OpenLayers.Pixel(-1238, -632) │ │ │ │ + }, { // stem │ │ │ │ + size: new OpenLayers.Size(81, 35), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 0, 0, null), │ │ │ │ + position: new OpenLayers.Pixel(0, -688) │ │ │ │ + }] │ │ │ │ + }, │ │ │ │ + "tr": { │ │ │ │ + 'offset': new OpenLayers.Pixel(-45, 0), │ │ │ │ + 'padding': new OpenLayers.Bounds(8, 40, 8, 9), │ │ │ │ + 'blocks': [{ // top-left │ │ │ │ + size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 51, 22, 0), │ │ │ │ + position: new OpenLayers.Pixel(0, 0) │ │ │ │ + }, { //top-right │ │ │ │ + size: new OpenLayers.Size(22, 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 50, 0, 0), │ │ │ │ + position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ + }, { //bottom-left │ │ │ │ + size: new OpenLayers.Size('auto', 19), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 32, 22, null), │ │ │ │ + position: new OpenLayers.Pixel(0, -631) │ │ │ │ + }, { //bottom-right │ │ │ │ + size: new OpenLayers.Size(22, 19), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 32, 0, null), │ │ │ │ + position: new OpenLayers.Pixel(-1238, -631) │ │ │ │ + }, { // stem │ │ │ │ + size: new OpenLayers.Size(81, 35), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 0, null, null), │ │ │ │ + position: new OpenLayers.Pixel(-215, -687) │ │ │ │ + }] │ │ │ │ + }, │ │ │ │ + "bl": { │ │ │ │ + 'offset': new OpenLayers.Pixel(45, 0), │ │ │ │ + 'padding': new OpenLayers.Bounds(8, 9, 8, 40), │ │ │ │ + 'blocks': [{ // top-left │ │ │ │ + size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 21, 22, 32), │ │ │ │ + position: new OpenLayers.Pixel(0, 0) │ │ │ │ + }, { //top-right │ │ │ │ + size: new OpenLayers.Size(22, 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 21, 0, 32), │ │ │ │ + position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ + }, { //bottom-left │ │ │ │ + size: new OpenLayers.Size('auto', 21), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 0, 22, null), │ │ │ │ + position: new OpenLayers.Pixel(0, -629) │ │ │ │ + }, { //bottom-right │ │ │ │ + size: new OpenLayers.Size(22, 21), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 0, 0, null), │ │ │ │ + position: new OpenLayers.Pixel(-1238, -629) │ │ │ │ + }, { // stem │ │ │ │ + size: new OpenLayers.Size(81, 33), │ │ │ │ + anchor: new OpenLayers.Bounds(null, null, 0, 0), │ │ │ │ + position: new OpenLayers.Pixel(-101, -674) │ │ │ │ + }] │ │ │ │ + }, │ │ │ │ + "br": { │ │ │ │ + 'offset': new OpenLayers.Pixel(-44, 0), │ │ │ │ + 'padding': new OpenLayers.Bounds(8, 9, 8, 40), │ │ │ │ + 'blocks': [{ // top-left │ │ │ │ + size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 21, 22, 32), │ │ │ │ + position: new OpenLayers.Pixel(0, 0) │ │ │ │ + }, { //top-right │ │ │ │ + size: new OpenLayers.Size(22, 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 21, 0, 32), │ │ │ │ + position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ + }, { //bottom-left │ │ │ │ + size: new OpenLayers.Size('auto', 21), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 0, 22, null), │ │ │ │ + position: new OpenLayers.Pixel(0, -629) │ │ │ │ + }, { //bottom-right │ │ │ │ + size: new OpenLayers.Size(22, 21), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 0, 0, null), │ │ │ │ + position: new OpenLayers.Pixel(-1238, -629) │ │ │ │ + }, { // stem │ │ │ │ + size: new OpenLayers.Size(81, 33), │ │ │ │ + anchor: new OpenLayers.Bounds(0, null, null, 0), │ │ │ │ + position: new OpenLayers.Pixel(-311, -674) │ │ │ │ + }] │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - layerContext.styles.push({ │ │ │ │ - href: layer.params["SLD"], │ │ │ │ - body: layer.params["SLD_BODY"], │ │ │ │ - name: layer.params["STYLES"] || parser.defaultStyleName, │ │ │ │ - title: parser.defaultStyleTitle, │ │ │ │ - current: true │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - │ │ │ │ - return layerContext; │ │ │ │ - }, │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: toContext │ │ │ │ - * Create a context object free from layer given a map or a │ │ │ │ - * context object. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {<OpenLayers.Map> | Object} The map or context. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} A context object. │ │ │ │ - */ │ │ │ │ - toContext: function(obj) { │ │ │ │ - var context = {}; │ │ │ │ - var layers = obj.layers; │ │ │ │ - if (obj.CLASS_NAME == "OpenLayers.Map") { │ │ │ │ - var metadata = obj.metadata || {}; │ │ │ │ - context.size = obj.getSize(); │ │ │ │ - context.bounds = obj.getExtent(); │ │ │ │ - context.projection = obj.projection; │ │ │ │ - context.title = obj.title; │ │ │ │ - context.keywords = metadata.keywords; │ │ │ │ - context["abstract"] = metadata["abstract"]; │ │ │ │ - context.logo = metadata.logo; │ │ │ │ - context.descriptionURL = metadata.descriptionURL; │ │ │ │ - context.contactInformation = metadata.contactInformation; │ │ │ │ - context.maxExtent = obj.maxExtent; │ │ │ │ - } else { │ │ │ │ - // copy all obj properties except the "layers" property │ │ │ │ - OpenLayers.Util.applyDefaults(context, obj); │ │ │ │ - if (context.layers != undefined) { │ │ │ │ - delete(context.layers); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: minSize │ │ │ │ + * {<OpenLayers.Size>} │ │ │ │ + */ │ │ │ │ + minSize: new OpenLayers.Size(105, 10), │ │ │ │ │ │ │ │ - if (context.layersContext == undefined) { │ │ │ │ - context.layersContext = []; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: maxSize │ │ │ │ + * {<OpenLayers.Size>} │ │ │ │ + */ │ │ │ │ + maxSize: new OpenLayers.Size(1200, 660), │ │ │ │ │ │ │ │ - // let's convert layers into layersContext object (if any) │ │ │ │ - if (layers != undefined && OpenLayers.Util.isArray(layers)) { │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - var layer = layers[i]; │ │ │ │ - if (layer instanceof OpenLayers.Layer.WMS) { │ │ │ │ - context.layersContext.push(this.layerToContext(layer)); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return context; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Popup.FramedCloud │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * id - {String} │ │ │ │ + * lonlat - {<OpenLayers.LonLat>} │ │ │ │ + * contentSize - {<OpenLayers.Size>} │ │ │ │ + * contentHTML - {String} │ │ │ │ + * anchor - {Object} Object to which we'll anchor the popup. Must expose │ │ │ │ + * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) │ │ │ │ + * (Note that this is generally an <OpenLayers.Icon>). │ │ │ │ + * closeBox - {Boolean} │ │ │ │ + * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ + */ │ │ │ │ + initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, │ │ │ │ + closeBoxCallback) { │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMC" │ │ │ │ + this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png'); │ │ │ │ + OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments); │ │ │ │ + this.contentDiv.className = this.contentDisplayClass; │ │ │ │ + }, │ │ │ │ │ │ │ │ -}); │ │ │ │ + CLASS_NAME: "OpenLayers.Popup.FramedCloud" │ │ │ │ + }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/WPSCapabilities.js │ │ │ │ + OpenLayers/Control/Button.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.WPSCapabilities │ │ │ │ - * Read WPS Capabilities. │ │ │ │ + * Class: OpenLayers.Control.Button │ │ │ │ + * The Button control is a very simple push-button, for use with │ │ │ │ + * <OpenLayers.Control.Panel>. │ │ │ │ + * When clicked, the function trigger() is executed. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + * │ │ │ │ + * Use: │ │ │ │ + * (code) │ │ │ │ + * var button = new OpenLayers.Control.Button({ │ │ │ │ + * displayClass: "MyButton", trigger: myFunction │ │ │ │ + * }); │ │ │ │ + * panel.addControls([button]); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Will create a button with CSS class MyButtonItemInactive, that │ │ │ │ + * will call the function MyFunction() when clicked. │ │ │ │ */ │ │ │ │ -OpenLayers.Format.WPSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ - * {String} Version number to assume if none found. Default is "1.0.0". │ │ │ │ - */ │ │ │ │ - defaultVersion: "1.0.0", │ │ │ │ - │ │ │ │ +OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.WPSCapabilities │ │ │ │ - * Create a new parser for WPS Capabilities. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * Property: type │ │ │ │ + * {Integer} OpenLayers.Control.TYPE_BUTTON. │ │ │ │ */ │ │ │ │ + type: OpenLayers.Control.TYPE_BUTTON, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read capabilities data from a string, and return information about │ │ │ │ - * the service. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Info about the WPS │ │ │ │ + * Method: trigger │ │ │ │ + * Called by a control panel when the button is clicked. │ │ │ │ */ │ │ │ │ + trigger: function() {}, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WPSCapabilities" │ │ │ │ - │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Button" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/EncodedPolyline.js │ │ │ │ + OpenLayers/Control/ZoomOut.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Control/Button.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.EncodedPolyline │ │ │ │ - * Class for reading and writing encoded polylines. Create a new instance │ │ │ │ - * with the <OpenLayers.Format.EncodedPolyline> constructor. │ │ │ │ + * Class: OpenLayers.Control.ZoomOut │ │ │ │ + * The ZoomOut control is a button to decrease the zoom level of a map. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: geometryType │ │ │ │ - * {String} Geometry type to output. One of: linestring (default), │ │ │ │ - * linearring, point, multipoint or polygon. If the geometryType is │ │ │ │ - * point, only the first point of the string is returned. │ │ │ │ - */ │ │ │ │ - geometryType: "linestring", │ │ │ │ +OpenLayers.Control.ZoomOut = OpenLayers.Class(OpenLayers.Control.Button, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.EncodedPolyline │ │ │ │ - * Create a new parser for encoded polylines │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Format.EncodedPolyline>} A new encoded polylines parser. │ │ │ │ + * Method: trigger │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.prototype.initialize.apply(this, [options]); │ │ │ │ + trigger: function() { │ │ │ │ + if (this.map) { │ │ │ │ + this.map.zoomOut(); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Deserialize an encoded polyline string and return a vector feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * encoded - {String} An encoded polyline string │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A vector feature with a linestring. │ │ │ │ - */ │ │ │ │ - read: function(encoded) { │ │ │ │ - var geomType; │ │ │ │ - if (this.geometryType == "linestring") │ │ │ │ - geomType = OpenLayers.Geometry.LineString; │ │ │ │ - else if (this.geometryType == "linearring") │ │ │ │ - geomType = OpenLayers.Geometry.LinearRing; │ │ │ │ - else if (this.geometryType == "multipoint") │ │ │ │ - geomType = OpenLayers.Geometry.MultiPoint; │ │ │ │ - else if (this.geometryType != "point" && this.geometryType != "polygon") │ │ │ │ - return null; │ │ │ │ - │ │ │ │ - var flatPoints = this.decodeDeltas(encoded, 2); │ │ │ │ - var flatPointsLength = flatPoints.length; │ │ │ │ - │ │ │ │ - var pointGeometries = []; │ │ │ │ - for (var i = 0; i + 1 < flatPointsLength;) { │ │ │ │ - var y = flatPoints[i++], │ │ │ │ - x = flatPoints[i++]; │ │ │ │ - pointGeometries.push(new OpenLayers.Geometry.Point(x, y)); │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Control.ZoomOut" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/Scale.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - if (this.geometryType == "point") │ │ │ │ - return new OpenLayers.Feature.Vector( │ │ │ │ - pointGeometries[0] │ │ │ │ - ); │ │ │ │ │ │ │ │ - if (this.geometryType == "polygon") │ │ │ │ - return new OpenLayers.Feature.Vector( │ │ │ │ - new OpenLayers.Geometry.Polygon([ │ │ │ │ - new OpenLayers.Geometry.LinearRing(pointGeometries) │ │ │ │ - ]) │ │ │ │ - ); │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Lang.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - return new OpenLayers.Feature.Vector( │ │ │ │ - new geomType(pointGeometries) │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.Scale │ │ │ │ + * The Scale control displays the current map scale as a ratio (e.g. Scale = │ │ │ │ + * 1:1M). By default it is displayed in the lower right corner of the map. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: decode │ │ │ │ - * Deserialize an encoded string and return an array of n-dimensional │ │ │ │ - * points. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * encoded - {String} An encoded string │ │ │ │ - * dims - {int} The dimension of the points that are returned │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array(Array(int))} An array containing n-dimensional arrays of │ │ │ │ - * coordinates. │ │ │ │ + * Property: element │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - decode: function(encoded, dims, opt_factor) { │ │ │ │ - var factor = opt_factor || 1e5; │ │ │ │ - var flatPoints = this.decodeDeltas(encoded, dims, factor); │ │ │ │ - var flatPointsLength = flatPoints.length; │ │ │ │ - │ │ │ │ - var points = []; │ │ │ │ - for (var i = 0; i + (dims - 1) < flatPointsLength;) { │ │ │ │ - var point = []; │ │ │ │ - │ │ │ │ - for (var dim = 0; dim < dims; ++dim) { │ │ │ │ - point.push(flatPoints[i++]) │ │ │ │ - } │ │ │ │ - │ │ │ │ - points.push(point); │ │ │ │ - } │ │ │ │ - │ │ │ │ - return points; │ │ │ │ - }, │ │ │ │ + element: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Serialize a feature or array of features into a WKT string. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {<OpenLayers.Feature.Vector>|Array} A feature or array of │ │ │ │ - * features │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The WKT string representation of the input geometries │ │ │ │ + * APIProperty: geodesic │ │ │ │ + * {Boolean} Use geodesic measurement. Default is false. The recommended │ │ │ │ + * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to │ │ │ │ + * true, the scale will be calculated based on the horizontal size of the │ │ │ │ + * pixel in the center of the map viewport. │ │ │ │ */ │ │ │ │ - write: function(features) { │ │ │ │ - var feature; │ │ │ │ - if (features.constructor == Array) │ │ │ │ - feature = features[0]; │ │ │ │ - else │ │ │ │ - feature = features; │ │ │ │ - │ │ │ │ - var geometry = feature.geometry; │ │ │ │ - var type = geometry.CLASS_NAME.split('.')[2].toLowerCase(); │ │ │ │ - │ │ │ │ - var pointGeometries; │ │ │ │ - if (type == "point") │ │ │ │ - pointGeometries = new Array(geometry); │ │ │ │ - else if (type == "linestring" || │ │ │ │ - type == "linearring" || │ │ │ │ - type == "multipoint") │ │ │ │ - pointGeometries = geometry.components; │ │ │ │ - else if (type == "polygon") │ │ │ │ - pointGeometries = geometry.components[0].components; │ │ │ │ - else │ │ │ │ - return null; │ │ │ │ - │ │ │ │ - var flatPoints = []; │ │ │ │ - │ │ │ │ - var pointGeometriesLength = pointGeometries.length; │ │ │ │ - for (var i = 0; i < pointGeometriesLength; ++i) { │ │ │ │ - var pointGeometry = pointGeometries[i]; │ │ │ │ - flatPoints.push(pointGeometry.y); │ │ │ │ - flatPoints.push(pointGeometry.x); │ │ │ │ - } │ │ │ │ - │ │ │ │ - return this.encodeDeltas(flatPoints, 2); │ │ │ │ - }, │ │ │ │ + geodesic: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: encode │ │ │ │ - * Serialize an array of n-dimensional points and return an encoded string │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Control.Scale │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * points - {Array(Array(int))} An array containing n-dimensional │ │ │ │ - * arrays of coordinates │ │ │ │ - * dims - {int} The dimension of the points that should be read │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} An encoded string │ │ │ │ + * element - {DOMElement} │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - encode: function(points, dims, opt_factor) { │ │ │ │ - var factor = opt_factor || 1e5; │ │ │ │ - var flatPoints = []; │ │ │ │ - │ │ │ │ - var pointsLength = points.length; │ │ │ │ - for (var i = 0; i < pointsLength; ++i) { │ │ │ │ - var point = points[i]; │ │ │ │ - │ │ │ │ - for (var dim = 0; dim < dims; ++dim) { │ │ │ │ - flatPoints.push(point[dim]); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - return this.encodeDeltas(flatPoints, dims, factor); │ │ │ │ + initialize: function(element, options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + this.element = OpenLayers.Util.getElement(element); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: encodeDeltas │ │ │ │ - * Encode a list of n-dimensional points and return an encoded string │ │ │ │ - * │ │ │ │ - * Attention: This function will modify the passed array! │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * numbers - {Array.<number>} A list of n-dimensional points. │ │ │ │ - * dimension - {number} The dimension of the points in the list. │ │ │ │ - * opt_factor - {number=} The factor by which the numbers will be │ │ │ │ - * multiplied. The remaining decimal places will get rounded away. │ │ │ │ - * │ │ │ │ + * Method: draw │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {string} The encoded string. │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - encodeDeltas: function(numbers, dimension, opt_factor) { │ │ │ │ - var factor = opt_factor || 1e5; │ │ │ │ - var d; │ │ │ │ - │ │ │ │ - var lastNumbers = new Array(dimension); │ │ │ │ - for (d = 0; d < dimension; ++d) { │ │ │ │ - lastNumbers[d] = 0; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var numbersLength = numbers.length; │ │ │ │ - for (var i = 0; i < numbersLength;) { │ │ │ │ - for (d = 0; d < dimension; ++d, ++i) { │ │ │ │ - var num = numbers[i]; │ │ │ │ - var delta = num - lastNumbers[d]; │ │ │ │ - lastNumbers[d] = num; │ │ │ │ - │ │ │ │ - numbers[i] = delta; │ │ │ │ - } │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ + if (!this.element) { │ │ │ │ + this.element = document.createElement("div"); │ │ │ │ + this.div.appendChild(this.element); │ │ │ │ } │ │ │ │ - │ │ │ │ - return this.encodeFloats(numbers, factor); │ │ │ │ + this.map.events.register('moveend', this, this.updateScale); │ │ │ │ + this.updateScale(); │ │ │ │ + return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIMethod: decodeDeltas │ │ │ │ - * Decode a list of n-dimensional points from an encoded string │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * encoded - {string} An encoded string. │ │ │ │ - * dimension - {number} The dimension of the points in the encoded string. │ │ │ │ - * opt_factor - {number=} The factor by which the resulting numbers will │ │ │ │ - * be divided. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array.<number>} A list of n-dimensional points. │ │ │ │ + * Method: updateScale │ │ │ │ */ │ │ │ │ - decodeDeltas: function(encoded, dimension, opt_factor) { │ │ │ │ - var factor = opt_factor || 1e5; │ │ │ │ - var d; │ │ │ │ - │ │ │ │ - var lastNumbers = new Array(dimension); │ │ │ │ - for (d = 0; d < dimension; ++d) { │ │ │ │ - lastNumbers[d] = 0; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var numbers = this.decodeFloats(encoded, factor); │ │ │ │ - │ │ │ │ - var numbersLength = numbers.length; │ │ │ │ - for (var i = 0; i < numbersLength;) { │ │ │ │ - for (d = 0; d < dimension; ++d, ++i) { │ │ │ │ - lastNumbers[d] += numbers[i]; │ │ │ │ - │ │ │ │ - numbers[i] = lastNumbers[d]; │ │ │ │ + updateScale: function() { │ │ │ │ + var scale; │ │ │ │ + if (this.geodesic === true) { │ │ │ │ + var units = this.map.getUnits(); │ │ │ │ + if (!units) { │ │ │ │ + return; │ │ │ │ } │ │ │ │ + var inches = OpenLayers.INCHES_PER_UNIT; │ │ │ │ + scale = (this.map.getGeodesicPixelSize().w || 0.000001) * │ │ │ │ + inches["km"] * OpenLayers.DOTS_PER_INCH; │ │ │ │ + } else { │ │ │ │ + scale = this.map.getScale(); │ │ │ │ } │ │ │ │ │ │ │ │ - return numbers; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: encodeFloats │ │ │ │ - * Encode a list of floating point numbers and return an encoded string │ │ │ │ - * │ │ │ │ - * Attention: This function will modify the passed array! │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * numbers - {Array.<number>} A list of floating point numbers. │ │ │ │ - * opt_factor - {number=} The factor by which the numbers will be │ │ │ │ - * multiplied. The remaining decimal places will get rounded away. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {string} The encoded string. │ │ │ │ - */ │ │ │ │ - encodeFloats: function(numbers, opt_factor) { │ │ │ │ - var factor = opt_factor || 1e5; │ │ │ │ + if (!scale) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ │ │ │ │ - var numbersLength = numbers.length; │ │ │ │ - for (var i = 0; i < numbersLength; ++i) { │ │ │ │ - numbers[i] = Math.round(numbers[i] * factor); │ │ │ │ + if (scale >= 9500 && scale <= 950000) { │ │ │ │ + scale = Math.round(scale / 1000) + "K"; │ │ │ │ + } else if (scale >= 950000) { │ │ │ │ + scale = Math.round(scale / 1000000) + "M"; │ │ │ │ + } else { │ │ │ │ + scale = Math.round(scale); │ │ │ │ } │ │ │ │ │ │ │ │ - return this.encodeSignedIntegers(numbers); │ │ │ │ + this.element.innerHTML = OpenLayers.i18n("Scale = 1 : ${scaleDenom}", { │ │ │ │ + 'scaleDenom': scale │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Scale" │ │ │ │ +}); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: decodeFloats │ │ │ │ - * Decode a list of floating point numbers from an encoded string │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * encoded - {string} An encoded string. │ │ │ │ - * opt_factor - {number=} The factor by which the result will be divided. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array.<number>} A list of floating point numbers. │ │ │ │ - */ │ │ │ │ - decodeFloats: function(encoded, opt_factor) { │ │ │ │ - var factor = opt_factor || 1e5; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/PinchZoom.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - var numbers = this.decodeSignedIntegers(encoded); │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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 numbersLength = numbers.length; │ │ │ │ - for (var i = 0; i < numbersLength; ++i) { │ │ │ │ - numbers[i] /= factor; │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Handler/Pinch.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - return numbers; │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.PinchZoom │ │ │ │ + * │ │ │ │ + * Inherits: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Property: type │ │ │ │ + * {OpenLayers.Control.TYPES} │ │ │ │ + */ │ │ │ │ + type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: encodeSignedIntegers │ │ │ │ - * Encode a list of signed integers and return an encoded string │ │ │ │ - * │ │ │ │ - * Attention: This function will modify the passed array! │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * numbers - {Array.<number>} A list of signed integers. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {string} The encoded string. │ │ │ │ + * Property: pinchOrigin │ │ │ │ + * {Object} Cached object representing the pinch start (in pixels). │ │ │ │ */ │ │ │ │ - encodeSignedIntegers: function(numbers) { │ │ │ │ - var numbersLength = numbers.length; │ │ │ │ - for (var i = 0; i < numbersLength; ++i) { │ │ │ │ - var num = numbers[i]; │ │ │ │ - │ │ │ │ - var signedNum = num << 1; │ │ │ │ - if (num < 0) { │ │ │ │ - signedNum = ~(signedNum); │ │ │ │ - } │ │ │ │ - │ │ │ │ - numbers[i] = signedNum; │ │ │ │ - } │ │ │ │ - │ │ │ │ - return this.encodeUnsignedIntegers(numbers); │ │ │ │ - }, │ │ │ │ - │ │ │ │ + pinchOrigin: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: decodeSignedIntegers │ │ │ │ - * Decode a list of signed integers from an encoded string │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * encoded - {string} An encoded string. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array.<number>} A list of signed integers. │ │ │ │ + * Property: currentCenter │ │ │ │ + * {Object} Cached object representing the latest pinch center (in pixels). │ │ │ │ */ │ │ │ │ - decodeSignedIntegers: function(encoded) { │ │ │ │ - var numbers = this.decodeUnsignedIntegers(encoded); │ │ │ │ - │ │ │ │ - var numbersLength = numbers.length; │ │ │ │ - for (var i = 0; i < numbersLength; ++i) { │ │ │ │ - var num = numbers[i]; │ │ │ │ - numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1); │ │ │ │ - } │ │ │ │ - │ │ │ │ - return numbers; │ │ │ │ - }, │ │ │ │ - │ │ │ │ + currentCenter: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: encodeUnsignedIntegers │ │ │ │ - * Encode a list of unsigned integers and return an encoded string │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * numbers - {Array.<number>} A list of unsigned integers. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {string} The encoded string. │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * true. │ │ │ │ */ │ │ │ │ - encodeUnsignedIntegers: function(numbers) { │ │ │ │ - var encoded = ''; │ │ │ │ - │ │ │ │ - var numbersLength = numbers.length; │ │ │ │ - for (var i = 0; i < numbersLength; ++i) { │ │ │ │ - encoded += this.encodeUnsignedInteger(numbers[i]); │ │ │ │ - } │ │ │ │ - │ │ │ │ - return encoded; │ │ │ │ - }, │ │ │ │ - │ │ │ │ + autoActivate: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: decodeUnsignedIntegers │ │ │ │ - * Decode a list of unsigned integers from an encoded string │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * encoded - {string} An encoded string. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array.<number>} A list of unsigned integers. │ │ │ │ + * APIProperty: preserveCenter │ │ │ │ + * {Boolean} Set this to true if you don't want the map center to change │ │ │ │ + * while pinching. For example you may want to set preserveCenter to │ │ │ │ + * true when the user location is being watched and you want to preserve │ │ │ │ + * the user location at the center of the map even if he zooms in or │ │ │ │ + * out using pinch. This property's value can be changed any time on an │ │ │ │ + * existing instance. Default is false. │ │ │ │ */ │ │ │ │ - decodeUnsignedIntegers: function(encoded) { │ │ │ │ - var numbers = []; │ │ │ │ - │ │ │ │ - var current = 0; │ │ │ │ - var shift = 0; │ │ │ │ - │ │ │ │ - var encodedLength = encoded.length; │ │ │ │ - for (var i = 0; i < encodedLength; ++i) { │ │ │ │ - var b = encoded.charCodeAt(i) - 63; │ │ │ │ - │ │ │ │ - current |= (b & 0x1f) << shift; │ │ │ │ - │ │ │ │ - if (b < 0x20) { │ │ │ │ - numbers.push(current); │ │ │ │ - current = 0; │ │ │ │ - shift = 0; │ │ │ │ - } else { │ │ │ │ - shift += 5; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - return numbers; │ │ │ │ - }, │ │ │ │ - │ │ │ │ + preserveCenter: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: encodeFloat │ │ │ │ - * Encode one single floating point number and return an encoded string │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * num - {number} Floating point number that should be encoded. │ │ │ │ - * opt_factor - {number=} The factor by which num will be multiplied. │ │ │ │ - * The remaining decimal places will get rounded away. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {string} The encoded string. │ │ │ │ + * APIProperty: handlerOptions │ │ │ │ + * {Object} Used to set non-default properties on the pinch handler │ │ │ │ */ │ │ │ │ - encodeFloat: function(num, opt_factor) { │ │ │ │ - num = Math.round(num * (opt_factor || 1e5)); │ │ │ │ - return this.encodeSignedInteger(num); │ │ │ │ - }, │ │ │ │ - │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: decodeFloat │ │ │ │ - * Decode one single floating point number from an encoded string │ │ │ │ + * Constructor: OpenLayers.Control.PinchZoom │ │ │ │ + * Create a control for zooming with pinch gestures. This works on devices │ │ │ │ + * with multi-touch support. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * encoded - {string} An encoded string. │ │ │ │ - * opt_factor - {number=} The factor by which the result will be divided. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {number} The decoded floating point number. │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * the control │ │ │ │ */ │ │ │ │ - decodeFloat: function(encoded, opt_factor) { │ │ │ │ - var result = this.decodeSignedInteger(encoded); │ │ │ │ - return result / (opt_factor || 1e5); │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ + this.handler = new OpenLayers.Handler.Pinch(this, { │ │ │ │ + start: this.pinchStart, │ │ │ │ + move: this.pinchMove, │ │ │ │ + done: this.pinchDone │ │ │ │ + }, this.handlerOptions); │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: encodeSignedInteger │ │ │ │ - * Encode one single signed integer and return an encoded string │ │ │ │ + * Method: pinchStart │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * num - {number} Signed integer that should be encoded. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {string} The encoded string. │ │ │ │ + * evt - {Event} │ │ │ │ + * pinchData - {Object} pinch data object related to the current touchmove │ │ │ │ + * of the pinch gesture. This give us the current scale of the pinch. │ │ │ │ */ │ │ │ │ - encodeSignedInteger: function(num) { │ │ │ │ - var signedNum = num << 1; │ │ │ │ - if (num < 0) { │ │ │ │ - signedNum = ~(signedNum); │ │ │ │ - } │ │ │ │ - │ │ │ │ - return this.encodeUnsignedInteger(signedNum); │ │ │ │ + pinchStart: function(evt, pinchData) { │ │ │ │ + var xy = (this.preserveCenter) ? │ │ │ │ + this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy; │ │ │ │ + this.pinchOrigin = xy; │ │ │ │ + this.currentCenter = xy; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: decodeSignedInteger │ │ │ │ - * Decode one single signed integer from an encoded string │ │ │ │ + * Method: pinchMove │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * encoded - {string} An encoded string. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {number} The decoded signed integer. │ │ │ │ + * evt - {Event} │ │ │ │ + * pinchData - {Object} pinch data object related to the current touchmove │ │ │ │ + * of the pinch gesture. This give us the current scale of the pinch. │ │ │ │ */ │ │ │ │ - decodeSignedInteger: function(encoded) { │ │ │ │ - var result = this.decodeUnsignedInteger(encoded); │ │ │ │ - return ((result & 1) ? ~(result >> 1) : (result >> 1)); │ │ │ │ - }, │ │ │ │ + pinchMove: function(evt, pinchData) { │ │ │ │ + var scale = pinchData.scale; │ │ │ │ + var containerOrigin = this.map.layerContainerOriginPx; │ │ │ │ + var pinchOrigin = this.pinchOrigin; │ │ │ │ + var current = (this.preserveCenter) ? │ │ │ │ + this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy; │ │ │ │ │ │ │ │ + var dx = Math.round((containerOrigin.x + current.x - pinchOrigin.x) + (scale - 1) * (containerOrigin.x - pinchOrigin.x)); │ │ │ │ + var dy = Math.round((containerOrigin.y + current.y - pinchOrigin.y) + (scale - 1) * (containerOrigin.y - pinchOrigin.y)); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: encodeUnsignedInteger │ │ │ │ - * Encode one single unsigned integer and return an encoded string │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * num - {number} Unsigned integer that should be encoded. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {string} The encoded string. │ │ │ │ - */ │ │ │ │ - encodeUnsignedInteger: function(num) { │ │ │ │ - var value, encoded = ''; │ │ │ │ - while (num >= 0x20) { │ │ │ │ - value = (0x20 | (num & 0x1f)) + 63; │ │ │ │ - encoded += (String.fromCharCode(value)); │ │ │ │ - num >>= 5; │ │ │ │ - } │ │ │ │ - value = num + 63; │ │ │ │ - encoded += (String.fromCharCode(value)); │ │ │ │ - return encoded; │ │ │ │ + this.map.applyTransform(dx, dy, scale); │ │ │ │ + this.currentCenter = current; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: decodeUnsignedInteger │ │ │ │ - * Decode one single unsigned integer from an encoded string │ │ │ │ + * Method: pinchDone │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * encoded - {string} An encoded string. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {number} The decoded unsigned integer. │ │ │ │ + * evt - {Event} │ │ │ │ + * start - {Object} pinch data object related to the touchstart event that │ │ │ │ + * started the pinch gesture. │ │ │ │ + * last - {Object} pinch data object related to the last touchmove event │ │ │ │ + * of the pinch gesture. This give us the final scale of the pinch. │ │ │ │ */ │ │ │ │ - decodeUnsignedInteger: function(encoded) { │ │ │ │ - var result = 0; │ │ │ │ - var shift = 0; │ │ │ │ + pinchDone: function(evt, start, last) { │ │ │ │ + this.map.applyTransform(); │ │ │ │ + var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true); │ │ │ │ + if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) { │ │ │ │ + var resolution = this.map.getResolutionForZoom(zoom); │ │ │ │ │ │ │ │ - var encodedLength = encoded.length; │ │ │ │ - for (var i = 0; i < encodedLength; ++i) { │ │ │ │ - var b = encoded.charCodeAt(i) - 63; │ │ │ │ + var location = this.map.getLonLatFromPixel(this.pinchOrigin); │ │ │ │ + var zoomPixel = this.currentCenter; │ │ │ │ + var size = this.map.getSize(); │ │ │ │ │ │ │ │ - result |= (b & 0x1f) << shift; │ │ │ │ + location.lon += resolution * ((size.w / 2) - zoomPixel.x); │ │ │ │ + location.lat -= resolution * ((size.h / 2) - zoomPixel.y); │ │ │ │ │ │ │ │ - if (b < 0x20) │ │ │ │ - break; │ │ │ │ + // Force a reflow before calling setCenter. This is to work │ │ │ │ + // around an issue occuring in iOS. │ │ │ │ + // │ │ │ │ + // See https://github.com/openlayers/openlayers/pull/351. │ │ │ │ + // │ │ │ │ + // Without a reflow setting the layer container div's top left │ │ │ │ + // style properties to "0px" - as done in Map.moveTo when zoom │ │ │ │ + // is changed - won't actually correctly reposition the layer │ │ │ │ + // container div. │ │ │ │ + // │ │ │ │ + // Also, we need to use a statement that the Google Closure │ │ │ │ + // compiler won't optimize away. │ │ │ │ + this.map.div.clientWidth = this.map.div.clientWidth; │ │ │ │ │ │ │ │ - shift += 5; │ │ │ │ + this.map.setCenter(location, zoom); │ │ │ │ } │ │ │ │ - │ │ │ │ - return result; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.EncodedPolyline" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.PinchZoom" │ │ │ │ + │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/Atom.js │ │ │ │ + OpenLayers/Control/ArgParser.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Format/GML/v3.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.Atom │ │ │ │ - * Read/write Atom feeds. Create a new instance with the │ │ │ │ - * <OpenLayers.Format.AtomFeed> constructor. │ │ │ │ + * Class: OpenLayers.Control.ArgParser │ │ │ │ + * The ArgParser control adds location bar query string parsing functionality │ │ │ │ + * to an OpenLayers Map. │ │ │ │ + * When added to a Map control, on a page load/refresh, the Map will │ │ │ │ + * automatically take the href string and parse it for lon, lat, zoom, and │ │ │ │ + * layers information. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. Properties │ │ │ │ - * of this object should not be set individually. Read-only. All │ │ │ │ - * XML subclasses should have their own namespaces object. Use │ │ │ │ - * <setNamespace> to add or set a namespace alias after construction. │ │ │ │ - */ │ │ │ │ - namespaces: { │ │ │ │ - atom: "http://www.w3.org/2005/Atom", │ │ │ │ - georss: "http://www.georss.org/georss" │ │ │ │ - }, │ │ │ │ +OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: feedTitle │ │ │ │ - * {String} Atom feed elements require a title. Default is "untitled". │ │ │ │ + * Property: center │ │ │ │ + * {<OpenLayers.LonLat>} │ │ │ │ */ │ │ │ │ - feedTitle: "untitled", │ │ │ │ + center: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: defaultEntryTitle │ │ │ │ - * {String} Atom entry elements require a title. In cases where one is │ │ │ │ - * not provided in the feature attributes, this will be used. Default │ │ │ │ - * is "untitled". │ │ │ │ + * Property: zoom │ │ │ │ + * {int} │ │ │ │ */ │ │ │ │ - defaultEntryTitle: "untitled", │ │ │ │ + zoom: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: gmlParse │ │ │ │ - * {Object} GML Format object for parsing features │ │ │ │ - * Non-API and only created if necessary │ │ │ │ + * Property: layers │ │ │ │ + * {String} Each character represents the state of the corresponding layer │ │ │ │ + * on the map. │ │ │ │ */ │ │ │ │ - gmlParser: null, │ │ │ │ + layers: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: xy │ │ │ │ - * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x) │ │ │ │ - * For GeoRSS the default is (y,x), therefore: false │ │ │ │ + /** │ │ │ │ + * APIProperty: displayProjection │ │ │ │ + * {<OpenLayers.Projection>} Requires proj4js support. │ │ │ │ + * Projection used when reading the coordinates from the URL. This will │ │ │ │ + * reproject the map coordinates from the URL into the map's │ │ │ │ + * projection. │ │ │ │ + * │ │ │ │ + * If you are using this functionality, be aware that any permalink │ │ │ │ + * which is added to the map will determine the coordinate type which │ │ │ │ + * is read from the URL, which means you should not add permalinks with │ │ │ │ + * different displayProjections to the same map. │ │ │ │ */ │ │ │ │ - xy: false, │ │ │ │ + displayProjection: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.AtomEntry │ │ │ │ - * Create a new parser for Atom. │ │ │ │ + * Constructor: OpenLayers.Control.ArgParser │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Return a list of features from an Atom feed or entry document. │ │ │ │ - │ │ │ │ - * Parameters: │ │ │ │ - * doc - {Element} or {String} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * Array({<OpenLayers.Feature.Vector>}) │ │ │ │ + * Method: getParameters │ │ │ │ */ │ │ │ │ - read: function(doc) { │ │ │ │ - if (typeof doc == "string") { │ │ │ │ - doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); │ │ │ │ + getParameters: function(url) { │ │ │ │ + url = url || window.location.href; │ │ │ │ + var parameters = OpenLayers.Util.getParameters(url); │ │ │ │ + │ │ │ │ + // If we have an anchor in the url use it to split the url │ │ │ │ + var index = url.indexOf('#'); │ │ │ │ + if (index > 0) { │ │ │ │ + // create an url to parse on the getParameters │ │ │ │ + url = '?' + url.substring(index + 1, url.length); │ │ │ │ + │ │ │ │ + OpenLayers.Util.extend(parameters, │ │ │ │ + OpenLayers.Util.getParameters(url)); │ │ │ │ } │ │ │ │ - return this.parseFeatures(doc); │ │ │ │ + return parameters; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Serialize or more feature nodes to Atom documents. │ │ │ │ - * │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the control. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * features - {<OpenLayers.Feature.Vector>} or Array({<OpenLayers.Feature.Vector>}) │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} an Atom entry document if passed one feature node, or a feed │ │ │ │ - * document if passed an array of feature nodes. │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - write: function(features) { │ │ │ │ - var doc; │ │ │ │ - if (OpenLayers.Util.isArray(features)) { │ │ │ │ - doc = this.createElementNSPlus("atom:feed"); │ │ │ │ - doc.appendChild( │ │ │ │ - this.createElementNSPlus("atom:title", { │ │ │ │ - value: this.feedTitle │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ - for (var i = 0, ii = features.length; i < ii; i++) { │ │ │ │ - doc.appendChild(this.buildEntryNode(features[i])); │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ + │ │ │ │ + //make sure we dont already have an arg parser attached │ │ │ │ + for (var i = 0, len = this.map.controls.length; i < len; i++) { │ │ │ │ + var control = this.map.controls[i]; │ │ │ │ + if ((control != this) && │ │ │ │ + (control.CLASS_NAME == "OpenLayers.Control.ArgParser")) { │ │ │ │ + │ │ │ │ + // If a second argparser is added to the map, then we │ │ │ │ + // override the displayProjection to be the one added to the │ │ │ │ + // map. │ │ │ │ + if (control.displayProjection != this.displayProjection) { │ │ │ │ + this.displayProjection = control.displayProjection; │ │ │ │ + } │ │ │ │ + │ │ │ │ + break; │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - doc = this.buildEntryNode(features); │ │ │ │ } │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [doc]); │ │ │ │ - }, │ │ │ │ + if (i == this.map.controls.length) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: buildContentNode │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * content - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} an Atom content node. │ │ │ │ - * │ │ │ │ - * TODO: types other than text. │ │ │ │ - */ │ │ │ │ - buildContentNode: function(content) { │ │ │ │ - var node = this.createElementNSPlus("atom:content", { │ │ │ │ - attributes: { │ │ │ │ - type: content.type || null │ │ │ │ + var args = this.getParameters(); │ │ │ │ + // Be careful to set layer first, to not trigger unnecessary layer loads │ │ │ │ + if (args.layers) { │ │ │ │ + this.layers = args.layers; │ │ │ │ + │ │ │ │ + // when we add a new layer, set its visibility │ │ │ │ + this.map.events.register('addlayer', this, │ │ │ │ + this.configureLayers); │ │ │ │ + this.configureLayers(); │ │ │ │ } │ │ │ │ - }); │ │ │ │ - if (content.src) { │ │ │ │ - node.setAttribute("src", content.src); │ │ │ │ - } else { │ │ │ │ - if (content.type == "text" || content.type == null) { │ │ │ │ - node.appendChild( │ │ │ │ - this.createTextNode(content.value) │ │ │ │ - ); │ │ │ │ - } else if (content.type == "html") { │ │ │ │ - if (typeof content.value != "string") { │ │ │ │ - throw "HTML content must be in form of an escaped string"; │ │ │ │ + if (args.lat && args.lon) { │ │ │ │ + this.center = new OpenLayers.LonLat(parseFloat(args.lon), │ │ │ │ + parseFloat(args.lat)); │ │ │ │ + if (args.zoom) { │ │ │ │ + this.zoom = parseFloat(args.zoom); │ │ │ │ } │ │ │ │ - node.appendChild( │ │ │ │ - this.createTextNode(content.value) │ │ │ │ - ); │ │ │ │ - } else if (content.type == "xhtml") { │ │ │ │ - node.appendChild(content.value); │ │ │ │ - } else if (content.type == "xhtml" || │ │ │ │ - content.type.match(/(\+|\/)xml$/)) { │ │ │ │ - node.appendChild(content.value); │ │ │ │ - } else { // MUST be a valid Base64 encoding │ │ │ │ - node.appendChild( │ │ │ │ - this.createTextNode(content.value) │ │ │ │ - ); │ │ │ │ + │ │ │ │ + // when we add a new baselayer to see when we can set the center │ │ │ │ + this.map.events.register('changebaselayer', this, │ │ │ │ + this.setCenter); │ │ │ │ + this.setCenter(); │ │ │ │ } │ │ │ │ } │ │ │ │ - return node; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: buildEntryNode │ │ │ │ - * Build an Atom entry node from a feature object. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} an Atom entry node. │ │ │ │ - * │ │ │ │ - * These entries are geared for publication using AtomPub. │ │ │ │ - * │ │ │ │ - * TODO: support extension elements │ │ │ │ + /** │ │ │ │ + * Method: setCenter │ │ │ │ + * As soon as a baseLayer has been loaded, we center and zoom │ │ │ │ + * ...and remove the handler. │ │ │ │ */ │ │ │ │ - buildEntryNode: function(feature) { │ │ │ │ - var attrib = feature.attributes; │ │ │ │ - var atomAttrib = attrib.atom || {}; │ │ │ │ - var entryNode = this.createElementNSPlus("atom:entry"); │ │ │ │ + setCenter: function() { │ │ │ │ │ │ │ │ - // atom:author │ │ │ │ - if (atomAttrib.authors) { │ │ │ │ - var authors = OpenLayers.Util.isArray(atomAttrib.authors) ? │ │ │ │ - atomAttrib.authors : [atomAttrib.authors]; │ │ │ │ - for (var i = 0, ii = authors.length; i < ii; i++) { │ │ │ │ - entryNode.appendChild( │ │ │ │ - this.buildPersonConstructNode( │ │ │ │ - "author", authors[i] │ │ │ │ - ) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + if (this.map.baseLayer) { │ │ │ │ + //dont need to listen for this one anymore │ │ │ │ + this.map.events.unregister('changebaselayer', this, │ │ │ │ + this.setCenter); │ │ │ │ │ │ │ │ - // atom:category │ │ │ │ - if (atomAttrib.categories) { │ │ │ │ - var categories = OpenLayers.Util.isArray(atomAttrib.categories) ? │ │ │ │ - atomAttrib.categories : [atomAttrib.categories]; │ │ │ │ - var category; │ │ │ │ - for (var i = 0, ii = categories.length; i < ii; i++) { │ │ │ │ - category = categories[i]; │ │ │ │ - entryNode.appendChild( │ │ │ │ - this.createElementNSPlus("atom:category", { │ │ │ │ - attributes: { │ │ │ │ - term: category.term, │ │ │ │ - scheme: category.scheme || null, │ │ │ │ - label: category.label || null │ │ │ │ - } │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ + if (this.displayProjection) { │ │ │ │ + this.center.transform(this.displayProjection, │ │ │ │ + this.map.getProjectionObject()); │ │ │ │ } │ │ │ │ - } │ │ │ │ │ │ │ │ - // atom:content │ │ │ │ - if (atomAttrib.content) { │ │ │ │ - entryNode.appendChild(this.buildContentNode(atomAttrib.content)); │ │ │ │ + this.map.setCenter(this.center, this.zoom); │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // atom:contributor │ │ │ │ - if (atomAttrib.contributors) { │ │ │ │ - var contributors = OpenLayers.Util.isArray(atomAttrib.contributors) ? │ │ │ │ - atomAttrib.contributors : [atomAttrib.contributors]; │ │ │ │ - for (var i = 0, ii = contributors.length; i < ii; i++) { │ │ │ │ - entryNode.appendChild( │ │ │ │ - this.buildPersonConstructNode( │ │ │ │ - "contributor", │ │ │ │ - contributors[i] │ │ │ │ - ) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: configureLayers │ │ │ │ + * As soon as all the layers are loaded, cycle through them and │ │ │ │ + * hide or show them. │ │ │ │ + */ │ │ │ │ + configureLayers: function() { │ │ │ │ │ │ │ │ - // atom:id │ │ │ │ - if (feature.fid) { │ │ │ │ - entryNode.appendChild( │ │ │ │ - this.createElementNSPlus("atom:id", { │ │ │ │ - value: feature.fid │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ + if (this.layers.length == this.map.layers.length) { │ │ │ │ + this.map.events.unregister('addlayer', this, this.configureLayers); │ │ │ │ │ │ │ │ - // atom:link │ │ │ │ - if (atomAttrib.links) { │ │ │ │ - var links = OpenLayers.Util.isArray(atomAttrib.links) ? │ │ │ │ - atomAttrib.links : [atomAttrib.links]; │ │ │ │ - var link; │ │ │ │ - for (var i = 0, ii = links.length; i < ii; i++) { │ │ │ │ - link = links[i]; │ │ │ │ - entryNode.appendChild( │ │ │ │ - this.createElementNSPlus("atom:link", { │ │ │ │ - attributes: { │ │ │ │ - href: link.href, │ │ │ │ - rel: link.rel || null, │ │ │ │ - type: link.type || null, │ │ │ │ - hreflang: link.hreflang || null, │ │ │ │ - title: link.title || null, │ │ │ │ - length: link.length || null │ │ │ │ - } │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ + for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ + │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + var c = this.layers.charAt(i); │ │ │ │ + │ │ │ │ + if (c == "B") { │ │ │ │ + this.map.setBaseLayer(layer); │ │ │ │ + } else if ((c == "T") || (c == "F")) { │ │ │ │ + layer.setVisibility(c == "T"); │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // atom:published │ │ │ │ - if (atomAttrib.published) { │ │ │ │ - entryNode.appendChild( │ │ │ │ - this.createElementNSPlus("atom:published", { │ │ │ │ - value: atomAttrib.published │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Control.ArgParser" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/Permalink.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - // atom:rights │ │ │ │ - if (atomAttrib.rights) { │ │ │ │ - entryNode.appendChild( │ │ │ │ - this.createElementNSPlus("atom:rights", { │ │ │ │ - value: atomAttrib.rights │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - // atom:source not implemented │ │ │ │ │ │ │ │ - // atom:summary │ │ │ │ - if (atomAttrib.summary || attrib.description) { │ │ │ │ - entryNode.appendChild( │ │ │ │ - this.createElementNSPlus("atom:summary", { │ │ │ │ - value: atomAttrib.summary || attrib.description │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Control/ArgParser.js │ │ │ │ + * @requires OpenLayers/Lang.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - // atom:title │ │ │ │ - entryNode.appendChild( │ │ │ │ - this.createElementNSPlus("atom:title", { │ │ │ │ - value: atomAttrib.title || attrib.title || this.defaultEntryTitle │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.Permalink │ │ │ │ + * The Permalink control is hyperlink that will return the user to the │ │ │ │ + * current map view. By default it is drawn in the lower right corner of the │ │ │ │ + * map. The href is updated as the map is zoomed, panned and whilst layers │ │ │ │ + * are switched. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - // atom:updated │ │ │ │ - if (atomAttrib.updated) { │ │ │ │ - entryNode.appendChild( │ │ │ │ - this.createElementNSPlus("atom:updated", { │ │ │ │ - value: atomAttrib.updated │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: argParserClass │ │ │ │ + * {Class} The ArgParser control class (not instance) to use with this │ │ │ │ + * control. │ │ │ │ + */ │ │ │ │ + argParserClass: OpenLayers.Control.ArgParser, │ │ │ │ │ │ │ │ - // georss:where │ │ │ │ - if (feature.geometry) { │ │ │ │ - var whereNode = this.createElementNSPlus("georss:where"); │ │ │ │ - whereNode.appendChild( │ │ │ │ - this.buildGeometryNode(feature.geometry) │ │ │ │ - ); │ │ │ │ - entryNode.appendChild(whereNode); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: element │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + element: null, │ │ │ │ │ │ │ │ - return entryNode; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: anchor │ │ │ │ + * {Boolean} This option changes 3 things: │ │ │ │ + * the character '#' is used in place of the character '?', │ │ │ │ + * the window.href is updated if no element is provided. │ │ │ │ + * When this option is set to true it's not recommend to provide │ │ │ │ + * a base without provide an element. │ │ │ │ + */ │ │ │ │ + anchor: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: initGmlParser │ │ │ │ - * Creates a GML parser. │ │ │ │ + /** │ │ │ │ + * APIProperty: base │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - initGmlParser: function() { │ │ │ │ - this.gmlParser = new OpenLayers.Format.GML.v3({ │ │ │ │ - xy: this.xy, │ │ │ │ - featureNS: "http://example.com#feature", │ │ │ │ - internalProjection: this.internalProjection, │ │ │ │ - externalProjection: this.externalProjection │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ + base: '', │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: buildGeometryNode │ │ │ │ - * builds a GeoRSS node with a given geometry │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A gml node. │ │ │ │ + /** │ │ │ │ + * APIProperty: displayProjection │ │ │ │ + * {<OpenLayers.Projection>} Requires proj4js support. Projection used │ │ │ │ + * when creating the coordinates in the link. This will reproject the │ │ │ │ + * map coordinates into display coordinates. If you are using this │ │ │ │ + * functionality, the permalink which is last added to the map will │ │ │ │ + * determine the coordinate type which is read from the URL, which │ │ │ │ + * means you should not add permalinks with different │ │ │ │ + * displayProjections to the same map. │ │ │ │ */ │ │ │ │ - buildGeometryNode: function(geometry) { │ │ │ │ - if (!this.gmlParser) { │ │ │ │ - this.initGmlParser(); │ │ │ │ - } │ │ │ │ - var node = this.gmlParser.writeNode("feature:_geometry", geometry); │ │ │ │ - return node.firstChild; │ │ │ │ - }, │ │ │ │ + displayProjection: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: buildPersonConstructNode │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * value - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} an Atom person construct node. │ │ │ │ + * Constructor: OpenLayers.Control.Permalink │ │ │ │ * │ │ │ │ - * Example: │ │ │ │ - * >>> buildPersonConstructNode("author", {name: "John Smith"}) │ │ │ │ - * {<author><name>John Smith</name></author>} │ │ │ │ + * Parameters: │ │ │ │ + * element - {DOMElement} │ │ │ │ + * base - {String} │ │ │ │ + * options - {Object} options to the control. │ │ │ │ * │ │ │ │ - * TODO: how to specify extension elements? Add to the oNames array? │ │ │ │ + * Or for anchor: │ │ │ │ + * options - {Object} options to the control. │ │ │ │ */ │ │ │ │ - buildPersonConstructNode: function(name, value) { │ │ │ │ - var oNames = ["uri", "email"]; │ │ │ │ - var personNode = this.createElementNSPlus("atom:" + name); │ │ │ │ - personNode.appendChild( │ │ │ │ - this.createElementNSPlus("atom:name", { │ │ │ │ - value: value.name │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ - for (var i = 0, ii = oNames.length; i < ii; i++) { │ │ │ │ - if (value[oNames[i]]) { │ │ │ │ - personNode.appendChild( │ │ │ │ - this.createElementNSPlus("atom:" + oNames[i], { │ │ │ │ - value: value[oNames[i]] │ │ │ │ - }) │ │ │ │ - ); │ │ │ │ + initialize: function(element, base, options) { │ │ │ │ + if (element !== null && typeof element == 'object' && !OpenLayers.Util.isElement(element)) { │ │ │ │ + options = element; │ │ │ │ + this.base = document.location.href; │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + if (this.element != null) { │ │ │ │ + this.element = OpenLayers.Util.getElement(this.element); │ │ │ │ } │ │ │ │ + } else { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + this.element = OpenLayers.Util.getElement(element); │ │ │ │ + this.base = base || document.location.href; │ │ │ │ } │ │ │ │ - return personNode; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getFirstChildValue │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * nsuri - {String} Child node namespace uri ("*" for any). │ │ │ │ - * name - {String} Child node name. │ │ │ │ - * def - {String} Optional string default to return if no child found. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The value of the first child with the given tag name. Returns │ │ │ │ - * default value or empty string if none found. │ │ │ │ + * APIMethod: destroy │ │ │ │ */ │ │ │ │ - getFirstChildValue: function(node, nsuri, name, def) { │ │ │ │ - var value; │ │ │ │ - var nodes = this.getElementsByTagNameNS(node, nsuri, name); │ │ │ │ - if (nodes && nodes.length > 0) { │ │ │ │ - value = this.getChildValue(nodes[0], def); │ │ │ │ - } else { │ │ │ │ - value = def; │ │ │ │ + destroy: function() { │ │ │ │ + if (this.element && this.element.parentNode == this.div) { │ │ │ │ + this.div.removeChild(this.element); │ │ │ │ + this.element = null; │ │ │ │ } │ │ │ │ - return value; │ │ │ │ + if (this.map) { │ │ │ │ + this.map.events.unregister('moveend', this, this.updateLink); │ │ │ │ + } │ │ │ │ + │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseFeature │ │ │ │ - * Parse feature from an Atom entry node.. │ │ │ │ - * │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the control. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} An Atom entry or feed node. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - parseFeature: function(node) { │ │ │ │ - var atomAttrib = {}; │ │ │ │ - var value = null; │ │ │ │ - var nodes = null; │ │ │ │ - var attval = null; │ │ │ │ - var atomns = this.namespaces.atom; │ │ │ │ - │ │ │ │ - // atomAuthor* │ │ │ │ - this.parsePersonConstructs(node, "author", atomAttrib); │ │ │ │ - │ │ │ │ - // atomCategory* │ │ │ │ - nodes = this.getElementsByTagNameNS(node, atomns, "category"); │ │ │ │ - if (nodes.length > 0) { │ │ │ │ - atomAttrib.categories = []; │ │ │ │ - } │ │ │ │ - for (var i = 0, ii = nodes.length; i < ii; i++) { │ │ │ │ - value = {}; │ │ │ │ - value.term = nodes[i].getAttribute("term"); │ │ │ │ - attval = nodes[i].getAttribute("scheme"); │ │ │ │ - if (attval) { │ │ │ │ - value.scheme = attval; │ │ │ │ - } │ │ │ │ - attval = nodes[i].getAttribute("label"); │ │ │ │ - if (attval) { │ │ │ │ - value.label = attval; │ │ │ │ - } │ │ │ │ - atomAttrib.categories.push(value); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // atomContent? │ │ │ │ - nodes = this.getElementsByTagNameNS(node, atomns, "content"); │ │ │ │ - if (nodes.length > 0) { │ │ │ │ - value = {}; │ │ │ │ - attval = nodes[0].getAttribute("type"); │ │ │ │ - if (attval) { │ │ │ │ - value.type = attval; │ │ │ │ - } │ │ │ │ - attval = nodes[0].getAttribute("src"); │ │ │ │ - if (attval) { │ │ │ │ - value.src = attval; │ │ │ │ - } else { │ │ │ │ - if (value.type == "text" || │ │ │ │ - value.type == "html" || │ │ │ │ - value.type == null) { │ │ │ │ - value.value = this.getFirstChildValue( │ │ │ │ - node, │ │ │ │ - atomns, │ │ │ │ - "content", │ │ │ │ - null │ │ │ │ - ); │ │ │ │ - } else if (value.type == "xhtml" || │ │ │ │ - value.type.match(/(\+|\/)xml$/)) { │ │ │ │ - value.value = this.getChildEl(nodes[0]); │ │ │ │ - } else { // MUST be base64 encoded │ │ │ │ - value.value = this.getFirstChildValue( │ │ │ │ - node, │ │ │ │ - atomns, │ │ │ │ - "content", │ │ │ │ - null │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - atomAttrib.content = value; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - // atomContributor* │ │ │ │ - this.parsePersonConstructs(node, "contributor", atomAttrib); │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ │ │ │ │ - // atomId │ │ │ │ - atomAttrib.id = this.getFirstChildValue(node, atomns, "id", null); │ │ │ │ + //make sure we have an arg parser attached │ │ │ │ + for (var i = 0, len = this.map.controls.length; i < len; i++) { │ │ │ │ + var control = this.map.controls[i]; │ │ │ │ + if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) { │ │ │ │ │ │ │ │ - // atomLink* │ │ │ │ - nodes = this.getElementsByTagNameNS(node, atomns, "link"); │ │ │ │ - if (nodes.length > 0) { │ │ │ │ - atomAttrib.links = new Array(nodes.length); │ │ │ │ - } │ │ │ │ - var oAtts = ["rel", "type", "hreflang", "title", "length"]; │ │ │ │ - for (var i = 0, ii = nodes.length; i < ii; i++) { │ │ │ │ - value = {}; │ │ │ │ - value.href = nodes[i].getAttribute("href"); │ │ │ │ - for (var j = 0, jj = oAtts.length; j < jj; j++) { │ │ │ │ - attval = nodes[i].getAttribute(oAtts[j]); │ │ │ │ - if (attval) { │ │ │ │ - value[oAtts[j]] = attval; │ │ │ │ + // If a permalink is added to the map, and an ArgParser already │ │ │ │ + // exists, we override the displayProjection to be the one │ │ │ │ + // on the permalink. │ │ │ │ + if (control.displayProjection != this.displayProjection) { │ │ │ │ + this.displayProjection = control.displayProjection; │ │ │ │ } │ │ │ │ + │ │ │ │ + break; │ │ │ │ } │ │ │ │ - atomAttrib.links[i] = value; │ │ │ │ } │ │ │ │ - │ │ │ │ - // atomPublished? │ │ │ │ - value = this.getFirstChildValue(node, atomns, "published", null); │ │ │ │ - if (value) { │ │ │ │ - atomAttrib.published = value; │ │ │ │ + if (i == this.map.controls.length) { │ │ │ │ + this.map.addControl(new this.argParserClass({ │ │ │ │ + 'displayProjection': this.displayProjection │ │ │ │ + })); │ │ │ │ } │ │ │ │ │ │ │ │ - // atomRights? │ │ │ │ - value = this.getFirstChildValue(node, atomns, "rights", null); │ │ │ │ - if (value) { │ │ │ │ - atomAttrib.rights = value; │ │ │ │ - } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // atomSource? -- not implemented │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ │ │ │ │ - // atomSummary? │ │ │ │ - value = this.getFirstChildValue(node, atomns, "summary", null); │ │ │ │ - if (value) { │ │ │ │ - atomAttrib.summary = value; │ │ │ │ + if (!this.element && !this.anchor) { │ │ │ │ + this.element = document.createElement("a"); │ │ │ │ + this.element.innerHTML = OpenLayers.i18n("Permalink"); │ │ │ │ + this.element.href = ""; │ │ │ │ + this.div.appendChild(this.element); │ │ │ │ } │ │ │ │ + this.map.events.on({ │ │ │ │ + 'moveend': this.updateLink, │ │ │ │ + 'changelayer': this.updateLink, │ │ │ │ + 'changebaselayer': this.updateLink, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ │ │ │ │ - // atomTitle │ │ │ │ - atomAttrib.title = this.getFirstChildValue( │ │ │ │ - node, atomns, "title", null │ │ │ │ - ); │ │ │ │ - │ │ │ │ - // atomUpdated │ │ │ │ - atomAttrib.updated = this.getFirstChildValue( │ │ │ │ - node, atomns, "updated", null │ │ │ │ - ); │ │ │ │ + // Make it so there is at least a link even though the map may not have │ │ │ │ + // moved yet. │ │ │ │ + this.updateLink(); │ │ │ │ │ │ │ │ - var featureAttrib = { │ │ │ │ - title: atomAttrib.title, │ │ │ │ - description: atomAttrib.summary, │ │ │ │ - atom: atomAttrib │ │ │ │ - }; │ │ │ │ - var geometry = this.parseLocations(node)[0]; │ │ │ │ - var feature = new OpenLayers.Feature.Vector(geometry, featureAttrib); │ │ │ │ - feature.fid = atomAttrib.id; │ │ │ │ - return feature; │ │ │ │ + return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseFeatures │ │ │ │ - * Return features from an Atom entry or feed. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} An Atom entry or feed node. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * Array({<OpenLayers.Feature.Vector>}) │ │ │ │ + * Method: updateLink │ │ │ │ */ │ │ │ │ - parseFeatures: function(node) { │ │ │ │ - var features = []; │ │ │ │ - var entries = this.getElementsByTagNameNS( │ │ │ │ - node, this.namespaces.atom, "entry" │ │ │ │ - ); │ │ │ │ - if (entries.length == 0) { │ │ │ │ - entries = [node]; │ │ │ │ + updateLink: function() { │ │ │ │ + var separator = this.anchor ? '#' : '?'; │ │ │ │ + var href = this.base; │ │ │ │ + var anchor = null; │ │ │ │ + if (href.indexOf("#") != -1 && this.anchor == false) { │ │ │ │ + anchor = href.substring(href.indexOf("#"), href.length); │ │ │ │ } │ │ │ │ - for (var i = 0, ii = entries.length; i < ii; i++) { │ │ │ │ - features.push(this.parseFeature(entries[i])); │ │ │ │ + if (href.indexOf(separator) != -1) { │ │ │ │ + href = href.substring(0, href.indexOf(separator)); │ │ │ │ + } │ │ │ │ + var splits = href.split("#"); │ │ │ │ + href = splits[0] + separator + OpenLayers.Util.getParameterString(this.createParams()); │ │ │ │ + if (anchor) { │ │ │ │ + href += anchor; │ │ │ │ + } │ │ │ │ + if (this.anchor && !this.element) { │ │ │ │ + window.location.href = href; │ │ │ │ + } else { │ │ │ │ + this.element.href = href; │ │ │ │ } │ │ │ │ - return features; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseLocations │ │ │ │ - * Parse the locations from an Atom entry or feed. │ │ │ │ - * │ │ │ │ + * APIMethod: createParams │ │ │ │ + * Creates the parameters that need to be encoded into the permalink url. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} An Atom entry or feed node. │ │ │ │ - * │ │ │ │ + * center - {<OpenLayers.LonLat>} center to encode in the permalink. │ │ │ │ + * Defaults to the current map center. │ │ │ │ + * zoom - {Integer} zoom level to encode in the permalink. Defaults to the │ │ │ │ + * current map zoom level. │ │ │ │ + * layers - {Array(<OpenLayers.Layer>)} layers to encode in the permalink. │ │ │ │ + * Defaults to the current map layers. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * Array({<OpenLayers.Geometry>}) │ │ │ │ + * {Object} Hash of parameters that will be url-encoded into the │ │ │ │ + * permalink. │ │ │ │ */ │ │ │ │ - parseLocations: function(node) { │ │ │ │ - var georssns = this.namespaces.georss; │ │ │ │ + createParams: function(center, zoom, layers) { │ │ │ │ + center = center || this.map.getCenter(); │ │ │ │ │ │ │ │ - var locations = { │ │ │ │ - components: [] │ │ │ │ - }; │ │ │ │ - var where = this.getElementsByTagNameNS(node, georssns, "where"); │ │ │ │ - if (where && where.length > 0) { │ │ │ │ - if (!this.gmlParser) { │ │ │ │ - this.initGmlParser(); │ │ │ │ - } │ │ │ │ - for (var i = 0, ii = where.length; i < ii; i++) { │ │ │ │ - this.gmlParser.readChildNodes(where[i], locations); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + var params = OpenLayers.Util.getParameters(this.base); │ │ │ │ │ │ │ │ - var components = locations.components; │ │ │ │ - var point = this.getElementsByTagNameNS(node, georssns, "point"); │ │ │ │ - if (point && point.length > 0) { │ │ │ │ - for (var i = 0, ii = point.length; i < ii; i++) { │ │ │ │ - var xy = OpenLayers.String.trim( │ │ │ │ - point[i].firstChild.nodeValue │ │ │ │ - ).split(/\s+/); │ │ │ │ - if (xy.length != 2) { │ │ │ │ - xy = OpenLayers.String.trim( │ │ │ │ - point[i].firstChild.nodeValue │ │ │ │ - ).split(/\s*,\s*/); │ │ │ │ - } │ │ │ │ - components.push(new OpenLayers.Geometry.Point(xy[1], xy[0])); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + // If there's still no center, map is not initialized yet. │ │ │ │ + // Break out of this function, and simply return the params from the │ │ │ │ + // base link. │ │ │ │ + if (center) { │ │ │ │ │ │ │ │ - var line = this.getElementsByTagNameNS(node, georssns, "line"); │ │ │ │ - if (line && line.length > 0) { │ │ │ │ - var coords; │ │ │ │ - var p; │ │ │ │ - var points; │ │ │ │ - for (var i = 0, ii = line.length; i < ii; i++) { │ │ │ │ - coords = OpenLayers.String.trim( │ │ │ │ - line[i].firstChild.nodeValue │ │ │ │ - ).split(/\s+/); │ │ │ │ - points = []; │ │ │ │ - for (var j = 0, jj = coords.length; j < jj; j += 2) { │ │ │ │ - p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]); │ │ │ │ - points.push(p); │ │ │ │ - } │ │ │ │ - components.push( │ │ │ │ - new OpenLayers.Geometry.LineString(points) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + //zoom │ │ │ │ + params.zoom = zoom || this.map.getZoom(); │ │ │ │ │ │ │ │ - var polygon = this.getElementsByTagNameNS(node, georssns, "polygon"); │ │ │ │ - if (polygon && polygon.length > 0) { │ │ │ │ - var coords; │ │ │ │ - var p; │ │ │ │ - var points; │ │ │ │ - for (var i = 0, ii = polygon.length; i < ii; i++) { │ │ │ │ - coords = OpenLayers.String.trim( │ │ │ │ - polygon[i].firstChild.nodeValue │ │ │ │ - ).split(/\s+/); │ │ │ │ - points = []; │ │ │ │ - for (var j = 0, jj = coords.length; j < jj; j += 2) { │ │ │ │ - p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]); │ │ │ │ - points.push(p); │ │ │ │ - } │ │ │ │ - components.push( │ │ │ │ - new OpenLayers.Geometry.Polygon( │ │ │ │ - [new OpenLayers.Geometry.LinearRing(points)] │ │ │ │ - ) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + //lon,lat │ │ │ │ + var lat = center.lat; │ │ │ │ + var lon = center.lon; │ │ │ │ │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - for (var i = 0, ii = components.length; i < ii; i++) { │ │ │ │ - if (components[i]) { │ │ │ │ - components[i].transform( │ │ │ │ - this.externalProjection, │ │ │ │ - this.internalProjection │ │ │ │ - ); │ │ │ │ - } │ │ │ │ + if (this.displayProjection) { │ │ │ │ + var mapPosition = OpenLayers.Projection.transform({ │ │ │ │ + x: lon, │ │ │ │ + y: lat │ │ │ │ + }, │ │ │ │ + this.map.getProjectionObject(), │ │ │ │ + this.displayProjection); │ │ │ │ + lon = mapPosition.x; │ │ │ │ + lat = mapPosition.y; │ │ │ │ } │ │ │ │ - } │ │ │ │ + params.lat = Math.round(lat * 100000) / 100000; │ │ │ │ + params.lon = Math.round(lon * 100000) / 100000; │ │ │ │ │ │ │ │ - return components; │ │ │ │ - }, │ │ │ │ + //layers │ │ │ │ + layers = layers || this.map.layers; │ │ │ │ + params.layers = ''; │ │ │ │ + for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ + var layer = layers[i]; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: parsePersonConstruct │ │ │ │ - * Parse Atom person constructs from an Atom entry node. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} An Atom entry or feed node. │ │ │ │ - * name - {String} Construcy name ("author" or "contributor") │ │ │ │ - * data = {Object} Object in which to put parsed persons. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * An {Object}. │ │ │ │ - */ │ │ │ │ - parsePersonConstructs: function(node, name, data) { │ │ │ │ - var persons = []; │ │ │ │ - var atomns = this.namespaces.atom; │ │ │ │ - var nodes = this.getElementsByTagNameNS(node, atomns, name); │ │ │ │ - var oAtts = ["uri", "email"]; │ │ │ │ - for (var i = 0, ii = nodes.length; i < ii; i++) { │ │ │ │ - var value = {}; │ │ │ │ - value.name = this.getFirstChildValue( │ │ │ │ - nodes[i], │ │ │ │ - atomns, │ │ │ │ - "name", │ │ │ │ - null │ │ │ │ - ); │ │ │ │ - for (var j = 0, jj = oAtts.length; j < jj; j++) { │ │ │ │ - var attval = this.getFirstChildValue( │ │ │ │ - nodes[i], │ │ │ │ - atomns, │ │ │ │ - oAtts[j], │ │ │ │ - null); │ │ │ │ - if (attval) { │ │ │ │ - value[oAtts[j]] = attval; │ │ │ │ + if (layer.isBaseLayer) { │ │ │ │ + params.layers += (layer == this.map.baseLayer) ? "B" : "0"; │ │ │ │ + } else { │ │ │ │ + params.layers += (layer.getVisibility()) ? "T" : "F"; │ │ │ │ } │ │ │ │ } │ │ │ │ - persons.push(value); │ │ │ │ - } │ │ │ │ - if (persons.length > 0) { │ │ │ │ - data[name + "s"] = persons; │ │ │ │ } │ │ │ │ + │ │ │ │ + return params; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.Atom" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Permalink" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/GeoRSS.js │ │ │ │ + OpenLayers/Control/NavigationHistory.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ - * @requires OpenLayers/Geometry/Point.js │ │ │ │ - * @requires OpenLayers/Geometry/LineString.js │ │ │ │ - * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Control/Button.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.GeoRSS │ │ │ │ - * Read/write GeoRSS parser. Create a new instance with the │ │ │ │ - * <OpenLayers.Format.GeoRSS> constructor. │ │ │ │ + * Class: OpenLayers.Control.NavigationHistory │ │ │ │ + * A navigation history control. This is a meta-control, that creates two │ │ │ │ + * dependent controls: <previous> and <next>. Call the trigger method │ │ │ │ + * on the <previous> and <next> controls to restore previous and next │ │ │ │ + * history states. The previous and next controls will become active │ │ │ │ + * when there are available states to restore and will become deactive │ │ │ │ + * when there are no states to restore. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ +OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: rssns │ │ │ │ - * {String} RSS namespace to use. Defaults to │ │ │ │ - * "http://backend.userland.com/rss2" │ │ │ │ + * Property: type │ │ │ │ + * {String} Note that this control is not intended to be added directly │ │ │ │ + * to a control panel. Instead, add the sub-controls previous and │ │ │ │ + * next. These sub-controls are button type controls that activate │ │ │ │ + * and deactivate themselves. If this parent control is added to │ │ │ │ + * a panel, it will act as a toggle. │ │ │ │ */ │ │ │ │ - rssns: "http://backend.userland.com/rss2", │ │ │ │ + type: OpenLayers.Control.TYPE_TOGGLE, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: featurens │ │ │ │ - * {String} Feature Attributes namespace. Defaults to │ │ │ │ - * "http://mapserver.gis.umn.edu/mapserver" │ │ │ │ + * APIProperty: previous │ │ │ │ + * {<OpenLayers.Control>} A button type control whose trigger method restores │ │ │ │ + * the previous state managed by this control. │ │ │ │ */ │ │ │ │ - featureNS: "http://mapserver.gis.umn.edu/mapserver", │ │ │ │ + previous: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: georssns │ │ │ │ - * {String} GeoRSS namespace to use. Defaults to │ │ │ │ - * "http://www.georss.org/georss" │ │ │ │ + * APIProperty: previousOptions │ │ │ │ + * {Object} Set this property on the options argument of the constructor │ │ │ │ + * to set optional properties on the <previous> control. │ │ │ │ */ │ │ │ │ - georssns: "http://www.georss.org/georss", │ │ │ │ + previousOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: geons │ │ │ │ - * {String} W3C Geo namespace to use. Defaults to │ │ │ │ - * "http://www.w3.org/2003/01/geo/wgs84_pos#" │ │ │ │ + * APIProperty: next │ │ │ │ + * {<OpenLayers.Control>} A button type control whose trigger method restores │ │ │ │ + * the next state managed by this control. │ │ │ │ */ │ │ │ │ - geons: "http://www.w3.org/2003/01/geo/wgs84_pos#", │ │ │ │ + next: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: featureTitle │ │ │ │ - * {String} Default title for features. Defaults to "Untitled" │ │ │ │ + * APIProperty: nextOptions │ │ │ │ + * {Object} Set this property on the options argument of the constructor │ │ │ │ + * to set optional properties on the <next> control. │ │ │ │ */ │ │ │ │ - featureTitle: "Untitled", │ │ │ │ + nextOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: featureDescription │ │ │ │ - * {String} Default description for features. Defaults to "No Description" │ │ │ │ + * APIProperty: limit │ │ │ │ + * {Integer} Optional limit on the number of history items to retain. If │ │ │ │ + * null, there is no limit. Default is 50. │ │ │ │ */ │ │ │ │ - featureDescription: "No Description", │ │ │ │ + limit: 50, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: gmlParse │ │ │ │ - * {Object} GML Format object for parsing features │ │ │ │ - * Non-API and only created if necessary │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * true. │ │ │ │ */ │ │ │ │ - gmlParser: null, │ │ │ │ + autoActivate: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: xy │ │ │ │ - * {Boolean} Order of the GML coordinate: true:(x,y) or false:(y,x) │ │ │ │ - * For GeoRSS the default is (y,x), therefore: false │ │ │ │ + * Property: clearOnDeactivate │ │ │ │ + * {Boolean} Clear the history when the control is deactivated. Default │ │ │ │ + * is false. │ │ │ │ */ │ │ │ │ - xy: false, │ │ │ │ + clearOnDeactivate: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.GeoRSS │ │ │ │ - * Create a new parser for GeoRSS. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * Property: registry │ │ │ │ + * {Object} An object with keys corresponding to event types. Values │ │ │ │ + * are functions that return an object representing the current state. │ │ │ │ */ │ │ │ │ + registry: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createGeometryFromItem │ │ │ │ - * Return a geometry from a GeoRSS Item. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * item - {DOMElement} A GeoRSS item node. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} A geometry representing the node. │ │ │ │ + * Property: nextStack │ │ │ │ + * {Array} Array of items in the history. │ │ │ │ */ │ │ │ │ - createGeometryFromItem: function(item) { │ │ │ │ - var point = this.getElementsByTagNameNS(item, this.georssns, "point"); │ │ │ │ - var lat = this.getElementsByTagNameNS(item, this.geons, 'lat'); │ │ │ │ - var lon = this.getElementsByTagNameNS(item, this.geons, 'long'); │ │ │ │ - │ │ │ │ - var line = this.getElementsByTagNameNS(item, │ │ │ │ - this.georssns, │ │ │ │ - "line"); │ │ │ │ - var polygon = this.getElementsByTagNameNS(item, │ │ │ │ - this.georssns, │ │ │ │ - "polygon"); │ │ │ │ - var where = this.getElementsByTagNameNS(item, │ │ │ │ - this.georssns, │ │ │ │ - "where"); │ │ │ │ - var box = this.getElementsByTagNameNS(item, │ │ │ │ - this.georssns, │ │ │ │ - "box"); │ │ │ │ - │ │ │ │ - if (point.length > 0 || (lat.length > 0 && lon.length > 0)) { │ │ │ │ - var location; │ │ │ │ - if (point.length > 0) { │ │ │ │ - location = OpenLayers.String.trim( │ │ │ │ - point[0].firstChild.nodeValue).split(/\s+/); │ │ │ │ - if (location.length != 2) { │ │ │ │ - location = OpenLayers.String.trim( │ │ │ │ - point[0].firstChild.nodeValue).split(/\s*,\s*/); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - location = [parseFloat(lat[0].firstChild.nodeValue), │ │ │ │ - parseFloat(lon[0].firstChild.nodeValue) │ │ │ │ - ]; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var geometry = new OpenLayers.Geometry.Point(location[1], location[0]); │ │ │ │ + nextStack: null, │ │ │ │ │ │ │ │ - } else if (line.length > 0) { │ │ │ │ - var coords = OpenLayers.String.trim(this.getChildValue(line[0])).split(/\s+/); │ │ │ │ - var components = []; │ │ │ │ - var point; │ │ │ │ - for (var i = 0, len = coords.length; i < len; i += 2) { │ │ │ │ - point = new OpenLayers.Geometry.Point(coords[i + 1], coords[i]); │ │ │ │ - components.push(point); │ │ │ │ - } │ │ │ │ - geometry = new OpenLayers.Geometry.LineString(components); │ │ │ │ - } else if (polygon.length > 0) { │ │ │ │ - var coords = OpenLayers.String.trim(this.getChildValue(polygon[0])).split(/\s+/); │ │ │ │ - var components = []; │ │ │ │ - var point; │ │ │ │ - for (var i = 0, len = coords.length; i < len; i += 2) { │ │ │ │ - point = new OpenLayers.Geometry.Point(coords[i + 1], coords[i]); │ │ │ │ - components.push(point); │ │ │ │ - } │ │ │ │ - geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]); │ │ │ │ - } else if (where.length > 0) { │ │ │ │ - if (!this.gmlParser) { │ │ │ │ - this.gmlParser = new OpenLayers.Format.GML({ │ │ │ │ - 'xy': this.xy │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - var feature = this.gmlParser.parseFeature(where[0]); │ │ │ │ - geometry = feature.geometry; │ │ │ │ - } else if (box.length > 0) { │ │ │ │ - var coords = OpenLayers.String.trim(box[0].firstChild.nodeValue).split(/\s+/); │ │ │ │ - var components = []; │ │ │ │ - var point; │ │ │ │ - if (coords.length > 3) { │ │ │ │ - point = new OpenLayers.Geometry.Point(coords[1], coords[0]); │ │ │ │ - components.push(point); │ │ │ │ - point = new OpenLayers.Geometry.Point(coords[1], coords[2]); │ │ │ │ - components.push(point); │ │ │ │ - point = new OpenLayers.Geometry.Point(coords[3], coords[2]); │ │ │ │ - components.push(point); │ │ │ │ - point = new OpenLayers.Geometry.Point(coords[3], coords[0]); │ │ │ │ - components.push(point); │ │ │ │ - point = new OpenLayers.Geometry.Point(coords[1], coords[0]); │ │ │ │ - components.push(point); │ │ │ │ - } │ │ │ │ - geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: previousStack │ │ │ │ + * {Array} List of items in the history. First item represents the current │ │ │ │ + * state. │ │ │ │ + */ │ │ │ │ + previousStack: null, │ │ │ │ │ │ │ │ - if (geometry && this.internalProjection && this.externalProjection) { │ │ │ │ - geometry.transform(this.externalProjection, │ │ │ │ - this.internalProjection); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: listeners │ │ │ │ + * {Object} An object containing properties corresponding to event types. │ │ │ │ + * This object is used to configure the control and is modified on │ │ │ │ + * construction. │ │ │ │ + */ │ │ │ │ + listeners: null, │ │ │ │ │ │ │ │ - return geometry; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: restoring │ │ │ │ + * {Boolean} Currently restoring a history state. This is set to true │ │ │ │ + * before calling restore and set to false after restore returns. │ │ │ │ + */ │ │ │ │ + restoring: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createFeatureFromItem │ │ │ │ - * Return a feature from a GeoRSS Item. │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Control.NavigationHistory │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * item - {DOMElement} A GeoRSS item node. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A feature representing the item. │ │ │ │ + * options - {Object} An optional object whose properties will be used │ │ │ │ + * to extend the control. │ │ │ │ */ │ │ │ │ - createFeatureFromItem: function(item) { │ │ │ │ - var geometry = this.createGeometryFromItem(item); │ │ │ │ - │ │ │ │ - /* Provide defaults for title and description */ │ │ │ │ - var title = this._getChildValue(item, "*", "title", this.featureTitle); │ │ │ │ - │ │ │ │ - /* First try RSS descriptions, then Atom summaries */ │ │ │ │ - var description = this._getChildValue( │ │ │ │ - item, "*", "description", │ │ │ │ - this._getChildValue(item, "*", "content", │ │ │ │ - this._getChildValue(item, "*", "summary", this.featureDescription))); │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ │ │ │ │ - /* If no link URL is found in the first child node, try the │ │ │ │ - href attribute */ │ │ │ │ - var link = this._getChildValue(item, "*", "link"); │ │ │ │ - if (!link) { │ │ │ │ - try { │ │ │ │ - link = this.getElementsByTagNameNS(item, "*", "link")[0].getAttribute("href"); │ │ │ │ - } catch (e) { │ │ │ │ - link = null; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + this.registry = OpenLayers.Util.extend({ │ │ │ │ + "moveend": this.getState │ │ │ │ + }, this.registry); │ │ │ │ │ │ │ │ - var id = this._getChildValue(item, "*", "id", null); │ │ │ │ + var previousOptions = { │ │ │ │ + trigger: OpenLayers.Function.bind(this.previousTrigger, this), │ │ │ │ + displayClass: this.displayClass + " " + this.displayClass + "Previous" │ │ │ │ + }; │ │ │ │ + OpenLayers.Util.extend(previousOptions, this.previousOptions); │ │ │ │ + this.previous = new OpenLayers.Control.Button(previousOptions); │ │ │ │ │ │ │ │ - var data = { │ │ │ │ - "title": title, │ │ │ │ - "description": description, │ │ │ │ - "link": link │ │ │ │ + var nextOptions = { │ │ │ │ + trigger: OpenLayers.Function.bind(this.nextTrigger, this), │ │ │ │ + displayClass: this.displayClass + " " + this.displayClass + "Next" │ │ │ │ }; │ │ │ │ - var feature = new OpenLayers.Feature.Vector(geometry, data); │ │ │ │ - feature.fid = id; │ │ │ │ - return feature; │ │ │ │ + OpenLayers.Util.extend(nextOptions, this.nextOptions); │ │ │ │ + this.next = new OpenLayers.Control.Button(nextOptions); │ │ │ │ + │ │ │ │ + this.clear(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: _getChildValue │ │ │ │ + * Method: onPreviousChange │ │ │ │ + * Called when the previous history stack changes. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * nsuri - {String} Child node namespace uri ("*" for any). │ │ │ │ - * name - {String} Child node name. │ │ │ │ - * def - {String} Optional string default to return if no child found. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The value of the first child with the given tag name. Returns │ │ │ │ - * default value or empty string if none found. │ │ │ │ + * state - {Object} An object representing the state to be restored │ │ │ │ + * if previous is triggered again or null if no previous states remain. │ │ │ │ + * length - {Integer} The number of remaining previous states that can │ │ │ │ + * be restored. │ │ │ │ */ │ │ │ │ - _getChildValue: function(node, nsuri, name, def) { │ │ │ │ - var value; │ │ │ │ - var eles = this.getElementsByTagNameNS(node, nsuri, name); │ │ │ │ - if (eles && eles[0] && eles[0].firstChild && │ │ │ │ - eles[0].firstChild.nodeValue) { │ │ │ │ - value = this.getChildValue(eles[0]); │ │ │ │ - } else { │ │ │ │ - value = (def == undefined) ? "" : def; │ │ │ │ + onPreviousChange: function(state, length) { │ │ │ │ + if (state && !this.previous.active) { │ │ │ │ + this.previous.activate(); │ │ │ │ + } else if (!state && this.previous.active) { │ │ │ │ + this.previous.deactivate(); │ │ │ │ } │ │ │ │ - return value; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Return a list of features from a GeoRSS doc │ │ │ │ + * Method: onNextChange │ │ │ │ + * Called when the next history stack changes. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * doc - {Element} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + * state - {Object} An object representing the state to be restored │ │ │ │ + * if next is triggered again or null if no next states remain. │ │ │ │ + * length - {Integer} The number of remaining next states that can │ │ │ │ + * be restored. │ │ │ │ */ │ │ │ │ - read: function(doc) { │ │ │ │ - if (typeof doc == "string") { │ │ │ │ - doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); │ │ │ │ + onNextChange: function(state, length) { │ │ │ │ + if (state && !this.next.active) { │ │ │ │ + this.next.activate(); │ │ │ │ + } else if (!state && this.next.active) { │ │ │ │ + this.next.deactivate(); │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /* Try RSS items first, then Atom entries */ │ │ │ │ - var itemlist = null; │ │ │ │ - itemlist = this.getElementsByTagNameNS(doc, '*', 'item'); │ │ │ │ - if (itemlist.length == 0) { │ │ │ │ - itemlist = this.getElementsByTagNameNS(doc, '*', 'entry'); │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Destroy the control. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this); │ │ │ │ + this.previous.destroy(); │ │ │ │ + this.next.destroy(); │ │ │ │ + this.deactivate(); │ │ │ │ + for (var prop in this) { │ │ │ │ + this[prop] = null; │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - var numItems = itemlist.length; │ │ │ │ - var features = new Array(numItems); │ │ │ │ - for (var i = 0; i < numItems; i++) { │ │ │ │ - features[i] = this.createFeatureFromItem(itemlist[i]); │ │ │ │ - } │ │ │ │ - return features; │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the control and <previous> and <next> child │ │ │ │ + * controls. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + this.map = map; │ │ │ │ + this.next.setMap(map); │ │ │ │ + this.previous.setMap(map); │ │ │ │ }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * Called when the control is added to the map. │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ + this.next.draw(); │ │ │ │ + this.previous.draw(); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Accept Feature Collection, and return a string. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string. │ │ │ │ + * Method: previousTrigger │ │ │ │ + * Restore the previous state. If no items are in the previous history │ │ │ │ + * stack, this has no effect. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} Item representing state that was restored. Undefined if no │ │ │ │ + * items are in the previous history stack. │ │ │ │ */ │ │ │ │ - write: function(features) { │ │ │ │ - var georss; │ │ │ │ - if (OpenLayers.Util.isArray(features)) { │ │ │ │ - georss = this.createElementNS(this.rssns, "rss"); │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - georss.appendChild(this.createFeatureXML(features[i])); │ │ │ │ - } │ │ │ │ + previousTrigger: function() { │ │ │ │ + var current = this.previousStack.shift(); │ │ │ │ + var state = this.previousStack.shift(); │ │ │ │ + if (state != undefined) { │ │ │ │ + this.nextStack.unshift(current); │ │ │ │ + this.previousStack.unshift(state); │ │ │ │ + this.restoring = true; │ │ │ │ + this.restore(state); │ │ │ │ + this.restoring = false; │ │ │ │ + this.onNextChange(this.nextStack[0], this.nextStack.length); │ │ │ │ + this.onPreviousChange( │ │ │ │ + this.previousStack[1], this.previousStack.length - 1 │ │ │ │ + ); │ │ │ │ } else { │ │ │ │ - georss = this.createFeatureXML(features); │ │ │ │ + this.previousStack.unshift(current); │ │ │ │ } │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [georss]); │ │ │ │ + return state; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createFeatureXML │ │ │ │ - * Accept an <OpenLayers.Feature.Vector>, and build a geometry for it. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * APIMethod: nextTrigger │ │ │ │ + * Restore the next state. If no items are in the next history │ │ │ │ + * stack, this has no effect. The next history stack is populated │ │ │ │ + * as states are restored from the previous history stack. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * {Object} Item representing state that was restored. Undefined if no │ │ │ │ + * items are in the next history stack. │ │ │ │ */ │ │ │ │ - createFeatureXML: function(feature) { │ │ │ │ - var geometryNode = this.buildGeometryNode(feature.geometry); │ │ │ │ - var featureNode = this.createElementNS(this.rssns, "item"); │ │ │ │ - var titleNode = this.createElementNS(this.rssns, "title"); │ │ │ │ - titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : "")); │ │ │ │ - var descNode = this.createElementNS(this.rssns, "description"); │ │ │ │ - descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : "")); │ │ │ │ - featureNode.appendChild(titleNode); │ │ │ │ - featureNode.appendChild(descNode); │ │ │ │ - if (feature.attributes.link) { │ │ │ │ - var linkNode = this.createElementNS(this.rssns, "link"); │ │ │ │ - linkNode.appendChild(this.createTextNode(feature.attributes.link)); │ │ │ │ - featureNode.appendChild(linkNode); │ │ │ │ - } │ │ │ │ - for (var attr in feature.attributes) { │ │ │ │ - if (attr == "link" || attr == "title" || attr == "description") { │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ - var attrText = this.createTextNode(feature.attributes[attr]); │ │ │ │ - var nodename = attr; │ │ │ │ - if (attr.search(":") != -1) { │ │ │ │ - nodename = attr.split(":")[1]; │ │ │ │ - } │ │ │ │ - var attrContainer = this.createElementNS(this.featureNS, "feature:" + nodename); │ │ │ │ - attrContainer.appendChild(attrText); │ │ │ │ - featureNode.appendChild(attrContainer); │ │ │ │ + nextTrigger: function() { │ │ │ │ + var state = this.nextStack.shift(); │ │ │ │ + if (state != undefined) { │ │ │ │ + this.previousStack.unshift(state); │ │ │ │ + this.restoring = true; │ │ │ │ + this.restore(state); │ │ │ │ + this.restoring = false; │ │ │ │ + this.onNextChange(this.nextStack[0], this.nextStack.length); │ │ │ │ + this.onPreviousChange( │ │ │ │ + this.previousStack[1], this.previousStack.length - 1 │ │ │ │ + ); │ │ │ │ } │ │ │ │ - featureNode.appendChild(geometryNode); │ │ │ │ - return featureNode; │ │ │ │ + return state; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: buildGeometryNode │ │ │ │ - * builds a GeoRSS node with a given geometry │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + /** │ │ │ │ + * APIMethod: clear │ │ │ │ + * Clear history. │ │ │ │ + */ │ │ │ │ + clear: function() { │ │ │ │ + this.previousStack = []; │ │ │ │ + this.previous.deactivate(); │ │ │ │ + this.nextStack = []; │ │ │ │ + this.next.deactivate(); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getState │ │ │ │ + * Get the current state and return it. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} A gml node. │ │ │ │ + * {Object} An object representing the current state. │ │ │ │ */ │ │ │ │ - buildGeometryNode: function(geometry) { │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - geometry = geometry.clone(); │ │ │ │ - geometry.transform(this.internalProjection, │ │ │ │ - this.externalProjection); │ │ │ │ - } │ │ │ │ - var node; │ │ │ │ - // match Polygon │ │ │ │ - if (geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") { │ │ │ │ - node = this.createElementNS(this.georssns, 'georss:polygon'); │ │ │ │ + getState: function() { │ │ │ │ + return { │ │ │ │ + center: this.map.getCenter(), │ │ │ │ + resolution: this.map.getResolution(), │ │ │ │ + projection: this.map.getProjectionObject(), │ │ │ │ + units: this.map.getProjectionObject().getUnits() || │ │ │ │ + this.map.units || this.map.baseLayer.units │ │ │ │ + }; │ │ │ │ + }, │ │ │ │ │ │ │ │ - node.appendChild(this.buildCoordinatesNode(geometry.components[0])); │ │ │ │ + /** │ │ │ │ + * Method: restore │ │ │ │ + * Update the state with the given object. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * state - {Object} An object representing the state to restore. │ │ │ │ + */ │ │ │ │ + restore: function(state) { │ │ │ │ + var center, zoom; │ │ │ │ + if (this.map.getProjectionObject() == state.projection) { │ │ │ │ + zoom = this.map.getZoomForResolution(state.resolution); │ │ │ │ + center = state.center; │ │ │ │ + } else { │ │ │ │ + center = state.center.clone(); │ │ │ │ + center.transform(state.projection, this.map.getProjectionObject()); │ │ │ │ + var sourceUnits = state.units; │ │ │ │ + var targetUnits = this.map.getProjectionObject().getUnits() || │ │ │ │ + this.map.units || this.map.baseLayer.units; │ │ │ │ + var resolutionFactor = sourceUnits && targetUnits ? │ │ │ │ + OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1; │ │ │ │ + zoom = this.map.getZoomForResolution(resolutionFactor * state.resolution); │ │ │ │ } │ │ │ │ - // match LineString │ │ │ │ - else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") { │ │ │ │ - node = this.createElementNS(this.georssns, 'georss:line'); │ │ │ │ + this.map.setCenter(center, zoom); │ │ │ │ + }, │ │ │ │ │ │ │ │ - node.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ - } │ │ │ │ - // match Point │ │ │ │ - else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - node = this.createElementNS(this.georssns, 'georss:point'); │ │ │ │ - node.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ - } else { │ │ │ │ - throw "Couldn't parse " + geometry.CLASS_NAME; │ │ │ │ + /** │ │ │ │ + * Method: setListeners │ │ │ │ + * Sets functions to be registered in the listeners object. │ │ │ │ + */ │ │ │ │ + setListeners: function() { │ │ │ │ + this.listeners = {}; │ │ │ │ + for (var type in this.registry) { │ │ │ │ + this.listeners[type] = OpenLayers.Function.bind(function() { │ │ │ │ + if (!this.restoring) { │ │ │ │ + var state = this.registry[type].apply(this, arguments); │ │ │ │ + this.previousStack.unshift(state); │ │ │ │ + if (this.previousStack.length > 1) { │ │ │ │ + this.onPreviousChange( │ │ │ │ + this.previousStack[1], this.previousStack.length - 1 │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (this.previousStack.length > (this.limit + 1)) { │ │ │ │ + this.previousStack.pop(); │ │ │ │ + } │ │ │ │ + if (this.nextStack.length > 0) { │ │ │ │ + this.nextStack = []; │ │ │ │ + this.onNextChange(null, 0); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return true; │ │ │ │ + }, this); │ │ │ │ } │ │ │ │ - return node; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: buildCoordinatesNode │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + /** │ │ │ │ + * APIMethod: activate │ │ │ │ + * Activate the control. This registers any listeners. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Control successfully activated. │ │ │ │ */ │ │ │ │ - buildCoordinatesNode: function(geometry) { │ │ │ │ - var points = null; │ │ │ │ + activate: function() { │ │ │ │ + var activated = false; │ │ │ │ + if (this.map) { │ │ │ │ + if (OpenLayers.Control.prototype.activate.apply(this)) { │ │ │ │ + if (this.listeners == null) { │ │ │ │ + this.setListeners(); │ │ │ │ + } │ │ │ │ + for (var type in this.listeners) { │ │ │ │ + this.map.events.register(type, this, this.listeners[type]); │ │ │ │ + } │ │ │ │ + activated = true; │ │ │ │ + if (this.previousStack.length == 0) { │ │ │ │ + this.initStack(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return activated; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (geometry.components) { │ │ │ │ - points = geometry.components; │ │ │ │ + /** │ │ │ │ + * Method: initStack │ │ │ │ + * Called after the control is activated if the previous history stack is │ │ │ │ + * empty. │ │ │ │ + */ │ │ │ │ + initStack: function() { │ │ │ │ + if (this.map.getCenter()) { │ │ │ │ + this.listeners.moveend(); │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - var path; │ │ │ │ - if (points) { │ │ │ │ - var numPoints = points.length; │ │ │ │ - var parts = new Array(numPoints); │ │ │ │ - for (var i = 0; i < numPoints; i++) { │ │ │ │ - parts[i] = points[i].y + " " + points[i].x; │ │ │ │ + /** │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivate the control. This unregisters any listeners. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Control successfully deactivated. │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (this.map) { │ │ │ │ + if (OpenLayers.Control.prototype.deactivate.apply(this)) { │ │ │ │ + for (var type in this.listeners) { │ │ │ │ + this.map.events.unregister( │ │ │ │ + type, this, this.listeners[type] │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (this.clearOnDeactivate) { │ │ │ │ + this.clear(); │ │ │ │ + } │ │ │ │ + deactivated = true; │ │ │ │ } │ │ │ │ - path = parts.join(" "); │ │ │ │ - } else { │ │ │ │ - path = geometry.y + " " + geometry.x; │ │ │ │ } │ │ │ │ - return this.createTextNode(path); │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.GeoRSS" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.NavigationHistory" │ │ │ │ }); │ │ │ │ + │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/CSWGetDomain.js │ │ │ │ + OpenLayers/Control/Pan.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format.js │ │ │ │ + * @requires OpenLayers/Control/Button.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.CSWGetDomain │ │ │ │ - * Default version is 2.0.2. │ │ │ │ + * Class: OpenLayers.Control.Pan │ │ │ │ + * The Pan control is a single button to pan the map in one direction. For │ │ │ │ + * a more complete control see <OpenLayers.Control.PanPanel>. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Format>} A CSWGetDomain format of the given version. │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.CSWGetDomain = function(options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults( │ │ │ │ - options, OpenLayers.Format.CSWGetDomain.DEFAULTS │ │ │ │ - ); │ │ │ │ - var cls = OpenLayers.Format.CSWGetDomain["v" + options.version.replace(/\./g, "_")]; │ │ │ │ - if (!cls) { │ │ │ │ - throw "Unsupported CSWGetDomain version: " + options.version; │ │ │ │ - } │ │ │ │ - return new cls(options); │ │ │ │ -}; │ │ │ │ +OpenLayers.Control.Pan = OpenLayers.Class(OpenLayers.Control.Button, { │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Constant: DEFAULTS │ │ │ │ - * {Object} Default properties for the CSWGetDomain format. │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.CSWGetDomain.DEFAULTS = { │ │ │ │ - "version": "2.0.2" │ │ │ │ -}; │ │ │ │ + /** │ │ │ │ + * APIProperty: slideFactor │ │ │ │ + * {Integer} Number of pixels by which we'll pan the map in any direction │ │ │ │ + * on clicking the arrow buttons, defaults to 50. If you want to pan │ │ │ │ + * by some ratio of the map dimensions, use <slideRatio> instead. │ │ │ │ + */ │ │ │ │ + slideFactor: 50, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: slideRatio │ │ │ │ + * {Number} The fraction of map width/height by which we'll pan the map │ │ │ │ + * on clicking the arrow buttons. Default is null. If set, will │ │ │ │ + * override <slideFactor>. E.g. if slideRatio is .5, then Pan Up will │ │ │ │ + * pan up half the map height. │ │ │ │ + */ │ │ │ │ + slideRatio: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: direction │ │ │ │ + * {String} in {'North', 'South', 'East', 'West'} │ │ │ │ + */ │ │ │ │ + direction: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.Pan │ │ │ │ + * Control which handles the panning (in any of the cardinal directions) │ │ │ │ + * of the map by a set px distance. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * direction - {String} The direction this button should pan. │ │ │ │ + * options - {Object} An optional object whose properties will be used │ │ │ │ + * to extend the control. │ │ │ │ + */ │ │ │ │ + initialize: function(direction, options) { │ │ │ │ + │ │ │ │ + this.direction = direction; │ │ │ │ + this.CLASS_NAME += this.direction; │ │ │ │ + │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: trigger │ │ │ │ + */ │ │ │ │ + trigger: function() { │ │ │ │ + if (this.map) { │ │ │ │ + var getSlideFactor = OpenLayers.Function.bind(function(dim) { │ │ │ │ + return this.slideRatio ? │ │ │ │ + this.map.getSize()[dim] * this.slideRatio : │ │ │ │ + this.slideFactor; │ │ │ │ + }, this); │ │ │ │ + │ │ │ │ + switch (this.direction) { │ │ │ │ + case OpenLayers.Control.Pan.NORTH: │ │ │ │ + this.map.pan(0, -getSlideFactor("h")); │ │ │ │ + break; │ │ │ │ + case OpenLayers.Control.Pan.SOUTH: │ │ │ │ + this.map.pan(0, getSlideFactor("h")); │ │ │ │ + break; │ │ │ │ + case OpenLayers.Control.Pan.WEST: │ │ │ │ + this.map.pan(-getSlideFactor("w"), 0); │ │ │ │ + break; │ │ │ │ + case OpenLayers.Control.Pan.EAST: │ │ │ │ + this.map.pan(getSlideFactor("w"), 0); │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Pan" │ │ │ │ +}); │ │ │ │ + │ │ │ │ +OpenLayers.Control.Pan.NORTH = "North"; │ │ │ │ +OpenLayers.Control.Pan.SOUTH = "South"; │ │ │ │ +OpenLayers.Control.Pan.EAST = "East"; │ │ │ │ +OpenLayers.Control.Pan.WEST = "West"; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/OSM.js │ │ │ │ + OpenLayers/Control/Panel.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ - * @requires OpenLayers/Geometry/Point.js │ │ │ │ - * @requires OpenLayers/Geometry/LineString.js │ │ │ │ - * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ - * @requires OpenLayers/Projection.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Events/buttonclick.js │ │ │ │ */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.OSM │ │ │ │ - * OSM parser. Create a new instance with the │ │ │ │ - * <OpenLayers.Format.OSM> constructor. │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.Panel │ │ │ │ + * The Panel control is a container for other controls. With it toolbars │ │ │ │ + * may be composed. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.OSM = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ +OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + /** │ │ │ │ + * Property: controls │ │ │ │ + * {Array(<OpenLayers.Control>)} │ │ │ │ + */ │ │ │ │ + controls: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: checkTags │ │ │ │ - * {Boolean} Should tags be checked to determine whether something │ │ │ │ - * should be treated as a seperate node. Will slow down parsing. │ │ │ │ - * Default is false. │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * true. │ │ │ │ */ │ │ │ │ - checkTags: false, │ │ │ │ + autoActivate: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: defaultControl │ │ │ │ + * {<OpenLayers.Control>} The control which is activated when the control is │ │ │ │ + * activated (turned on), which also happens at instantiation. │ │ │ │ + * If <saveState> is true, <defaultControl> will be nullified after the │ │ │ │ + * first activation of the panel. │ │ │ │ + */ │ │ │ │ + defaultControl: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: interestingTagsExclude │ │ │ │ - * {Array} List of tags to exclude from 'interesting' checks on nodes. │ │ │ │ - * Must be set when creating the format. Will only be used if checkTags │ │ │ │ - * is set. │ │ │ │ + * APIProperty: saveState │ │ │ │ + * {Boolean} If set to true, the active state of this panel's controls will │ │ │ │ + * be stored on panel deactivation, and restored on reactivation. Default │ │ │ │ + * is false. │ │ │ │ */ │ │ │ │ - interestingTagsExclude: null, │ │ │ │ + saveState: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: areaTags │ │ │ │ - * {Array} List of tags indicating that something is an area. │ │ │ │ - * Must be set when creating the format. Will only be used if │ │ │ │ - * checkTags is true. │ │ │ │ + * APIProperty: allowDepress │ │ │ │ + * {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can │ │ │ │ + * be deactivated by clicking the icon that represents them. Default │ │ │ │ + * is false. │ │ │ │ */ │ │ │ │ - areaTags: null, │ │ │ │ + allowDepress: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.OSM │ │ │ │ - * Create a new parser for OSM. │ │ │ │ + * Property: activeState │ │ │ │ + * {Object} stores the active state of this panel's controls. │ │ │ │ + */ │ │ │ │ + activeState: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.Panel │ │ │ │ + * Create a new control panel. │ │ │ │ + * │ │ │ │ + * Each control in the panel is represented by an icon. When clicking │ │ │ │ + * on an icon, the <activateControl> method is called. │ │ │ │ + * │ │ │ │ + * Specific properties for controls on a panel: │ │ │ │ + * type - {Number} One of <OpenLayers.Control.TYPE_TOOL>, │ │ │ │ + * <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>. │ │ │ │ + * If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed. │ │ │ │ + * title - {string} Text displayed when mouse is over the icon that │ │ │ │ + * represents the control. │ │ │ │ + * │ │ │ │ + * The <OpenLayers.Control.type> of a control determines the behavior when │ │ │ │ + * clicking its icon: │ │ │ │ + * <OpenLayers.Control.TYPE_TOOL> - The control is activated and other │ │ │ │ + * controls of this type in the same panel are deactivated. This is │ │ │ │ + * the default type. │ │ │ │ + * <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is │ │ │ │ + * toggled. │ │ │ │ + * <OpenLayers.Control.TYPE_BUTTON> - The │ │ │ │ + * <OpenLayers.Control.Button.trigger> method of the control is called, │ │ │ │ + * but its active state is not changed. │ │ │ │ + * │ │ │ │ + * If a control is <OpenLayers.Control.active>, it will be drawn with the │ │ │ │ + * olControl[Name]ItemActive class, otherwise with the │ │ │ │ + * olControl[Name]ItemInactive class. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * options - {Object} An optional object whose properties will be used │ │ │ │ + * to extend the control. │ │ │ │ */ │ │ │ │ initialize: function(options) { │ │ │ │ - var layer_defaults = { │ │ │ │ - 'interestingTagsExclude': ['source', 'source_ref', │ │ │ │ - 'source:ref', 'history', 'attribution', 'created_by' │ │ │ │ - ], │ │ │ │ - 'areaTags': ['area', 'building', 'leisure', 'tourism', 'ruins', │ │ │ │ - 'historic', 'landuse', 'military', 'natural', 'sport' │ │ │ │ - ] │ │ │ │ - }; │ │ │ │ - │ │ │ │ - layer_defaults = OpenLayers.Util.extend(layer_defaults, options); │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + this.controls = []; │ │ │ │ + this.activeState = {}; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var interesting = {}; │ │ │ │ - for (var i = 0; i < layer_defaults.interestingTagsExclude.length; i++) { │ │ │ │ - interesting[layer_defaults.interestingTagsExclude[i]] = true; │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + if (this.map) { │ │ │ │ + this.map.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ } │ │ │ │ - layer_defaults.interestingTagsExclude = interesting; │ │ │ │ - │ │ │ │ - var area = {}; │ │ │ │ - for (var i = 0; i < layer_defaults.areaTags.length; i++) { │ │ │ │ - area[layer_defaults.areaTags[i]] = true; │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + for (var ctl, i = this.controls.length - 1; i >= 0; i--) { │ │ │ │ + ctl = this.controls[i]; │ │ │ │ + if (ctl.events) { │ │ │ │ + ctl.events.un({ │ │ │ │ + activate: this.iconOn, │ │ │ │ + deactivate: this.iconOff │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + ctl.panel_div = null; │ │ │ │ } │ │ │ │ - layer_defaults.areaTags = area; │ │ │ │ + this.activeState = null; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // OSM coordinates are always in longlat WGS84 │ │ │ │ - this.externalProjection = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + /** │ │ │ │ + * APIMethod: activate │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ + var control; │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ + control = this.controls[i]; │ │ │ │ + if (control === this.defaultControl || │ │ │ │ + (this.saveState && this.activeState[control.id])) { │ │ │ │ + control.activate(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (this.saveState === true) { │ │ │ │ + this.defaultControl = null; │ │ │ │ + } │ │ │ │ + this.redraw(); │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [layer_defaults]); │ │ │ │ + /** │ │ │ │ + * APIMethod: deactivate │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + var control; │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ + control = this.controls[i]; │ │ │ │ + this.activeState[control.id] = control.deactivate(); │ │ │ │ + } │ │ │ │ + this.redraw(); │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Return a list of features from a OSM doc │ │ │ │ - │ │ │ │ - * Parameters: │ │ │ │ - * doc - {Element} │ │ │ │ + * Method: draw │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * Array({<OpenLayers.Feature.Vector>}) │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - read: function(doc) { │ │ │ │ - if (typeof doc == "string") { │ │ │ │ - doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ + if (this.outsideViewport) { │ │ │ │ + this.events.attachToElement(this.div); │ │ │ │ + this.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ + } else { │ │ │ │ + this.map.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ } │ │ │ │ + this.addControlsToMap(this.controls); │ │ │ │ + return this.div; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var nodes = this.getNodes(doc); │ │ │ │ - var ways = this.getWays(doc); │ │ │ │ - │ │ │ │ - // Geoms will contain at least ways.length entries. │ │ │ │ - var feat_list = new Array(ways.length); │ │ │ │ - │ │ │ │ - for (var i = 0; i < ways.length; i++) { │ │ │ │ - // We know the minimal of this one ahead of time. (Could be -1 │ │ │ │ - // due to areas/polygons) │ │ │ │ - var point_list = new Array(ways[i].nodes.length); │ │ │ │ - │ │ │ │ - var poly = this.isWayArea(ways[i]) ? 1 : 0; │ │ │ │ - for (var j = 0; j < ways[i].nodes.length; j++) { │ │ │ │ - var node = nodes[ways[i].nodes[j]]; │ │ │ │ - │ │ │ │ - var point = new OpenLayers.Geometry.Point(node.lon, node.lat); │ │ │ │ - │ │ │ │ - // Since OSM is topological, we stash the node ID internally. │ │ │ │ - point.osm_id = parseInt(ways[i].nodes[j]); │ │ │ │ - point_list[j] = point; │ │ │ │ - │ │ │ │ - // We don't display nodes if they're used inside other │ │ │ │ - // elements. │ │ │ │ - node.used = true; │ │ │ │ - } │ │ │ │ - var geometry = null; │ │ │ │ - if (poly) { │ │ │ │ - geometry = new OpenLayers.Geometry.Polygon( │ │ │ │ - new OpenLayers.Geometry.LinearRing(point_list)); │ │ │ │ - } else { │ │ │ │ - geometry = new OpenLayers.Geometry.LineString(point_list); │ │ │ │ - } │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - geometry.transform(this.externalProjection, │ │ │ │ - this.internalProjection); │ │ │ │ - } │ │ │ │ - var feat = new OpenLayers.Feature.Vector(geometry, │ │ │ │ - ways[i].tags); │ │ │ │ - feat.osm_id = parseInt(ways[i].id); │ │ │ │ - feat.fid = "way." + feat.osm_id; │ │ │ │ - feat_list[i] = feat; │ │ │ │ + /** │ │ │ │ + * Method: redraw │ │ │ │ + */ │ │ │ │ + redraw: function() { │ │ │ │ + for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) { │ │ │ │ + this.div.removeChild(this.div.childNodes[i]); │ │ │ │ } │ │ │ │ - for (var node_id in nodes) { │ │ │ │ - var node = nodes[node_id]; │ │ │ │ - if (!node.used || this.checkTags) { │ │ │ │ - var tags = null; │ │ │ │ - │ │ │ │ - if (this.checkTags) { │ │ │ │ - var result = this.getTags(node.node, true); │ │ │ │ - if (node.used && !result[1]) { │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ - tags = result[0]; │ │ │ │ - } else { │ │ │ │ - tags = this.getTags(node.node); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var feat = new OpenLayers.Feature.Vector( │ │ │ │ - new OpenLayers.Geometry.Point(node['lon'], node['lat']), │ │ │ │ - tags); │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - feat.geometry.transform(this.externalProjection, │ │ │ │ - this.internalProjection); │ │ │ │ - } │ │ │ │ - feat.osm_id = parseInt(node_id); │ │ │ │ - feat.fid = "node." + feat.osm_id; │ │ │ │ - feat_list.push(feat); │ │ │ │ + this.div.innerHTML = ""; │ │ │ │ + if (this.active) { │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ + this.div.appendChild(this.controls[i].panel_div); │ │ │ │ } │ │ │ │ - // Memory cleanup │ │ │ │ - node.node = null; │ │ │ │ } │ │ │ │ - return feat_list; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getNodes │ │ │ │ - * Return the node items from a doc. │ │ │ │ + * APIMethod: activateControl │ │ │ │ + * This method is called when the user click on the icon representing a │ │ │ │ + * control in the panel. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * doc - {DOMElement} node to parse tags from │ │ │ │ + * control - {<OpenLayers.Control>} │ │ │ │ */ │ │ │ │ - getNodes: function(doc) { │ │ │ │ - var node_list = doc.getElementsByTagName("node"); │ │ │ │ - var nodes = {}; │ │ │ │ - for (var i = 0; i < node_list.length; i++) { │ │ │ │ - var node = node_list[i]; │ │ │ │ - var id = node.getAttribute("id"); │ │ │ │ - nodes[id] = { │ │ │ │ - 'lat': node.getAttribute("lat"), │ │ │ │ - 'lon': node.getAttribute("lon"), │ │ │ │ - 'node': node │ │ │ │ - }; │ │ │ │ + activateControl: function(control) { │ │ │ │ + if (!this.active) { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + if (control.type == OpenLayers.Control.TYPE_BUTTON) { │ │ │ │ + control.trigger(); │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (control.type == OpenLayers.Control.TYPE_TOGGLE) { │ │ │ │ + if (control.active) { │ │ │ │ + control.deactivate(); │ │ │ │ + } else { │ │ │ │ + control.activate(); │ │ │ │ + } │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (this.allowDepress && control.active) { │ │ │ │ + control.deactivate(); │ │ │ │ + } else { │ │ │ │ + var c; │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ + c = this.controls[i]; │ │ │ │ + if (c != control && │ │ │ │ + (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) { │ │ │ │ + c.deactivate(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + control.activate(); │ │ │ │ } │ │ │ │ - return nodes; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getWays │ │ │ │ - * Return the way items from a doc. │ │ │ │ + * APIMethod: addControls │ │ │ │ + * To build a toolbar, you add a set of controls to it. addControls │ │ │ │ + * lets you add a single control or a list of controls to the │ │ │ │ + * Control Panel. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * doc - {DOMElement} node to parse tags from │ │ │ │ + * controls - {<OpenLayers.Control>} Controls to add in the panel. │ │ │ │ */ │ │ │ │ - getWays: function(doc) { │ │ │ │ - var way_list = doc.getElementsByTagName("way"); │ │ │ │ - var return_ways = []; │ │ │ │ - for (var i = 0; i < way_list.length; i++) { │ │ │ │ - var way = way_list[i]; │ │ │ │ - var way_object = { │ │ │ │ - id: way.getAttribute("id") │ │ │ │ - }; │ │ │ │ - │ │ │ │ - way_object.tags = this.getTags(way); │ │ │ │ - │ │ │ │ - var node_list = way.getElementsByTagName("nd"); │ │ │ │ - │ │ │ │ - way_object.nodes = new Array(node_list.length); │ │ │ │ + addControls: function(controls) { │ │ │ │ + if (!(OpenLayers.Util.isArray(controls))) { │ │ │ │ + controls = [controls]; │ │ │ │ + } │ │ │ │ + this.controls = this.controls.concat(controls); │ │ │ │ │ │ │ │ - for (var j = 0; j < node_list.length; j++) { │ │ │ │ - way_object.nodes[j] = node_list[j].getAttribute("ref"); │ │ │ │ + for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ + var control = controls[i], │ │ │ │ + element = this.createControlMarkup(control); │ │ │ │ + OpenLayers.Element.addClass(element, │ │ │ │ + control.displayClass + "ItemInactive"); │ │ │ │ + OpenLayers.Element.addClass(element, "olButton"); │ │ │ │ + if (control.title != "" && !element.title) { │ │ │ │ + element.title = control.title; │ │ │ │ } │ │ │ │ - return_ways.push(way_object); │ │ │ │ + control.panel_div = element; │ │ │ │ } │ │ │ │ - return return_ways; │ │ │ │ │ │ │ │ + if (this.map) { // map.addControl() has already been called on the panel │ │ │ │ + this.addControlsToMap(controls); │ │ │ │ + this.redraw(); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getTags │ │ │ │ - * Return the tags list attached to a specific DOM element. │ │ │ │ + * APIMethod: createControlMarkup │ │ │ │ + * This function just creates a div for the control. If specific HTML │ │ │ │ + * markup is needed this function can be overridden in specific classes, │ │ │ │ + * or at panel instantiation time: │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * (code) │ │ │ │ + * var panel = new OpenLayers.Control.Panel({ │ │ │ │ + * defaultControl: control, │ │ │ │ + * // ovverride createControlMarkup to create actual buttons │ │ │ │ + * // including texts wrapped into span elements. │ │ │ │ + * createControlMarkup: function(control) { │ │ │ │ + * var button = document.createElement('button'), │ │ │ │ + * span = document.createElement('span'); │ │ │ │ + * if (control.text) { │ │ │ │ + * span.innerHTML = control.text; │ │ │ │ + * } │ │ │ │ + * return button; │ │ │ │ + * } │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * dom_node - {DOMElement} node to parse tags from │ │ │ │ - * interesting_tags - {Boolean} whether the return from this function should │ │ │ │ - * return a boolean indicating that it has 'interesting tags' -- │ │ │ │ - * tags like attribution and source are ignored. (To change the list │ │ │ │ - * of tags, see interestingTagsExclude) │ │ │ │ - * │ │ │ │ + * control - {<OpenLayers.Control>} The control to create the HTML │ │ │ │ + * markup for. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * tags - {Object} hash of tags │ │ │ │ - * interesting - {Boolean} if interesting_tags is passed, returns │ │ │ │ - * whether there are any interesting tags on this element. │ │ │ │ + * {DOMElement} The markup. │ │ │ │ */ │ │ │ │ - getTags: function(dom_node, interesting_tags) { │ │ │ │ - var tag_list = dom_node.getElementsByTagName("tag"); │ │ │ │ - var tags = {}; │ │ │ │ - var interesting = false; │ │ │ │ - for (var j = 0; j < tag_list.length; j++) { │ │ │ │ - var key = tag_list[j].getAttribute("k"); │ │ │ │ - tags[key] = tag_list[j].getAttribute("v"); │ │ │ │ - if (interesting_tags) { │ │ │ │ - if (!this.interestingTagsExclude[key]) { │ │ │ │ - interesting = true; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return interesting_tags ? [tags, interesting] : tags; │ │ │ │ + createControlMarkup: function(control) { │ │ │ │ + return document.createElement("div"); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: isWayArea │ │ │ │ - * Given a way object from getWays, check whether the tags and geometry │ │ │ │ - * indicate something is an area. │ │ │ │ + /** │ │ │ │ + * Method: addControlsToMap │ │ │ │ + * Only for internal use in draw() and addControls() methods. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} │ │ │ │ + * Parameters: │ │ │ │ + * controls - {Array(<OpenLayers.Control>)} Controls to add into map. │ │ │ │ */ │ │ │ │ - isWayArea: function(way) { │ │ │ │ - var poly_shaped = false; │ │ │ │ - var poly_tags = false; │ │ │ │ - │ │ │ │ - if (way.nodes[0] == way.nodes[way.nodes.length - 1]) { │ │ │ │ - poly_shaped = true; │ │ │ │ - } │ │ │ │ - if (this.checkTags) { │ │ │ │ - for (var key in way.tags) { │ │ │ │ - if (this.areaTags[key]) { │ │ │ │ - poly_tags = true; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ + addControlsToMap: function(controls) { │ │ │ │ + var control; │ │ │ │ + for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ + control = controls[i]; │ │ │ │ + if (control.autoActivate === true) { │ │ │ │ + control.autoActivate = false; │ │ │ │ + this.map.addControl(control); │ │ │ │ + control.autoActivate = true; │ │ │ │ + } else { │ │ │ │ + this.map.addControl(control); │ │ │ │ + control.deactivate(); │ │ │ │ } │ │ │ │ + control.events.on({ │ │ │ │ + activate: this.iconOn, │ │ │ │ + deactivate: this.iconOff │ │ │ │ + }); │ │ │ │ } │ │ │ │ - return poly_shaped && (this.checkTags ? poly_tags : true); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Takes a list of features, returns a serialized OSM format file for use │ │ │ │ - * in tools like JOSM. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + * Method: iconOn │ │ │ │ + * Internal use, for use only with "controls[i].events.on/un". │ │ │ │ */ │ │ │ │ - write: function(features) { │ │ │ │ - if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ - features = [features]; │ │ │ │ - } │ │ │ │ + iconOn: function() { │ │ │ │ + var d = this.panel_div; // "this" refers to a control on panel! │ │ │ │ + var re = new RegExp("\\b(" + this.displayClass + "Item)Inactive\\b"); │ │ │ │ + d.className = d.className.replace(re, "$1Active"); │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.osm_id = 1; │ │ │ │ - this.created_nodes = {}; │ │ │ │ - var root_node = this.createElementNS(null, "osm"); │ │ │ │ - root_node.setAttribute("version", "0.5"); │ │ │ │ - root_node.setAttribute("generator", "OpenLayers " + OpenLayers.VERSION_NUMBER); │ │ │ │ + /** │ │ │ │ + * Method: iconOff │ │ │ │ + * Internal use, for use only with "controls[i].events.on/un". │ │ │ │ + */ │ │ │ │ + iconOff: function() { │ │ │ │ + var d = this.panel_div; // "this" refers to a control on panel! │ │ │ │ + var re = new RegExp("\\b(" + this.displayClass + "Item)Active\\b"); │ │ │ │ + d.className = d.className.replace(re, "$1Inactive"); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Loop backwards, because the deserializer puts nodes last, and │ │ │ │ - // we want them first if possible │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - var nodes = this.createFeatureNodes(features[i]); │ │ │ │ - for (var j = 0; j < nodes.length; j++) { │ │ │ │ - root_node.appendChild(nodes[j]); │ │ │ │ + /** │ │ │ │ + * Method: onButtonClick │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + */ │ │ │ │ + onButtonClick: function(evt) { │ │ │ │ + var controls = this.controls, │ │ │ │ + button = evt.buttonElement; │ │ │ │ + for (var i = controls.length - 1; i >= 0; --i) { │ │ │ │ + if (controls[i].panel_div === button) { │ │ │ │ + this.activateControl(controls[i]); │ │ │ │ + break; │ │ │ │ } │ │ │ │ } │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [root_node]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createFeatureNodes │ │ │ │ - * Takes a feature, returns a list of nodes from size 0->n. │ │ │ │ - * Will include all pieces of the serialization that are required which │ │ │ │ - * have not already been created. Calls out to createXML based on geometry │ │ │ │ - * type. │ │ │ │ + * APIMethod: getControlsBy │ │ │ │ + * Get a list of controls with properties matching the given criteria. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * property - {String} A control property to be matched. │ │ │ │ + * match - {String | Object} A string to match. Can also be a regular │ │ │ │ + * expression literal or object. In addition, it can be any object │ │ │ │ + * with a method named test. For reqular expressions or other, if │ │ │ │ + * match.test(control[property]) evaluates to true, the control will be │ │ │ │ + * included in the array returned. If no controls are found, an empty │ │ │ │ + * array is returned. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Control>)} A list of controls matching the given criteria. │ │ │ │ + * An empty array is returned if no matches are found. │ │ │ │ */ │ │ │ │ - createFeatureNodes: function(feature) { │ │ │ │ - var nodes = []; │ │ │ │ - var className = feature.geometry.CLASS_NAME; │ │ │ │ - var type = className.substring(className.lastIndexOf(".") + 1); │ │ │ │ - type = type.toLowerCase(); │ │ │ │ - var builder = this.createXML[type]; │ │ │ │ - if (builder) { │ │ │ │ - nodes = builder.apply(this, [feature]); │ │ │ │ - } │ │ │ │ - return nodes; │ │ │ │ + getControlsBy: function(property, match) { │ │ │ │ + var test = (typeof match.test == "function"); │ │ │ │ + var found = OpenLayers.Array.filter(this.controls, function(item) { │ │ │ │ + return item[property] == match || (test && match.test(item[property])); │ │ │ │ + }); │ │ │ │ + return found; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createXML │ │ │ │ - * Takes a feature, returns a list of nodes from size 0->n. │ │ │ │ - * Will include all pieces of the serialization that are required which │ │ │ │ - * have not already been created. │ │ │ │ + * APIMethod: getControlsByName │ │ │ │ + * Get a list of contorls with names matching the given name. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * match - {String | Object} A control name. The name can also be a regular │ │ │ │ + * expression literal or object. In addition, it can be any object │ │ │ │ + * with a method named test. For reqular expressions or other, if │ │ │ │ + * name.test(control.name) evaluates to true, the control will be included │ │ │ │ + * in the list of controls returned. If no controls are found, an empty │ │ │ │ + * array is returned. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Control>)} A list of controls matching the given name. │ │ │ │ + * An empty array is returned if no matches are found. │ │ │ │ */ │ │ │ │ - createXML: { │ │ │ │ - 'point': function(point) { │ │ │ │ - var id = null; │ │ │ │ - var geometry = point.geometry ? point.geometry : point; │ │ │ │ + getControlsByName: function(match) { │ │ │ │ + return this.getControlsBy("name", match); │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - geometry = geometry.clone(); │ │ │ │ - geometry.transform(this.internalProjection, │ │ │ │ - this.externalProjection); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIMethod: getControlsByClass │ │ │ │ + * Get a list of controls of a given type (CLASS_NAME). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * match - {String | Object} A control class name. The type can also be a │ │ │ │ + * regular expression literal or object. In addition, it can be any │ │ │ │ + * object with a method named test. For reqular expressions or other, │ │ │ │ + * if type.test(control.CLASS_NAME) evaluates to true, the control will │ │ │ │ + * be included in the list of controls returned. If no controls are │ │ │ │ + * found, an empty array is returned. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Control>)} A list of controls matching the given type. │ │ │ │ + * An empty array is returned if no matches are found. │ │ │ │ + */ │ │ │ │ + getControlsByClass: function(match) { │ │ │ │ + return this.getControlsBy("CLASS_NAME", match); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var already_exists = false; // We don't return anything if the node │ │ │ │ - // has already been created │ │ │ │ - if (point.osm_id) { │ │ │ │ - id = point.osm_id; │ │ │ │ - if (this.created_nodes[id]) { │ │ │ │ - already_exists = true; │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - id = -this.osm_id; │ │ │ │ - this.osm_id++; │ │ │ │ - } │ │ │ │ - if (already_exists) { │ │ │ │ - node = this.created_nodes[id]; │ │ │ │ - } else { │ │ │ │ - var node = this.createElementNS(null, "node"); │ │ │ │ - } │ │ │ │ - this.created_nodes[id] = node; │ │ │ │ - node.setAttribute("id", id); │ │ │ │ - node.setAttribute("lon", geometry.x); │ │ │ │ - node.setAttribute("lat", geometry.y); │ │ │ │ - if (point.attributes) { │ │ │ │ - this.serializeTags(point, node); │ │ │ │ - } │ │ │ │ - this.setState(point, node); │ │ │ │ - return already_exists ? [] : [node]; │ │ │ │ - }, │ │ │ │ - linestring: function(feature) { │ │ │ │ - var id; │ │ │ │ - var nodes = []; │ │ │ │ - var geometry = feature.geometry; │ │ │ │ - if (feature.osm_id) { │ │ │ │ - id = feature.osm_id; │ │ │ │ - } else { │ │ │ │ - id = -this.osm_id; │ │ │ │ - this.osm_id++; │ │ │ │ - } │ │ │ │ - var way = this.createElementNS(null, "way"); │ │ │ │ - way.setAttribute("id", id); │ │ │ │ - for (var i = 0; i < geometry.components.length; i++) { │ │ │ │ - var node = this.createXML['point'].apply(this, [geometry.components[i]]); │ │ │ │ - if (node.length) { │ │ │ │ - node = node[0]; │ │ │ │ - var node_ref = node.getAttribute("id"); │ │ │ │ - nodes.push(node); │ │ │ │ - } else { │ │ │ │ - node_ref = geometry.components[i].osm_id; │ │ │ │ - node = this.created_nodes[node_ref]; │ │ │ │ - } │ │ │ │ - this.setState(feature, node); │ │ │ │ - var nd_dom = this.createElementNS(null, "nd"); │ │ │ │ - nd_dom.setAttribute("ref", node_ref); │ │ │ │ - way.appendChild(nd_dom); │ │ │ │ - } │ │ │ │ - this.serializeTags(feature, way); │ │ │ │ - nodes.push(way); │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Panel" │ │ │ │ +}); │ │ │ │ │ │ │ │ - return nodes; │ │ │ │ - }, │ │ │ │ - polygon: function(feature) { │ │ │ │ - var attrs = OpenLayers.Util.extend({ │ │ │ │ - 'area': 'yes' │ │ │ │ - }, feature.attributes); │ │ │ │ - var feat = new OpenLayers.Feature.Vector(feature.geometry.components[0], attrs); │ │ │ │ - feat.osm_id = feature.osm_id; │ │ │ │ - return this.createXML['linestring'].apply(this, [feat]); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/ZoomIn.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control/Button.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.ZoomIn │ │ │ │ + * The ZoomIn control is a button to increase the zoom level of a map. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.ZoomIn = OpenLayers.Class(OpenLayers.Control.Button, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: trigger │ │ │ │ + */ │ │ │ │ + trigger: function() { │ │ │ │ + if (this.map) { │ │ │ │ + this.map.zoomIn(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Control.ZoomIn" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/ZoomToMaxExtent.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control/Button.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.ZoomToMaxExtent │ │ │ │ + * The ZoomToMaxExtent control is a button that zooms out to the maximum │ │ │ │ + * extent of the map. It is designed to be used with a │ │ │ │ + * <OpenLayers.Control.Panel>. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control.Button, { │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: serializeTags │ │ │ │ - * Given a feature, serialize the attributes onto the given node. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * node - {DOMNode} │ │ │ │ + * Method: trigger │ │ │ │ + * │ │ │ │ + * Called whenever this control is being rendered inside of a panel and a │ │ │ │ + * click occurs on this controls element. Actually zooms to the maximum │ │ │ │ + * extent of this controls map. │ │ │ │ */ │ │ │ │ - serializeTags: function(feature, node) { │ │ │ │ - for (var key in feature.attributes) { │ │ │ │ - var tag = this.createElementNS(null, "tag"); │ │ │ │ - tag.setAttribute("k", key); │ │ │ │ - tag.setAttribute("v", feature.attributes[key]); │ │ │ │ - node.appendChild(tag); │ │ │ │ + trigger: function() { │ │ │ │ + if (this.map) { │ │ │ │ + this.map.zoomToMaxExtent(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Control.ZoomToMaxExtent" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/ZoomPanel.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control/Panel.js │ │ │ │ + * @requires OpenLayers/Control/ZoomIn.js │ │ │ │ + * @requires OpenLayers/Control/ZoomOut.js │ │ │ │ + * @requires OpenLayers/Control/ZoomToMaxExtent.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.ZoomPanel │ │ │ │ + * The ZoomPanel control is a compact collecton of 3 zoom controls: a │ │ │ │ + * <OpenLayers.Control.ZoomIn>, a <OpenLayers.Control.ZoomToMaxExtent>, and a │ │ │ │ + * <OpenLayers.Control.ZoomOut>. By default it is drawn in the upper left │ │ │ │ + * corner of the map. │ │ │ │ + * │ │ │ │ + * Note: │ │ │ │ + * If you wish to use this class with the default images and you want │ │ │ │ + * it to look nice in ie6, you should add the following, conditionally │ │ │ │ + * added css stylesheet to your HTML file: │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * <!--[if lte IE 6]> │ │ │ │ + * <link rel="stylesheet" href="../theme/default/ie6-style.css" type="text/css" /> │ │ │ │ + * <![endif]--> │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control.Panel> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.ZoomPanel = OpenLayers.Class(OpenLayers.Control.Panel, { │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: setState │ │ │ │ - * OpenStreetMap has a convention that 'state' is stored for modification or deletion. │ │ │ │ - * This allows the file to be uploaded via JOSM or the bulk uploader tool. │ │ │ │ + * Constructor: OpenLayers.Control.ZoomPanel │ │ │ │ + * Add the three zooming controls. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * node - {DOMNode} │ │ │ │ + * options - {Object} An optional object whose properties will be used │ │ │ │ + * to extend the control. │ │ │ │ */ │ │ │ │ - setState: function(feature, node) { │ │ │ │ - if (feature.state) { │ │ │ │ - var state = null; │ │ │ │ - switch (feature.state) { │ │ │ │ - case OpenLayers.State.UPDATE: │ │ │ │ - state = "modify"; │ │ │ │ - case OpenLayers.State.DELETE: │ │ │ │ - state = "delete"; │ │ │ │ - } │ │ │ │ - if (state) { │ │ │ │ - node.setAttribute("action", state); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); │ │ │ │ + this.addControls([ │ │ │ │ + new OpenLayers.Control.ZoomIn(), │ │ │ │ + new OpenLayers.Control.ZoomToMaxExtent(), │ │ │ │ + new OpenLayers.Control.ZoomOut() │ │ │ │ + ]); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.OSM" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.ZoomPanel" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/GPX.js │ │ │ │ + OpenLayers/Control/SelectFeature.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ * @requires OpenLayers/Feature/Vector.js │ │ │ │ - * @requires OpenLayers/Geometry/Point.js │ │ │ │ - * @requires OpenLayers/Geometry/LineString.js │ │ │ │ - * @requires OpenLayers/Projection.js │ │ │ │ + * @requires OpenLayers/Handler/Feature.js │ │ │ │ + * @requires OpenLayers/Layer/Vector/RootContainer.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.GPX │ │ │ │ - * Read/write GPX parser. Create a new instance with the │ │ │ │ - * <OpenLayers.Format.GPX> constructor. │ │ │ │ + * Class: OpenLayers.Control.SelectFeature │ │ │ │ + * The SelectFeature control selects vector features from a given layer on │ │ │ │ + * click or hover. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ - │ │ │ │ +OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: defaultDesc │ │ │ │ - * {String} Default description for the waypoints/tracks in the case │ │ │ │ - * where the feature has no "description" attribute. │ │ │ │ - * Default is "No description available". │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * beforefeaturehighlighted - Triggered before a feature is highlighted │ │ │ │ + * featurehighlighted - Triggered when a feature is highlighted │ │ │ │ + * featureunhighlighted - Triggered when a feature is unhighlighted │ │ │ │ + * boxselectionstart - Triggered before box selection starts │ │ │ │ + * boxselectionend - Triggered after box selection ends │ │ │ │ */ │ │ │ │ - defaultDesc: "No description available", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: extractWaypoints │ │ │ │ - * {Boolean} Extract waypoints from GPX. (default: true) │ │ │ │ + * Property: multipleKey │ │ │ │ + * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets │ │ │ │ + * the <multiple> property to true. Default is null. │ │ │ │ */ │ │ │ │ - extractWaypoints: true, │ │ │ │ + multipleKey: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: extractTracks │ │ │ │ - * {Boolean} Extract tracks from GPX. (default: true) │ │ │ │ + * Property: toggleKey │ │ │ │ + * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets │ │ │ │ + * the <toggle> property to true. Default is null. │ │ │ │ */ │ │ │ │ - extractTracks: true, │ │ │ │ + toggleKey: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: extractRoutes │ │ │ │ - * {Boolean} Extract routes from GPX. (default: true) │ │ │ │ + * APIProperty: multiple │ │ │ │ + * {Boolean} Allow selection of multiple geometries. Default is false. │ │ │ │ */ │ │ │ │ - extractRoutes: true, │ │ │ │ + multiple: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: extractAttributes │ │ │ │ - * {Boolean} Extract feature attributes from GPX. (default: true) │ │ │ │ - * NOTE: Attributes as part of extensions to the GPX standard may not │ │ │ │ - * be extracted. │ │ │ │ + * APIProperty: clickout │ │ │ │ + * {Boolean} Unselect features when clicking outside any feature. │ │ │ │ + * Default is true. │ │ │ │ */ │ │ │ │ - extractAttributes: true, │ │ │ │ + clickout: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + * APIProperty: toggle │ │ │ │ + * {Boolean} Unselect a selected feature on click. Default is false. Only │ │ │ │ + * has meaning if hover is false. │ │ │ │ */ │ │ │ │ - namespaces: { │ │ │ │ - gpx: "http://www.topografix.com/GPX/1/1", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ - }, │ │ │ │ + toggle: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} Schema location. Defaults to │ │ │ │ - * "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd" │ │ │ │ + * APIProperty: hover │ │ │ │ + * {Boolean} Select on mouse over and deselect on mouse out. If true, this │ │ │ │ + * ignores clicks and only listens to mouse moves. │ │ │ │ */ │ │ │ │ - schemaLocation: "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd", │ │ │ │ + hover: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: creator │ │ │ │ - * {String} The creator attribute to be added to the written GPX files. │ │ │ │ - * Defaults to "OpenLayers" │ │ │ │ + * APIProperty: highlightOnly │ │ │ │ + * {Boolean} If true do not actually select features (that is place them in │ │ │ │ + * the layer's selected features array), just highlight them. This property │ │ │ │ + * has no effect if hover is false. Defaults to false. │ │ │ │ */ │ │ │ │ - creator: "OpenLayers", │ │ │ │ + highlightOnly: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.GPX │ │ │ │ - * Create a new parser for GPX. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * APIProperty: box │ │ │ │ + * {Boolean} Allow feature selection by drawing a box. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - // GPX coordinates are always in longlat WGS84 │ │ │ │ - this.externalProjection = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + box: false, │ │ │ │ │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: onBeforeSelect │ │ │ │ + * {Function} Optional function to be called before a feature is selected. │ │ │ │ + * The function should expect to be called with a feature. │ │ │ │ + */ │ │ │ │ + onBeforeSelect: function() {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Return a list of features from a GPX doc │ │ │ │ + * APIProperty: onSelect │ │ │ │ + * {Function} Optional function to be called when a feature is selected. │ │ │ │ + * The function should expect to be called with a feature. │ │ │ │ + */ │ │ │ │ + onSelect: function() {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: onUnselect │ │ │ │ + * {Function} Optional function to be called when a feature is unselected. │ │ │ │ + * The function should expect to be called with a feature. │ │ │ │ + */ │ │ │ │ + onUnselect: function() {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: scope │ │ │ │ + * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect │ │ │ │ + * callbacks. If null the scope will be this control. │ │ │ │ + */ │ │ │ │ + scope: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: geometryTypes │ │ │ │ + * {Array(String)} To restrict selecting to a limited set of geometry types, │ │ │ │ + * send a list of strings corresponding to the geometry class names. │ │ │ │ + */ │ │ │ │ + geometryTypes: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: layer │ │ │ │ + * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer │ │ │ │ + * root for all layers this control is configured with (if an array of │ │ │ │ + * layers was passed to the constructor), or the vector layer the control │ │ │ │ + * was configured with (if a single layer was passed to the constructor). │ │ │ │ + */ │ │ │ │ + layer: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: layers │ │ │ │ + * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on, │ │ │ │ + * or null if the control was configured with a single layer │ │ │ │ + */ │ │ │ │ + layers: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: callbacks │ │ │ │ + * {Object} The functions that are sent to the handlers.feature for callback │ │ │ │ + */ │ │ │ │ + callbacks: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: selectStyle │ │ │ │ + * {Object} Hash of styles │ │ │ │ + */ │ │ │ │ + selectStyle: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: renderIntent │ │ │ │ + * {String} key used to retrieve the select style from the layer's │ │ │ │ + * style map. │ │ │ │ + */ │ │ │ │ + renderIntent: "select", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: handlers │ │ │ │ + * {Object} Object with references to multiple <OpenLayers.Handler> │ │ │ │ + * instances. │ │ │ │ + */ │ │ │ │ + handlers: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.SelectFeature │ │ │ │ + * Create a new control for selecting features. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * doc - {Element} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * Array({<OpenLayers.Feature.Vector>}) │ │ │ │ + * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The │ │ │ │ + * layer(s) this control will select features from. │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - read: function(doc) { │ │ │ │ - if (typeof doc == "string") { │ │ │ │ - doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]); │ │ │ │ + initialize: function(layers, options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + │ │ │ │ + if (this.scope === null) { │ │ │ │ + this.scope = this; │ │ │ │ + } │ │ │ │ + this.initLayer(layers); │ │ │ │ + var callbacks = { │ │ │ │ + click: this.clickFeature, │ │ │ │ + clickout: this.clickoutFeature │ │ │ │ + }; │ │ │ │ + if (this.hover) { │ │ │ │ + callbacks.over = this.overFeature; │ │ │ │ + callbacks.out = this.outFeature; │ │ │ │ } │ │ │ │ - var features = []; │ │ │ │ │ │ │ │ - if (this.extractTracks) { │ │ │ │ - var tracks = doc.getElementsByTagName("trk"); │ │ │ │ - for (var i = 0, len = tracks.length; i < len; i++) { │ │ │ │ - // Attributes are only in trk nodes, not trkseg nodes │ │ │ │ - var attrs = {}; │ │ │ │ - if (this.extractAttributes) { │ │ │ │ - attrs = this.parseAttributes(tracks[i]); │ │ │ │ + this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); │ │ │ │ + this.handlers = { │ │ │ │ + feature: new OpenLayers.Handler.Feature( │ │ │ │ + this, this.layer, this.callbacks, { │ │ │ │ + geometryTypes: this.geometryTypes │ │ │ │ } │ │ │ │ + ) │ │ │ │ + }; │ │ │ │ │ │ │ │ - var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, "trkseg"); │ │ │ │ - for (var j = 0, seglen = segs.length; j < seglen; j++) { │ │ │ │ - // We don't yet support extraction of trkpt attributes │ │ │ │ - // All trksegs of a trk get that trk's attributes │ │ │ │ - var track = this.extractSegment(segs[j], "trkpt"); │ │ │ │ - features.push(new OpenLayers.Feature.Vector(track, attrs)); │ │ │ │ + if (this.box) { │ │ │ │ + this.handlers.box = new OpenLayers.Handler.Box( │ │ │ │ + this, { │ │ │ │ + done: this.selectBox │ │ │ │ + }, { │ │ │ │ + boxDivClassName: "olHandlerBoxSelectFeature" │ │ │ │ } │ │ │ │ - } │ │ │ │ + ); │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.extractRoutes) { │ │ │ │ - var routes = doc.getElementsByTagName("rte"); │ │ │ │ - for (var k = 0, klen = routes.length; k < klen; k++) { │ │ │ │ - var attrs = {}; │ │ │ │ - if (this.extractAttributes) { │ │ │ │ - attrs = this.parseAttributes(routes[k]); │ │ │ │ + /** │ │ │ │ + * Method: initLayer │ │ │ │ + * Assign the layer property. If layers is an array, we need to use │ │ │ │ + * a RootContainer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. │ │ │ │ + */ │ │ │ │ + initLayer: function(layers) { │ │ │ │ + if (OpenLayers.Util.isArray(layers)) { │ │ │ │ + this.layers = layers; │ │ │ │ + this.layer = new OpenLayers.Layer.Vector.RootContainer( │ │ │ │ + this.id + "_container", { │ │ │ │ + layers: layers │ │ │ │ } │ │ │ │ - var route = this.extractSegment(routes[k], "rtept"); │ │ │ │ - features.push(new OpenLayers.Feature.Vector(route, attrs)); │ │ │ │ - } │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + this.layer = layers; │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.extractWaypoints) { │ │ │ │ - var waypoints = doc.getElementsByTagName("wpt"); │ │ │ │ - for (var l = 0, len = waypoints.length; l < len; l++) { │ │ │ │ - var attrs = {}; │ │ │ │ - if (this.extractAttributes) { │ │ │ │ - attrs = this.parseAttributes(waypoints[l]); │ │ │ │ - } │ │ │ │ - var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute("lon"), waypoints[l].getAttribute("lat")); │ │ │ │ - features.push(new OpenLayers.Feature.Vector(wpt, attrs)); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + if (this.active && this.layers) { │ │ │ │ + this.map.removeLayer(this.layer); │ │ │ │ + } │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + if (this.layers) { │ │ │ │ + this.layer.destroy(); │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - for (var g = 0, featLength = features.length; g < featLength; g++) { │ │ │ │ - features[g].geometry.transform(this.externalProjection, │ │ │ │ - this.internalProjection); │ │ │ │ + /** │ │ │ │ + * Method: activate │ │ │ │ + * Activates the control. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The control was effectively activated. │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + if (!this.active) { │ │ │ │ + if (this.layers) { │ │ │ │ + this.map.addLayer(this.layer); │ │ │ │ + } │ │ │ │ + this.handlers.feature.activate(); │ │ │ │ + if (this.box && this.handlers.box) { │ │ │ │ + this.handlers.box.activate(); │ │ │ │ } │ │ │ │ } │ │ │ │ - │ │ │ │ - return features; │ │ │ │ + return OpenLayers.Control.prototype.activate.apply( │ │ │ │ + this, arguments │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: extractSegment │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * segment - {DOMElement} a trkseg or rte node to parse │ │ │ │ - * segmentType - {String} nodeName of waypoints that form the line │ │ │ │ - * │ │ │ │ + * Method: deactivate │ │ │ │ + * Deactivates the control. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Geometry.LineString>} A linestring geometry │ │ │ │ + * {Boolean} The control was effectively deactivated. │ │ │ │ */ │ │ │ │ - extractSegment: function(segment, segmentType) { │ │ │ │ - var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType); │ │ │ │ - var point_features = []; │ │ │ │ - for (var i = 0, len = points.length; i < len; i++) { │ │ │ │ - point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute("lon"), points[i].getAttribute("lat"))); │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + this.handlers.feature.deactivate(); │ │ │ │ + if (this.handlers.box) { │ │ │ │ + this.handlers.box.deactivate(); │ │ │ │ + } │ │ │ │ + if (this.layers) { │ │ │ │ + this.map.removeLayer(this.layer); │ │ │ │ + } │ │ │ │ } │ │ │ │ - return new OpenLayers.Geometry.LineString(point_features); │ │ │ │ + return OpenLayers.Control.prototype.deactivate.apply( │ │ │ │ + this, arguments │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseAttributes │ │ │ │ + * Method: unselectAll │ │ │ │ + * Unselect all selected features. To unselect all except for a single │ │ │ │ + * feature, set the options.except property to the feature. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {<DOMElement>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An attributes object. │ │ │ │ + * options - {Object} Optional configuration object. │ │ │ │ */ │ │ │ │ - parseAttributes: function(node) { │ │ │ │ - // node is either a wpt, trk or rte │ │ │ │ - // attributes are children of the form <attr>value</attr> │ │ │ │ - var attributes = {}; │ │ │ │ - var attrNode = node.firstChild, │ │ │ │ - value, name; │ │ │ │ - while (attrNode) { │ │ │ │ - if (attrNode.nodeType == 1 && attrNode.firstChild) { │ │ │ │ - value = attrNode.firstChild; │ │ │ │ - if (value.nodeType == 3 || value.nodeType == 4) { │ │ │ │ - name = (attrNode.prefix) ? │ │ │ │ - attrNode.nodeName.split(":")[1] : │ │ │ │ - attrNode.nodeName; │ │ │ │ - if (name != "trkseg" && name != "rtept") { │ │ │ │ - attributes[name] = value.nodeValue; │ │ │ │ + unselectAll: function(options) { │ │ │ │ + // we'll want an option to supress notification here │ │ │ │ + var layers = this.layers || [this.layer], │ │ │ │ + layer, feature, l, numExcept; │ │ │ │ + for (l = 0; l < layers.length; ++l) { │ │ │ │ + layer = layers[l]; │ │ │ │ + numExcept = 0; │ │ │ │ + //layer.selectedFeatures is null when layer is destroyed and │ │ │ │ + //one of it's preremovelayer listener calls setLayer │ │ │ │ + //with another layer on this control │ │ │ │ + if (layer.selectedFeatures != null) { │ │ │ │ + while (layer.selectedFeatures.length > numExcept) { │ │ │ │ + feature = layer.selectedFeatures[numExcept]; │ │ │ │ + if (!options || options.except != feature) { │ │ │ │ + this.unselect(feature); │ │ │ │ + } else { │ │ │ │ + ++numExcept; │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ - attrNode = attrNode.nextSibling; │ │ │ │ } │ │ │ │ - return attributes; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Accepts Feature Collection, and returns a string. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} List of features to serialize into a string. │ │ │ │ - * metadata - {Object} A key/value pairs object to build a metadata node to │ │ │ │ - * add to the gpx. Supported keys are 'name', 'desc', 'author'. │ │ │ │ + * Method: clickFeature │ │ │ │ + * Called on click in a feature │ │ │ │ + * Only responds if this.hover is false. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - write: function(features, metadata) { │ │ │ │ - features = OpenLayers.Util.isArray(features) ? │ │ │ │ - features : [features]; │ │ │ │ - var gpx = this.createElementNS(this.namespaces.gpx, "gpx"); │ │ │ │ - gpx.setAttribute("version", "1.1"); │ │ │ │ - gpx.setAttribute("creator", this.creator); │ │ │ │ - this.setAttributes(gpx, { │ │ │ │ - "xsi:schemaLocation": this.schemaLocation │ │ │ │ - }); │ │ │ │ - │ │ │ │ - if (metadata && typeof metadata == 'object') { │ │ │ │ - gpx.appendChild(this.buildMetadataNode(metadata)); │ │ │ │ - } │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - gpx.appendChild(this.buildFeatureNode(features[i])); │ │ │ │ + clickFeature: function(feature) { │ │ │ │ + if (!this.hover) { │ │ │ │ + var selected = (OpenLayers.Util.indexOf( │ │ │ │ + feature.layer.selectedFeatures, feature) > -1); │ │ │ │ + if (selected) { │ │ │ │ + if (this.toggleSelect()) { │ │ │ │ + this.unselect(feature); │ │ │ │ + } else if (!this.multipleSelect()) { │ │ │ │ + this.unselectAll({ │ │ │ │ + except: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + if (!this.multipleSelect()) { │ │ │ │ + this.unselectAll({ │ │ │ │ + except: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + this.select(feature); │ │ │ │ + } │ │ │ │ } │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [gpx]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: buildMetadataNode │ │ │ │ - * Creates a "metadata" node. │ │ │ │ + * Method: multipleSelect │ │ │ │ + * Allow for multiple selected features based on <multiple> property and │ │ │ │ + * <multipleKey> event modifier. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * {Boolean} Allow for multiple selected features. │ │ │ │ */ │ │ │ │ - buildMetadataNode: function(metadata) { │ │ │ │ - var types = ['name', 'desc', 'author'], │ │ │ │ - node = this.createElementNS(this.namespaces.gpx, 'metadata'); │ │ │ │ - for (var i = 0; i < types.length; i++) { │ │ │ │ - var type = types[i]; │ │ │ │ - if (metadata[type]) { │ │ │ │ - var n = this.createElementNS(this.namespaces.gpx, type); │ │ │ │ - n.appendChild(this.createTextNode(metadata[type])); │ │ │ │ - node.appendChild(n); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ + multipleSelect: function() { │ │ │ │ + return this.multiple || (this.handlers.feature.evt && │ │ │ │ + this.handlers.feature.evt[this.multipleKey]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: buildFeatureNode │ │ │ │ - * Accepts an <OpenLayers.Feature.Vector>, and builds a node for it. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * Method: toggleSelect │ │ │ │ + * Event should toggle the selected state of a feature based on <toggle> │ │ │ │ + * property and <toggleKey> event modifier. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} - The created node, either a 'wpt' or a 'trk'. │ │ │ │ + * {Boolean} Toggle the selected state of a feature. │ │ │ │ */ │ │ │ │ - buildFeatureNode: function(feature) { │ │ │ │ - var geometry = feature.geometry; │ │ │ │ - geometry = geometry.clone(); │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - geometry.transform(this.internalProjection, │ │ │ │ - this.externalProjection); │ │ │ │ + toggleSelect: function() { │ │ │ │ + return this.toggle || (this.handlers.feature.evt && │ │ │ │ + this.handlers.feature.evt[this.toggleKey]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: clickoutFeature │ │ │ │ + * Called on click outside a previously clicked (selected) feature. │ │ │ │ + * Only responds if this.hover is false. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Vector.Feature>} │ │ │ │ + */ │ │ │ │ + clickoutFeature: function(feature) { │ │ │ │ + if (!this.hover && this.clickout) { │ │ │ │ + this.unselectAll(); │ │ │ │ } │ │ │ │ - if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - var wpt = this.buildWptNode(geometry); │ │ │ │ - this.appendAttributesNode(wpt, feature); │ │ │ │ - return wpt; │ │ │ │ - } else { │ │ │ │ - var trkNode = this.createElementNS(this.namespaces.gpx, "trk"); │ │ │ │ - this.appendAttributesNode(trkNode, feature); │ │ │ │ - var trkSegNodes = this.buildTrkSegNode(geometry); │ │ │ │ - trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ? │ │ │ │ - trkSegNodes : [trkSegNodes]; │ │ │ │ - for (var i = 0, len = trkSegNodes.length; i < len; i++) { │ │ │ │ - trkNode.appendChild(trkSegNodes[i]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: overFeature │ │ │ │ + * Called on over a feature. │ │ │ │ + * Only responds if this.hover is true. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + */ │ │ │ │ + overFeature: function(feature) { │ │ │ │ + var layer = feature.layer; │ │ │ │ + if (this.hover) { │ │ │ │ + if (this.highlightOnly) { │ │ │ │ + this.highlight(feature); │ │ │ │ + } else if (OpenLayers.Util.indexOf( │ │ │ │ + layer.selectedFeatures, feature) == -1) { │ │ │ │ + this.select(feature); │ │ │ │ } │ │ │ │ - return trkNode; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: buildTrkSegNode │ │ │ │ - * Builds trkseg node(s) given a geometry │ │ │ │ + * Method: outFeature │ │ │ │ + * Called on out of a selected feature. │ │ │ │ + * Only responds if this.hover is true. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * trknode │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - buildTrkSegNode: function(geometry) { │ │ │ │ - var node, │ │ │ │ - i, │ │ │ │ - len, │ │ │ │ - point, │ │ │ │ - nodes; │ │ │ │ - if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" || │ │ │ │ - geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { │ │ │ │ - node = this.createElementNS(this.namespaces.gpx, "trkseg"); │ │ │ │ - for (i = 0, len = geometry.components.length; i < len; i++) { │ │ │ │ - point = geometry.components[i]; │ │ │ │ - node.appendChild(this.buildTrkPtNode(point)); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - } else { │ │ │ │ - nodes = []; │ │ │ │ - for (i = 0, len = geometry.components.length; i < len; i++) { │ │ │ │ - nodes.push(this.buildTrkSegNode(geometry.components[i])); │ │ │ │ + outFeature: function(feature) { │ │ │ │ + if (this.hover) { │ │ │ │ + if (this.highlightOnly) { │ │ │ │ + // we do nothing if we're not the last highlighter of the │ │ │ │ + // feature │ │ │ │ + if (feature._lastHighlighter == this.id) { │ │ │ │ + // if another select control had highlighted the feature before │ │ │ │ + // we did it ourself then we use that control to highlight the │ │ │ │ + // feature as it was before we highlighted it, else we just │ │ │ │ + // unhighlight it │ │ │ │ + if (feature._prevHighlighter && │ │ │ │ + feature._prevHighlighter != this.id) { │ │ │ │ + delete feature._lastHighlighter; │ │ │ │ + var control = this.map.getControl( │ │ │ │ + feature._prevHighlighter); │ │ │ │ + if (control) { │ │ │ │ + control.highlight(feature); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.unhighlight(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.unselect(feature); │ │ │ │ } │ │ │ │ - return nodes; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: buildTrkPtNode │ │ │ │ - * Builds a trkpt node given a point │ │ │ │ + * Method: highlight │ │ │ │ + * Redraw feature with the select style. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A trkpt node │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - buildTrkPtNode: function(point) { │ │ │ │ - var node = this.createElementNS(this.namespaces.gpx, "trkpt"); │ │ │ │ - node.setAttribute("lon", point.x); │ │ │ │ - node.setAttribute("lat", point.y); │ │ │ │ - return node; │ │ │ │ + highlight: function(feature) { │ │ │ │ + var layer = feature.layer; │ │ │ │ + var cont = this.events.triggerEvent("beforefeaturehighlighted", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + if (cont !== false) { │ │ │ │ + feature._prevHighlighter = feature._lastHighlighter; │ │ │ │ + feature._lastHighlighter = this.id; │ │ │ │ + var style = this.selectStyle || this.renderIntent; │ │ │ │ + layer.drawFeature(feature, style); │ │ │ │ + this.events.triggerEvent("featurehighlighted", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: buildWptNode │ │ │ │ - * Builds a wpt node given a point │ │ │ │ + * Method: unhighlight │ │ │ │ + * Redraw feature with the "default" style │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry.Point>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A wpt node │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - buildWptNode: function(geometry) { │ │ │ │ - var node = this.createElementNS(this.namespaces.gpx, "wpt"); │ │ │ │ - node.setAttribute("lon", geometry.x); │ │ │ │ - node.setAttribute("lat", geometry.y); │ │ │ │ - return node; │ │ │ │ + unhighlight: function(feature) { │ │ │ │ + var layer = feature.layer; │ │ │ │ + // three cases: │ │ │ │ + // 1. there's no other highlighter, in that case _prev is undefined, │ │ │ │ + // and we just need to undef _last │ │ │ │ + // 2. another control highlighted the feature after we did it, in │ │ │ │ + // that case _last references this other control, and we just │ │ │ │ + // need to undef _prev │ │ │ │ + // 3. another control highlighted the feature before we did it, in │ │ │ │ + // that case _prev references this other control, and we need to │ │ │ │ + // set _last to _prev and undef _prev │ │ │ │ + if (feature._prevHighlighter == undefined) { │ │ │ │ + delete feature._lastHighlighter; │ │ │ │ + } else if (feature._prevHighlighter == this.id) { │ │ │ │ + delete feature._prevHighlighter; │ │ │ │ + } else { │ │ │ │ + feature._lastHighlighter = feature._prevHighlighter; │ │ │ │ + delete feature._prevHighlighter; │ │ │ │ + } │ │ │ │ + layer.drawFeature(feature, feature.style || feature.layer.style || │ │ │ │ + "default"); │ │ │ │ + this.events.triggerEvent("featureunhighlighted", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: appendAttributesNode │ │ │ │ - * Adds some attributes node. │ │ │ │ + * Method: select │ │ │ │ + * Add feature to the layer's selectedFeature array, render the feature as │ │ │ │ + * selected, and call the onSelect function. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + */ │ │ │ │ + select: function(feature) { │ │ │ │ + var cont = this.onBeforeSelect.call(this.scope, feature); │ │ │ │ + var layer = feature.layer; │ │ │ │ + if (cont !== false) { │ │ │ │ + cont = layer.events.triggerEvent("beforefeatureselected", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + if (cont !== false) { │ │ │ │ + layer.selectedFeatures.push(feature); │ │ │ │ + this.highlight(feature); │ │ │ │ + // if the feature handler isn't involved in the feature │ │ │ │ + // selection (because the box handler is used or the │ │ │ │ + // feature is selected programatically) we fake the │ │ │ │ + // feature handler to allow unselecting on click │ │ │ │ + if (!this.handlers.feature.lastFeature) { │ │ │ │ + this.handlers.feature.lastFeature = layer.selectedFeatures[0]; │ │ │ │ + } │ │ │ │ + layer.events.triggerEvent("featureselected", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + this.onSelect.call(this.scope, feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: unselect │ │ │ │ + * Remove feature from the layer's selectedFeature array, render the feature as │ │ │ │ + * normal, and call the onUnselect function. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} the node to append the attribute nodes to. │ │ │ │ * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - appendAttributesNode: function(node, feature) { │ │ │ │ - var name = this.createElementNS(this.namespaces.gpx, 'name'); │ │ │ │ - name.appendChild(this.createTextNode( │ │ │ │ - feature.attributes.name || feature.id)); │ │ │ │ - node.appendChild(name); │ │ │ │ - var desc = this.createElementNS(this.namespaces.gpx, 'desc'); │ │ │ │ - desc.appendChild(this.createTextNode( │ │ │ │ - feature.attributes.description || this.defaultDesc)); │ │ │ │ - node.appendChild(desc); │ │ │ │ - // TBD - deal with remaining (non name/description) attributes. │ │ │ │ + unselect: function(feature) { │ │ │ │ + var layer = feature.layer; │ │ │ │ + // Store feature style for restoration later │ │ │ │ + this.unhighlight(feature); │ │ │ │ + OpenLayers.Util.removeItem(layer.selectedFeatures, feature); │ │ │ │ + layer.events.triggerEvent("featureunselected", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + this.onUnselect.call(this.scope, feature); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.GPX" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WCSCapabilities.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + /** │ │ │ │ + * Method: selectBox │ │ │ │ + * Callback from the handlers.box set up when <box> selection is true │ │ │ │ + * on. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> } │ │ │ │ + */ │ │ │ │ + selectBox: function(position) { │ │ │ │ + if (position instanceof OpenLayers.Bounds) { │ │ │ │ + var minXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.left, │ │ │ │ + y: position.bottom │ │ │ │ + }); │ │ │ │ + var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.right, │ │ │ │ + y: position.top │ │ │ │ + }); │ │ │ │ + var bounds = new OpenLayers.Bounds( │ │ │ │ + minXY.lon, minXY.lat, maxXY.lon, maxXY.lat │ │ │ │ + ); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ - */ │ │ │ │ + // if multiple is false, first deselect currently selected features │ │ │ │ + if (!this.multipleSelect()) { │ │ │ │ + this.unselectAll(); │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WCSCapabilities │ │ │ │ - * Read WCS Capabilities. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WCSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + // because we're using a box, we consider we want multiple selection │ │ │ │ + var prevMultiple = this.multiple; │ │ │ │ + this.multiple = true; │ │ │ │ + var layers = this.layers || [this.layer]; │ │ │ │ + this.events.triggerEvent("boxselectionstart", { │ │ │ │ + layers: layers │ │ │ │ + }); │ │ │ │ + var layer; │ │ │ │ + for (var l = 0; l < layers.length; ++l) { │ │ │ │ + layer = layers[l]; │ │ │ │ + for (var i = 0, len = layer.features.length; i < len; ++i) { │ │ │ │ + var feature = layer.features[i]; │ │ │ │ + // check if the feature is displayed │ │ │ │ + if (!feature.getVisibility()) { │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ - * {String} Version number to assume if none found. Default is "1.1.0". │ │ │ │ - */ │ │ │ │ - defaultVersion: "1.1.0", │ │ │ │ + if (this.geometryTypes == null || OpenLayers.Util.indexOf( │ │ │ │ + this.geometryTypes, feature.geometry.CLASS_NAME) > -1) { │ │ │ │ + if (bounds.toGeometry().intersects(feature.geometry)) { │ │ │ │ + if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { │ │ │ │ + this.select(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.multiple = prevMultiple; │ │ │ │ + this.events.triggerEvent("boxselectionend", { │ │ │ │ + layers: layers │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WCSCapabilities │ │ │ │ - * Create a new parser for WCS capabilities. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the control. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ + setMap: function(map) { │ │ │ │ + this.handlers.feature.setMap(map); │ │ │ │ + if (this.box) { │ │ │ │ + this.handlers.box.setMap(map); │ │ │ │ + } │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read capabilities data from a string, and return a list of coverages. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * APIMethod: setLayer │ │ │ │ + * Attach a new layer to the control, overriding any existing layers. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Array} List of named coverages. │ │ │ │ + * Parameters: │ │ │ │ + * layers - Array of {<OpenLayers.Layer.Vector>} or a single │ │ │ │ + * {<OpenLayers.Layer.Vector>} │ │ │ │ */ │ │ │ │ + setLayer: function(layers) { │ │ │ │ + var isActive = this.active; │ │ │ │ + this.unselectAll(); │ │ │ │ + this.deactivate(); │ │ │ │ + if (this.layers) { │ │ │ │ + this.layer.destroy(); │ │ │ │ + this.layers = null; │ │ │ │ + } │ │ │ │ + this.initLayer(layers); │ │ │ │ + this.handlers.feature.layer = this.layer; │ │ │ │ + if (isActive) { │ │ │ │ + this.activate(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WCSCapabilities" │ │ │ │ - │ │ │ │ + CLASS_NAME: "OpenLayers.Control.SelectFeature" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/OWSContext.js │ │ │ │ + OpenLayers/Control/PanZoom.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/Context.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Events/buttonclick.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.OWSContext │ │ │ │ - * Read and write OWS Context documents. OWS Context documents are a │ │ │ │ - * preliminary OGC (Open Geospatial Consortium) standard for storing the │ │ │ │ - * state of a web mapping application. In a way it is the successor to │ │ │ │ - * Web Map Context (WMC), since it is more generic and more types of layers │ │ │ │ - * can be stored. Also, nesting of layers is supported since version 0.3.1. │ │ │ │ - * For more information see: http://www.ogcnetwork.net/context │ │ │ │ + * Class: OpenLayers.Control.PanZoom │ │ │ │ + * The PanZoom is a visible control, composed of a │ │ │ │ + * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomPanel>. By │ │ │ │ + * default it is drawn in the upper left corner of the map. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.Context> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context, { │ │ │ │ +OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ - * {String} Version number to assume if none found. Default is "0.3.1". │ │ │ │ + /** │ │ │ │ + * APIProperty: slideFactor │ │ │ │ + * {Integer} Number of pixels by which we'll pan the map in any direction │ │ │ │ + * on clicking the arrow buttons. If you want to pan by some ratio │ │ │ │ + * of the map dimensions, use <slideRatio> instead. │ │ │ │ */ │ │ │ │ - defaultVersion: "0.3.1", │ │ │ │ + slideFactor: 50, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: slideRatio │ │ │ │ + * {Number} The fraction of map width/height by which we'll pan the map │ │ │ │ + * on clicking the arrow buttons. Default is null. If set, will │ │ │ │ + * override <slideFactor>. E.g. if slideRatio is .5, then the Pan Up │ │ │ │ + * button will pan up half the map height. │ │ │ │ + */ │ │ │ │ + slideRatio: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: buttons │ │ │ │ + * {Array(DOMElement)} Array of Button Divs │ │ │ │ + */ │ │ │ │ + buttons: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: position │ │ │ │ + * {<OpenLayers.Pixel>} │ │ │ │ + */ │ │ │ │ + position: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.OWSContext │ │ │ │ - * Create a new parser for OWS Context documents. │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Control.PanZoom │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ + initialize: function(options) { │ │ │ │ + this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X, │ │ │ │ + OpenLayers.Control.PanZoom.Y); │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIMethod: destroy │ │ │ │ */ │ │ │ │ - getVersion: function(root, options) { │ │ │ │ - var version = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply( │ │ │ │ - this, arguments); │ │ │ │ - // 0.3.1 is backwards compatible with 0.3.0 │ │ │ │ - if (version === "0.3.0") { │ │ │ │ - version = this.defaultVersion; │ │ │ │ + destroy: function() { │ │ │ │ + if (this.map) { │ │ │ │ + this.map.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ } │ │ │ │ - return version; │ │ │ │ + this.removeButtons(); │ │ │ │ + this.buttons = null; │ │ │ │ + this.position = null; │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * │ │ │ │ + * Properties: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ + this.map.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: toContext │ │ │ │ - * Create a context object free from layer given a map or a │ │ │ │ - * context object. │ │ │ │ + * Method: draw │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * obj - {<OpenLayers.Map> | Object} The map or context. │ │ │ │ - * │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Object} A context object. │ │ │ │ + * {DOMElement} A reference to the container div for the PanZoom control. │ │ │ │ */ │ │ │ │ - toContext: function(obj) { │ │ │ │ - var context = {}; │ │ │ │ - if (obj.CLASS_NAME == "OpenLayers.Map") { │ │ │ │ - context.bounds = obj.getExtent(); │ │ │ │ - context.maxExtent = obj.maxExtent; │ │ │ │ - context.projection = obj.projection; │ │ │ │ - context.size = obj.getSize(); │ │ │ │ - context.layers = obj.layers; │ │ │ │ - } │ │ │ │ - return context; │ │ │ │ - }, │ │ │ │ + draw: function(px) { │ │ │ │ + // initialize our internal div │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ + px = this.position; │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.OWSContext" │ │ │ │ + // place the controls │ │ │ │ + this.buttons = []; │ │ │ │ │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WFSCapabilities.js │ │ │ │ - ====================================================================== */ │ │ │ │ + var sz = { │ │ │ │ + w: 18, │ │ │ │ + h: 18 │ │ │ │ + }; │ │ │ │ + var centered = new OpenLayers.Pixel(px.x + sz.w / 2, px.y); │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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._addButton("panup", "north-mini.png", centered, sz); │ │ │ │ + px.y = centered.y + sz.h; │ │ │ │ + this._addButton("panleft", "west-mini.png", px, sz); │ │ │ │ + this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz); │ │ │ │ + this._addButton("pandown", "south-mini.png", │ │ │ │ + centered.add(0, sz.h * 2), sz); │ │ │ │ + this._addButton("zoomin", "zoom-plus-mini.png", │ │ │ │ + centered.add(0, sz.h * 3 + 5), sz); │ │ │ │ + this._addButton("zoomworld", "zoom-world-mini.png", │ │ │ │ + centered.add(0, sz.h * 4 + 5), sz); │ │ │ │ + this._addButton("zoomout", "zoom-minus-mini.png", │ │ │ │ + centered.add(0, sz.h * 5 + 5), sz); │ │ │ │ + return this.div; │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Method: _addButton │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * id - {String} │ │ │ │ + * img - {String} │ │ │ │ + * xy - {<OpenLayers.Pixel>} │ │ │ │ + * sz - {<OpenLayers.Size>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A Div (an alphaImageDiv, to be precise) that contains the │ │ │ │ + * image of the button, and has all the proper event handlers set. │ │ │ │ + */ │ │ │ │ + _addButton: function(id, img, xy, sz) { │ │ │ │ + var imgLocation = OpenLayers.Util.getImageLocation(img); │ │ │ │ + var btn = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ + this.id + "_" + id, │ │ │ │ + xy, sz, imgLocation, "absolute"); │ │ │ │ + btn.style.cursor = "pointer"; │ │ │ │ + //we want to add the outer div │ │ │ │ + this.div.appendChild(btn); │ │ │ │ + btn.action = id; │ │ │ │ + btn.className = "olButton"; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WFSCapabilities │ │ │ │ - * Read WFS Capabilities. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + //we want to remember/reference the outer div │ │ │ │ + this.buttons.push(btn); │ │ │ │ + return btn; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ - * {String} Version number to assume if none found. Default is "1.1.0". │ │ │ │ + * Method: _removeButton │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * btn - {Object} │ │ │ │ */ │ │ │ │ - defaultVersion: "1.1.0", │ │ │ │ + _removeButton: function(btn) { │ │ │ │ + this.div.removeChild(btn); │ │ │ │ + OpenLayers.Util.removeItem(this.buttons, btn); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.WFSCapabilities │ │ │ │ - * Create a new parser for WFS capabilities. │ │ │ │ + * Method: removeButtons │ │ │ │ + */ │ │ │ │ + removeButtons: function() { │ │ │ │ + for (var i = this.buttons.length - 1; i >= 0; --i) { │ │ │ │ + this._removeButton(this.buttons[i]); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: onButtonClick │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ + onButtonClick: function(evt) { │ │ │ │ + var btn = evt.buttonElement; │ │ │ │ + switch (btn.action) { │ │ │ │ + case "panup": │ │ │ │ + this.map.pan(0, -this.getSlideFactor("h")); │ │ │ │ + break; │ │ │ │ + case "pandown": │ │ │ │ + this.map.pan(0, this.getSlideFactor("h")); │ │ │ │ + break; │ │ │ │ + case "panleft": │ │ │ │ + this.map.pan(-this.getSlideFactor("w"), 0); │ │ │ │ + break; │ │ │ │ + case "panright": │ │ │ │ + this.map.pan(this.getSlideFactor("w"), 0); │ │ │ │ + break; │ │ │ │ + case "zoomin": │ │ │ │ + this.map.zoomIn(); │ │ │ │ + break; │ │ │ │ + case "zoomout": │ │ │ │ + this.map.zoomOut(); │ │ │ │ + break; │ │ │ │ + case "zoomworld": │ │ │ │ + this.map.zoomToMaxExtent(); │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read capabilities data from a string, and return a list of layers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * Method: getSlideFactor │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * dim - {String} "w" or "h" (for width or height). │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array} List of named layers. │ │ │ │ + * {Number} The slide factor for panning in the requested direction. │ │ │ │ */ │ │ │ │ + getSlideFactor: function(dim) { │ │ │ │ + return this.slideRatio ? │ │ │ │ + this.map.getSize()[dim] * this.slideRatio : │ │ │ │ + this.slideFactor; │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WFSCapabilities" │ │ │ │ - │ │ │ │ + CLASS_NAME: "OpenLayers.Control.PanZoom" │ │ │ │ }); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: X │ │ │ │ + * {Integer} │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.PanZoom.X = 4; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: Y │ │ │ │ + * {Integer} │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.PanZoom.Y = 4; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/KML.js │ │ │ │ + OpenLayers/Control/Split.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/BaseTypes/Date.js │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ - * @requires OpenLayers/Geometry/Point.js │ │ │ │ - * @requires OpenLayers/Geometry/LineString.js │ │ │ │ - * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ - * @requires OpenLayers/Geometry/Collection.js │ │ │ │ - * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ - * @requires OpenLayers/Projection.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Handler/Path.js │ │ │ │ + * @requires OpenLayers/Layer/Vector.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.KML │ │ │ │ - * Read/Write KML. Create a new instance with the <OpenLayers.Format.KML> │ │ │ │ - * constructor. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Control.Split │ │ │ │ + * Acts as a split feature agent while editing vector features. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ +OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * beforesplit - Triggered before a split occurs. Listeners receive an │ │ │ │ + * event object with *source* and *target* properties. │ │ │ │ + * split - Triggered when a split occurs. Listeners receive an event with │ │ │ │ + * an *original* property and a *features* property. The original │ │ │ │ + * is a reference to the target feature that the sketch or modified │ │ │ │ + * feature intersects. The features property is a list of all features │ │ │ │ + * that result from this single split. This event is triggered before │ │ │ │ + * the resulting features are added to the layer (while the layer still │ │ │ │ + * has a reference to the original). │ │ │ │ + * aftersplit - Triggered after all splits resulting from a single sketch │ │ │ │ + * or feature modification have occurred. The original features │ │ │ │ + * have been destroyed and features that result from the split │ │ │ │ + * have already been added to the layer. Listeners receive an event │ │ │ │ + * with a *source* and *features* property. The source references the │ │ │ │ + * sketch or modified feature used as a splitter. The features │ │ │ │ + * property is a list of all resulting features. │ │ │ │ */ │ │ │ │ - namespaces: { │ │ │ │ - kml: "http://www.opengis.net/kml/2.2", │ │ │ │ - gx: "http://www.google.com/kml/ext/2.2" │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: kmlns │ │ │ │ - * {String} KML Namespace to use. Defaults to 2.0 namespace. │ │ │ │ + * APIProperty: layer │ │ │ │ + * {<OpenLayers.Layer.Vector>} The target layer with features to be split. │ │ │ │ + * Set at construction or after construction with <setLayer>. │ │ │ │ */ │ │ │ │ - kmlns: "http://earth.google.com/kml/2.0", │ │ │ │ + layer: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: placemarksDesc │ │ │ │ - * {String} Name of the placemarks. Default is "No description available". │ │ │ │ + /** │ │ │ │ + * Property: source │ │ │ │ + * {<OpenLayers.Layer.Vector>} Optional source layer. Any newly created │ │ │ │ + * or modified features from this layer will be used to split features │ │ │ │ + * on the target layer. If not provided, a temporary sketch layer will │ │ │ │ + * be created. │ │ │ │ */ │ │ │ │ - placemarksDesc: "No description available", │ │ │ │ + source: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: foldersName │ │ │ │ - * {String} Name of the folders. Default is "OpenLayers export". │ │ │ │ - * If set to null, no name element will be created. │ │ │ │ + /** │ │ │ │ + * Property: sourceOptions │ │ │ │ + * {Options} If a temporary sketch layer is created, these layer options │ │ │ │ + * will be applied. │ │ │ │ */ │ │ │ │ - foldersName: "OpenLayers export", │ │ │ │ + sourceOptions: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: foldersDesc │ │ │ │ - * {String} Description of the folders. Default is "Exported on [date]." │ │ │ │ - * If set to null, no description element will be created. │ │ │ │ + /** │ │ │ │ + * APIProperty: tolerance │ │ │ │ + * {Number} Distance between the calculated intersection and a vertex on │ │ │ │ + * the source geometry below which the existing vertex will be used │ │ │ │ + * for the split. Default is null. │ │ │ │ */ │ │ │ │ - foldersDesc: "Exported on " + new Date(), │ │ │ │ + tolerance: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: extractAttributes │ │ │ │ - * {Boolean} Extract attributes from KML. Default is true. │ │ │ │ - * Extracting styleUrls requires this to be set to true │ │ │ │ - * Note that currently only Data and SimpleData │ │ │ │ - * elements are handled. │ │ │ │ + * APIProperty: edge │ │ │ │ + * {Boolean} Allow splits given intersection of edges only. Default is │ │ │ │ + * true. If false, a vertex on the source must be within the │ │ │ │ + * <tolerance> distance of the calculated intersection for a split │ │ │ │ + * to occur. │ │ │ │ */ │ │ │ │ - extractAttributes: true, │ │ │ │ + edge: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: kvpAttributes │ │ │ │ - * {Boolean} Only used if extractAttributes is true. │ │ │ │ - * If set to true, attributes will be simple │ │ │ │ - * key-value pairs, compatible with other formats, │ │ │ │ - * Any displayName elements will be ignored. │ │ │ │ - * If set to false, attributes will be objects, │ │ │ │ - * retaining any displayName elements, but not │ │ │ │ - * compatible with other formats. Any CDATA in │ │ │ │ - * displayName will be read in as a string value. │ │ │ │ - * Default is false. │ │ │ │ + * APIProperty: deferDelete │ │ │ │ + * {Boolean} Instead of removing features from the layer, set feature │ │ │ │ + * states of split features to DELETE. This assumes a save strategy │ │ │ │ + * or other component is in charge of removing features from the │ │ │ │ + * layer. Default is false. If false, split features will be │ │ │ │ + * immediately deleted from the layer. │ │ │ │ */ │ │ │ │ - kvpAttributes: false, │ │ │ │ + deferDelete: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: extractStyles │ │ │ │ - * {Boolean} Extract styles from KML. Default is false. │ │ │ │ - * Extracting styleUrls also requires extractAttributes to be │ │ │ │ - * set to true │ │ │ │ + * APIProperty: mutual │ │ │ │ + * {Boolean} If source and target layers are the same, split source │ │ │ │ + * features and target features where they intersect. Default is │ │ │ │ + * true. If false, only target features will be split. │ │ │ │ */ │ │ │ │ - extractStyles: false, │ │ │ │ + mutual: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: extractTracks │ │ │ │ - * {Boolean} Extract gx:Track elements from Placemark elements. Default │ │ │ │ - * is false. If true, features will be generated for all points in │ │ │ │ - * all gx:Track elements. Features will have a when (Date) attribute │ │ │ │ - * based on when elements in the track. If tracks include angle │ │ │ │ - * elements, features will have heading, tilt, and roll attributes. │ │ │ │ - * If track point coordinates have three values, features will have │ │ │ │ - * an altitude attribute with the third coordinate value. │ │ │ │ + * APIProperty: targetFilter │ │ │ │ + * {<OpenLayers.Filter>} Optional filter that will be evaluated │ │ │ │ + * to determine if a feature from the target layer is eligible for │ │ │ │ + * splitting. │ │ │ │ */ │ │ │ │ - extractTracks: false, │ │ │ │ + targetFilter: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: trackAttributes │ │ │ │ - * {Array} If <extractTracks> is true, points within gx:Track elements will │ │ │ │ - * be parsed as features with when, heading, tilt, and roll attributes. │ │ │ │ - * Any additional attribute names can be provided in <trackAttributes>. │ │ │ │ + * APIProperty: sourceFilter │ │ │ │ + * {<OpenLayers.Filter>} Optional filter that will be evaluated │ │ │ │ + * to determine if a feature from the source layer is eligible for │ │ │ │ + * splitting. │ │ │ │ */ │ │ │ │ - trackAttributes: null, │ │ │ │ + sourceFilter: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: internalns │ │ │ │ - * {String} KML Namespace to use -- defaults to the namespace of the │ │ │ │ - * Placemark node being parsed, but falls back to kmlns. │ │ │ │ + * Property: handler │ │ │ │ + * {<OpenLayers.Handler.Path>} The temporary sketch handler created if │ │ │ │ + * no source layer is provided. │ │ │ │ */ │ │ │ │ - internalns: null, │ │ │ │ + handler: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: features │ │ │ │ - * {Array} Array of features │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Control.Split │ │ │ │ + * Creates a new split control. A control is constructed with a target │ │ │ │ + * layer and an optional source layer. While the control is active, │ │ │ │ + * creating new features or modifying existing features on the source │ │ │ │ + * layer will result in splitting any eligible features on the target │ │ │ │ + * layer. If no source layer is provided, a temporary sketch layer will │ │ │ │ + * be created to create lines for splitting features on the target. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An object containing all configuration properties for │ │ │ │ + * the control. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} The target layer. Features from this │ │ │ │ + * layer will be split by new or modified features on the source layer │ │ │ │ + * or temporary sketch layer. │ │ │ │ + * source - {<OpenLayers.Layer.Vector>} Optional source layer. If provided │ │ │ │ + * newly created features or modified features will be used to split │ │ │ │ + * features on the target layer. If not provided, a temporary sketch │ │ │ │ + * layer will be created for drawing lines. │ │ │ │ + * tolerance - {Number} Optional value for the distance between a source │ │ │ │ + * vertex and the calculated intersection below which the split will │ │ │ │ + * occur at the vertex. │ │ │ │ + * edge - {Boolean} Allow splits given intersection of edges only. Default │ │ │ │ + * is true. If false, a vertex on the source must be within the │ │ │ │ + * <tolerance> distance of the calculated intersection for a split │ │ │ │ + * to occur. │ │ │ │ + * mutual - {Boolean} If source and target are the same, split source │ │ │ │ + * features and target features where they intersect. Default is │ │ │ │ + * true. If false, only target features will be split. │ │ │ │ + * targetFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated │ │ │ │ + * to determine if a feature from the target layer is eligible for │ │ │ │ + * splitting. │ │ │ │ + * sourceFilter - {<OpenLayers.Filter>} Optional filter that will be evaluated │ │ │ │ + * to determine if a feature from the target layer is eligible for │ │ │ │ + * splitting. │ │ │ │ */ │ │ │ │ - features: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + this.options = options || {}; // TODO: this could be done by the super │ │ │ │ + │ │ │ │ + // set the source layer if provided │ │ │ │ + if (this.options.source) { │ │ │ │ + this.setSource(this.options.source); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: styles │ │ │ │ - * {Object} Storage of style objects │ │ │ │ - * │ │ │ │ + * APIMethod: setSource │ │ │ │ + * Set the source layer for edits layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} The new source layer layer. If │ │ │ │ + * null, a temporary sketch layer will be created. │ │ │ │ */ │ │ │ │ - styles: null, │ │ │ │ + setSource: function(layer) { │ │ │ │ + if (this.active) { │ │ │ │ + this.deactivate(); │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.destroy(); │ │ │ │ + delete this.handler; │ │ │ │ + } │ │ │ │ + this.source = layer; │ │ │ │ + this.activate(); │ │ │ │ + } else { │ │ │ │ + this.source = layer; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: styleBaseUrl │ │ │ │ - * {String} │ │ │ │ + * APIMethod: activate │ │ │ │ + * Activate the control. Activating the control registers listeners for │ │ │ │ + * editing related events so that during feature creation and │ │ │ │ + * modification, features in the target will be considered for │ │ │ │ + * splitting. │ │ │ │ */ │ │ │ │ - styleBaseUrl: "", │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Control.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + if (!this.source) { │ │ │ │ + if (!this.handler) { │ │ │ │ + this.handler = new OpenLayers.Handler.Path(this, { │ │ │ │ + done: function(geometry) { │ │ │ │ + this.onSketchComplete({ │ │ │ │ + feature: new OpenLayers.Feature.Vector(geometry) │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, { │ │ │ │ + layerOptions: this.sourceOptions │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + this.handler.activate(); │ │ │ │ + } else if (this.source.events) { │ │ │ │ + this.source.events.on({ │ │ │ │ + sketchcomplete: this.onSketchComplete, │ │ │ │ + afterfeaturemodified: this.afterFeatureModified, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return activated; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: fetched │ │ │ │ - * {Object} Storage of KML URLs that have been fetched before │ │ │ │ - * in order to prevent reloading them. │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivate the control. Deactivating the control unregisters listeners │ │ │ │ + * so feature editing may proceed without engaging the split agent. │ │ │ │ */ │ │ │ │ - fetched: null, │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Control.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + if (this.source && this.source.events) { │ │ │ │ + this.source.events.un({ │ │ │ │ + sketchcomplete: this.onSketchComplete, │ │ │ │ + afterfeaturemodified: this.afterFeatureModified, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return deactivated; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: maxDepth │ │ │ │ - * {Integer} Maximum depth for recursive loading external KML URLs │ │ │ │ - * Defaults to 0: do no external fetching │ │ │ │ + * Method: onSketchComplete │ │ │ │ + * Registered as a listener for the sketchcomplete event on the editable │ │ │ │ + * layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * event - {Object} The sketch complete event. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Stop the sketch from being added to the layer (it has been │ │ │ │ + * split). │ │ │ │ */ │ │ │ │ - maxDepth: 0, │ │ │ │ + onSketchComplete: function(event) { │ │ │ │ + this.feature = null; │ │ │ │ + return !this.considerSplit(event.feature); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.KML │ │ │ │ - * Create a new parser for KML. │ │ │ │ + * Method: afterFeatureModified │ │ │ │ + * Registered as a listener for the afterfeaturemodified event on the │ │ │ │ + * editable layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * event - {Object} The after feature modified event. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - // compile regular expressions once instead of every time they are used │ │ │ │ - this.regExes = { │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ - trimComma: (/\s*,\s*/g), │ │ │ │ - kmlColor: (/(\w{2})(\w{2})(\w{2})(\w{2})/), │ │ │ │ - kmlIconPalette: (/root:\/\/icons\/palette-(\d+)(\.\w+)/), │ │ │ │ - straightBracket: (/\$\[(.*?)\]/g) │ │ │ │ - }; │ │ │ │ - // KML coordinates are always in longlat WGS84 │ │ │ │ - this.externalProjection = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + afterFeatureModified: function(event) { │ │ │ │ + if (event.modified) { │ │ │ │ + var feature = event.feature; │ │ │ │ + if (typeof feature.geometry.split === "function") { │ │ │ │ + this.feature = event.feature; │ │ │ │ + this.considerSplit(event.feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ + /** │ │ │ │ + * Method: removeByGeometry │ │ │ │ + * Remove a feature from a list based on the given geometry. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} A list of features. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} A geometry. │ │ │ │ + */ │ │ │ │ + removeByGeometry: function(features, geometry) { │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + if (features[i].geometry === geometry) { │ │ │ │ + features.splice(i, 1); │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read data from a string, and return a list of features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * Method: isEligible │ │ │ │ + * Test if a target feature is eligible for splitting. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * target - {<OpenLayers.Feature.Vector>} The target feature. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} List of features. │ │ │ │ + * {Boolean} The target is eligible for splitting. │ │ │ │ */ │ │ │ │ - read: function(data) { │ │ │ │ - this.features = []; │ │ │ │ - this.styles = {}; │ │ │ │ - this.fetched = {}; │ │ │ │ - │ │ │ │ - // Set default options │ │ │ │ - var options = { │ │ │ │ - depth: 0, │ │ │ │ - styleBaseUrl: this.styleBaseUrl │ │ │ │ - }; │ │ │ │ - │ │ │ │ - return this.parseData(data, options); │ │ │ │ + isEligible: function(target) { │ │ │ │ + if (!target.geometry) { │ │ │ │ + return false; │ │ │ │ + } else { │ │ │ │ + return ( │ │ │ │ + target.state !== OpenLayers.State.DELETE │ │ │ │ + ) && ( │ │ │ │ + typeof target.geometry.split === "function" │ │ │ │ + ) && ( │ │ │ │ + this.feature !== target │ │ │ │ + ) && ( │ │ │ │ + !this.targetFilter || │ │ │ │ + this.targetFilter.evaluate(target.attributes) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseData │ │ │ │ - * Read data from a string, and return a list of features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * options - {Object} Hash of options │ │ │ │ + * Method: considerSplit │ │ │ │ + * Decide whether or not to split target features with the supplied │ │ │ │ + * feature. If <mutual> is true, both the source and target features │ │ │ │ + * will be split if eligible. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The newly created or modified │ │ │ │ + * feature. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} List of features. │ │ │ │ + * {Boolean} The supplied feature was split (and destroyed). │ │ │ │ */ │ │ │ │ - parseData: function(data, options) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + considerSplit: function(feature) { │ │ │ │ + var sourceSplit = false; │ │ │ │ + var targetSplit = false; │ │ │ │ + if (!this.sourceFilter || │ │ │ │ + this.sourceFilter.evaluate(feature.attributes)) { │ │ │ │ + var features = this.layer && this.layer.features || []; │ │ │ │ + var target, results, proceed; │ │ │ │ + var additions = [], │ │ │ │ + removals = []; │ │ │ │ + var mutual = (this.layer === this.source) && this.mutual; │ │ │ │ + var options = { │ │ │ │ + edge: this.edge, │ │ │ │ + tolerance: this.tolerance, │ │ │ │ + mutual: mutual │ │ │ │ + }; │ │ │ │ + var sourceParts = [feature.geometry]; │ │ │ │ + var targetFeature, targetParts; │ │ │ │ + var source, parts; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + targetFeature = features[i]; │ │ │ │ + if (this.isEligible(targetFeature)) { │ │ │ │ + targetParts = [targetFeature.geometry]; │ │ │ │ + // work through source geoms - this array may change │ │ │ │ + for (var j = 0; j < sourceParts.length; ++j) { │ │ │ │ + source = sourceParts[j]; │ │ │ │ + // work through target parts - this array may change │ │ │ │ + for (var k = 0; k < targetParts.length; ++k) { │ │ │ │ + target = targetParts[k]; │ │ │ │ + if (source.getBounds().intersectsBounds(target.getBounds())) { │ │ │ │ + results = source.split(target, options); │ │ │ │ + if (results) { │ │ │ │ + proceed = this.events.triggerEvent( │ │ │ │ + "beforesplit", { │ │ │ │ + source: feature, │ │ │ │ + target: targetFeature │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + if (proceed !== false) { │ │ │ │ + if (mutual) { │ │ │ │ + parts = results[0]; │ │ │ │ + // handle parts that result from source splitting │ │ │ │ + if (parts.length > 1) { │ │ │ │ + // splice in new source parts │ │ │ │ + parts.unshift(j, 1); // add args for splice below │ │ │ │ + Array.prototype.splice.apply(sourceParts, parts); │ │ │ │ + j += parts.length - 3; │ │ │ │ + } │ │ │ │ + results = results[1]; │ │ │ │ + } │ │ │ │ + // handle parts that result from target splitting │ │ │ │ + if (results.length > 1) { │ │ │ │ + // splice in new target parts │ │ │ │ + results.unshift(k, 1); // add args for splice below │ │ │ │ + Array.prototype.splice.apply(targetParts, results); │ │ │ │ + k += results.length - 3; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (targetParts && targetParts.length > 1) { │ │ │ │ + this.geomsToFeatures(targetFeature, targetParts); │ │ │ │ + this.events.triggerEvent("split", { │ │ │ │ + original: targetFeature, │ │ │ │ + features: targetParts │ │ │ │ + }); │ │ │ │ + Array.prototype.push.apply(additions, targetParts); │ │ │ │ + removals.push(targetFeature); │ │ │ │ + targetSplit = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (sourceParts && sourceParts.length > 1) { │ │ │ │ + this.geomsToFeatures(feature, sourceParts); │ │ │ │ + this.events.triggerEvent("split", { │ │ │ │ + original: feature, │ │ │ │ + features: sourceParts │ │ │ │ + }); │ │ │ │ + Array.prototype.push.apply(additions, sourceParts); │ │ │ │ + removals.push(feature); │ │ │ │ + sourceSplit = true; │ │ │ │ + } │ │ │ │ + if (sourceSplit || targetSplit) { │ │ │ │ + // remove and add feature events are suppressed │ │ │ │ + // listen for split event on this control instead │ │ │ │ + if (this.deferDelete) { │ │ │ │ + // Set state instead of removing. Take care to avoid │ │ │ │ + // setting delete for features that have not yet been │ │ │ │ + // inserted - those should be destroyed immediately. │ │ │ │ + var feat, destroys = []; │ │ │ │ + for (var i = 0, len = removals.length; i < len; ++i) { │ │ │ │ + feat = removals[i]; │ │ │ │ + if (feat.state === OpenLayers.State.INSERT) { │ │ │ │ + destroys.push(feat); │ │ │ │ + } else { │ │ │ │ + feat.state = OpenLayers.State.DELETE; │ │ │ │ + this.layer.drawFeature(feat); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.layer.destroyFeatures(destroys, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + for (var i = 0, len = additions.length; i < len; ++i) { │ │ │ │ + additions[i].state = OpenLayers.State.INSERT; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.layer.destroyFeatures(removals, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + this.layer.addFeatures(additions, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.events.triggerEvent("aftersplit", { │ │ │ │ + source: feature, │ │ │ │ + features: additions │ │ │ │ + }); │ │ │ │ + } │ │ │ │ } │ │ │ │ + return sourceSplit; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Loop throught the following node types in this order and │ │ │ │ - // process the nodes found │ │ │ │ - var types = ["Link", "NetworkLink", "Style", "StyleMap", "Placemark"]; │ │ │ │ - for (var i = 0, len = types.length; i < len; ++i) { │ │ │ │ - var type = types[i]; │ │ │ │ - │ │ │ │ - var nodes = this.getElementsByTagNameNS(data, "*", type); │ │ │ │ + /** │ │ │ │ + * Method: geomsToFeatures │ │ │ │ + * Create new features given a template feature and a list of geometries. │ │ │ │ + * The list of geometries is modified in place. The result will be │ │ │ │ + * a list of new features. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The feature to be cloned. │ │ │ │ + * geoms - {Array(<OpenLayers.Geometry>)} List of goemetries. This will │ │ │ │ + * become a list of new features. │ │ │ │ + */ │ │ │ │ + geomsToFeatures: function(feature, geoms) { │ │ │ │ + var clone = feature.clone(); │ │ │ │ + delete clone.geometry; │ │ │ │ + var newFeature; │ │ │ │ + for (var i = 0, len = geoms.length; i < len; ++i) { │ │ │ │ + // turn results list from geoms to features │ │ │ │ + newFeature = clone.clone(); │ │ │ │ + newFeature.geometry = geoms[i]; │ │ │ │ + newFeature.state = OpenLayers.State.INSERT; │ │ │ │ + geoms[i] = newFeature; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // skip to next type if no nodes are found │ │ │ │ - if (nodes.length == 0) { │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * Clean up the control. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + if (this.active) { │ │ │ │ + this.deactivate(); // TODO: this should be handled by the super │ │ │ │ + } │ │ │ │ + OpenLayers.Control.prototype.destroy.call(this); │ │ │ │ + }, │ │ │ │ │ │ │ │ - switch (type.toLowerCase()) { │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Split" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/MousePosition.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - // Fetch external links │ │ │ │ - case "link": │ │ │ │ - case "networklink": │ │ │ │ - this.parseLinks(nodes, options); │ │ │ │ - break; │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - // parse style information │ │ │ │ - case "style": │ │ │ │ - if (this.extractStyles) { │ │ │ │ - this.parseStyles(nodes, options); │ │ │ │ - } │ │ │ │ - break; │ │ │ │ - case "stylemap": │ │ │ │ - if (this.extractStyles) { │ │ │ │ - this.parseStyleMaps(nodes, options); │ │ │ │ - } │ │ │ │ - break; │ │ │ │ │ │ │ │ - // parse features │ │ │ │ - case "placemark": │ │ │ │ - this.parseFeatures(nodes, options); │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - return this.features; │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.MousePosition │ │ │ │ + * The MousePosition control displays geographic coordinates of the mouse │ │ │ │ + * pointer, as it is moved about the map. │ │ │ │ + * │ │ │ │ + * You can use the <prefix>- or <suffix>-properties to provide more information │ │ │ │ + * about the displayed coordinates to the user: │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * var mousePositionCtrl = new OpenLayers.Control.MousePosition({ │ │ │ │ + * prefix: '<a target="_blank" ' + │ │ │ │ + * 'href="http://spatialreference.org/ref/epsg/4326/">' + │ │ │ │ + * 'EPSG:4326</a> coordinates: ' │ │ │ │ + * } │ │ │ │ + * ); │ │ │ │ + * (end code) │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseLinks │ │ │ │ - * Finds URLs of linked KML documents and fetches them │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * nodes - {Array} of {DOMElement} data to read/parse. │ │ │ │ - * options - {Object} Hash of options │ │ │ │ - * │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * true. │ │ │ │ */ │ │ │ │ - parseLinks: function(nodes, options) { │ │ │ │ + autoActivate: true, │ │ │ │ │ │ │ │ - // Fetch external links <NetworkLink> and <Link> │ │ │ │ - // Don't do anything if we have reached our maximum depth for recursion │ │ │ │ - if (options.depth >= this.maxDepth) { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: element │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + element: null, │ │ │ │ │ │ │ │ - // increase depth │ │ │ │ - var newOptions = OpenLayers.Util.extend({}, options); │ │ │ │ - newOptions.depth++; │ │ │ │ + /** │ │ │ │ + * APIProperty: prefix │ │ │ │ + * {String} A string to be prepended to the current pointers coordinates │ │ │ │ + * when it is rendered. Defaults to the empty string ''. │ │ │ │ + */ │ │ │ │ + prefix: '', │ │ │ │ │ │ │ │ - for (var i = 0, len = nodes.length; i < len; i++) { │ │ │ │ - var href = this.parseProperty(nodes[i], "*", "href"); │ │ │ │ - if (href && !this.fetched[href]) { │ │ │ │ - this.fetched[href] = true; // prevent reloading the same urls │ │ │ │ - var data = this.fetchLink(href); │ │ │ │ - if (data) { │ │ │ │ - this.parseData(data, newOptions); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: separator │ │ │ │ + * {String} A string to be used to seperate the two coordinates from each │ │ │ │ + * other. Defaults to the string ', ', which will result in a │ │ │ │ + * rendered coordinate of e.g. '42.12, 21.22'. │ │ │ │ + */ │ │ │ │ + separator: ', ', │ │ │ │ │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: suffix │ │ │ │ + * {String} A string to be appended to the current pointers coordinates │ │ │ │ + * when it is rendered. Defaults to the empty string ''. │ │ │ │ + */ │ │ │ │ + suffix: '', │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: fetchLink │ │ │ │ - * Fetches a URL and returns the result │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * href - {String} url to be fetched │ │ │ │ - * │ │ │ │ + * APIProperty: numDigits │ │ │ │ + * {Integer} The number of digits each coordinate shall have when being │ │ │ │ + * rendered, Defaults to 5. │ │ │ │ */ │ │ │ │ - fetchLink: function(href) { │ │ │ │ - var request = OpenLayers.Request.GET({ │ │ │ │ - url: href, │ │ │ │ - async: false │ │ │ │ - }); │ │ │ │ - if (request) { │ │ │ │ - return request.responseText; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + numDigits: 5, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseStyles │ │ │ │ - * Parses <Style> nodes │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * nodes - {Array} of {DOMElement} data to read/parse. │ │ │ │ - * options - {Object} Hash of options │ │ │ │ - * │ │ │ │ + * APIProperty: granularity │ │ │ │ + * {Integer} │ │ │ │ */ │ │ │ │ - parseStyles: function(nodes, options) { │ │ │ │ - for (var i = 0, len = nodes.length; i < len; i++) { │ │ │ │ - var style = this.parseStyle(nodes[i]); │ │ │ │ - if (style) { │ │ │ │ - var styleName = (options.styleBaseUrl || "") + "#" + style.id; │ │ │ │ + granularity: 10, │ │ │ │ │ │ │ │ - this.styles[styleName] = style; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: emptyString │ │ │ │ + * {String} Set this to some value to set when the mouse is outside the │ │ │ │ + * map. │ │ │ │ + */ │ │ │ │ + emptyString: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseKmlColor │ │ │ │ - * Parses a kml color (in 'aabbggrr' format) and returns the corresponding │ │ │ │ - * color and opacity or null if the color is invalid. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * kmlColor - {String} a kml formated color │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} │ │ │ │ + * Property: lastXy │ │ │ │ + * {<OpenLayers.Pixel>} │ │ │ │ */ │ │ │ │ - parseKmlColor: function(kmlColor) { │ │ │ │ - var color = null; │ │ │ │ - if (kmlColor) { │ │ │ │ - var matches = kmlColor.match(this.regExes.kmlColor); │ │ │ │ - if (matches) { │ │ │ │ - color = { │ │ │ │ - color: '#' + matches[4] + matches[3] + matches[2], │ │ │ │ - opacity: parseInt(matches[1], 16) / 255 │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return color; │ │ │ │ - }, │ │ │ │ + lastXy: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseStyle │ │ │ │ - * Parses the children of a <Style> node and builds the style hash │ │ │ │ - * accordingly │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} <Style> node │ │ │ │ - * │ │ │ │ + * APIProperty: displayProjection │ │ │ │ + * {<OpenLayers.Projection>} The projection in which the mouse position is │ │ │ │ + * displayed. │ │ │ │ */ │ │ │ │ - parseStyle: function(node) { │ │ │ │ - var style = {}; │ │ │ │ + displayProjection: null, │ │ │ │ │ │ │ │ - var types = ["LineStyle", "PolyStyle", "IconStyle", "BalloonStyle", │ │ │ │ - "LabelStyle" │ │ │ │ - ]; │ │ │ │ - var type, styleTypeNode, nodeList, geometry, parser; │ │ │ │ - for (var i = 0, len = types.length; i < len; ++i) { │ │ │ │ - type = types[i]; │ │ │ │ - styleTypeNode = this.getElementsByTagNameNS(node, "*", type)[0]; │ │ │ │ - if (!styleTypeNode) { │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.MousePosition │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Options for control. │ │ │ │ + */ │ │ │ │ │ │ │ │ - // only deal with first geometry of this type │ │ │ │ - switch (type.toLowerCase()) { │ │ │ │ - case "linestyle": │ │ │ │ - var kmlColor = this.parseProperty(styleTypeNode, "*", "color"); │ │ │ │ - var color = this.parseKmlColor(kmlColor); │ │ │ │ - if (color) { │ │ │ │ - style["strokeColor"] = color.color; │ │ │ │ - style["strokeOpacity"] = color.opacity; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var width = this.parseProperty(styleTypeNode, "*", "width"); │ │ │ │ - if (width) { │ │ │ │ - style["strokeWidth"] = width; │ │ │ │ - } │ │ │ │ - break; │ │ │ │ + /** │ │ │ │ + * APIMethod: activate │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.map.events.register('mousemove', this, this.redraw); │ │ │ │ + this.map.events.register('mouseout', this, this.reset); │ │ │ │ + this.redraw(); │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - case "polystyle": │ │ │ │ - var kmlColor = this.parseProperty(styleTypeNode, "*", "color"); │ │ │ │ - var color = this.parseKmlColor(kmlColor); │ │ │ │ - if (color) { │ │ │ │ - style["fillOpacity"] = color.opacity; │ │ │ │ - style["fillColor"] = color.color; │ │ │ │ - } │ │ │ │ - // Check if fill is disabled │ │ │ │ - var fill = this.parseProperty(styleTypeNode, "*", "fill"); │ │ │ │ - if (fill == "0") { │ │ │ │ - style["fillColor"] = "none"; │ │ │ │ - } │ │ │ │ - // Check if outline is disabled │ │ │ │ - var outline = this.parseProperty(styleTypeNode, "*", "outline"); │ │ │ │ - if (outline == "0") { │ │ │ │ - style["strokeWidth"] = "0"; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIMethod: deactivate │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.map.events.unregister('mousemove', this, this.redraw); │ │ │ │ + this.map.events.unregister('mouseout', this, this.reset); │ │ │ │ + this.element.innerHTML = ""; │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - break; │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ │ │ │ │ - case "iconstyle": │ │ │ │ - // set scale │ │ │ │ - var scale = parseFloat(this.parseProperty(styleTypeNode, │ │ │ │ - "*", "scale") || 1); │ │ │ │ + if (!this.element) { │ │ │ │ + this.div.left = ""; │ │ │ │ + this.div.top = ""; │ │ │ │ + this.element = this.div; │ │ │ │ + } │ │ │ │ │ │ │ │ - // set default width and height of icon │ │ │ │ - var width = 32 * scale; │ │ │ │ - var height = 32 * scale; │ │ │ │ + return this.div; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var iconNode = this.getElementsByTagNameNS(styleTypeNode, │ │ │ │ - "*", │ │ │ │ - "Icon")[0]; │ │ │ │ - if (iconNode) { │ │ │ │ - var href = this.parseProperty(iconNode, "*", "href"); │ │ │ │ - if (href) { │ │ │ │ + /** │ │ │ │ + * Method: redraw │ │ │ │ + */ │ │ │ │ + redraw: function(evt) { │ │ │ │ │ │ │ │ - var w = this.parseProperty(iconNode, "*", "w"); │ │ │ │ - var h = this.parseProperty(iconNode, "*", "h"); │ │ │ │ + var lonLat; │ │ │ │ │ │ │ │ - // Settings for Google specific icons that are 64x64 │ │ │ │ - // We set the width and height to 64 and halve the │ │ │ │ - // scale to prevent icons from being too big │ │ │ │ - var google = "http://maps.google.com/mapfiles/kml"; │ │ │ │ - if (OpenLayers.String.startsWith( │ │ │ │ - href, google) && !w && !h) { │ │ │ │ - w = 64; │ │ │ │ - h = 64; │ │ │ │ - scale = scale / 2; │ │ │ │ - } │ │ │ │ + if (evt == null) { │ │ │ │ + this.reset(); │ │ │ │ + return; │ │ │ │ + } else { │ │ │ │ + if (this.lastXy == null || │ │ │ │ + Math.abs(evt.xy.x - this.lastXy.x) > this.granularity || │ │ │ │ + Math.abs(evt.xy.y - this.lastXy.y) > this.granularity) { │ │ │ │ + this.lastXy = evt.xy; │ │ │ │ + return; │ │ │ │ + } │ │ │ │ │ │ │ │ - // if only dimension is defined, make sure the │ │ │ │ - // other one has the same value │ │ │ │ - w = w || h; │ │ │ │ - h = h || w; │ │ │ │ + lonLat = this.map.getLonLatFromPixel(evt.xy); │ │ │ │ + if (!lonLat) { │ │ │ │ + // map has not yet been properly initialized │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (this.displayProjection) { │ │ │ │ + lonLat.transform(this.map.getProjectionObject(), │ │ │ │ + this.displayProjection); │ │ │ │ + } │ │ │ │ + this.lastXy = evt.xy; │ │ │ │ │ │ │ │ - if (w) { │ │ │ │ - width = parseInt(w) * scale; │ │ │ │ - } │ │ │ │ + } │ │ │ │ │ │ │ │ - if (h) { │ │ │ │ - height = parseInt(h) * scale; │ │ │ │ - } │ │ │ │ + var newHtml = this.formatOutput(lonLat); │ │ │ │ │ │ │ │ - // support for internal icons │ │ │ │ - // (/root://icons/palette-x.png) │ │ │ │ - // x and y tell the position on the palette: │ │ │ │ - // - in pixels │ │ │ │ - // - starting from the left bottom │ │ │ │ - // We translate that to a position in the list │ │ │ │ - // and request the appropriate icon from the │ │ │ │ - // google maps website │ │ │ │ - var matches = href.match(this.regExes.kmlIconPalette); │ │ │ │ - if (matches) { │ │ │ │ - var palette = matches[1]; │ │ │ │ - var file_extension = matches[2]; │ │ │ │ + if (newHtml != this.element.innerHTML) { │ │ │ │ + this.element.innerHTML = newHtml; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - var x = this.parseProperty(iconNode, "*", "x"); │ │ │ │ - var y = this.parseProperty(iconNode, "*", "y"); │ │ │ │ + /** │ │ │ │ + * Method: reset │ │ │ │ + */ │ │ │ │ + reset: function(evt) { │ │ │ │ + if (this.emptyString != null) { │ │ │ │ + this.element.innerHTML = this.emptyString; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - var posX = x ? x / 32 : 0; │ │ │ │ - var posY = y ? (7 - y / 32) : 7; │ │ │ │ + /** │ │ │ │ + * Method: formatOutput │ │ │ │ + * Override to provide custom display output │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * lonLat - {<OpenLayers.LonLat>} Location to display │ │ │ │ + */ │ │ │ │ + formatOutput: function(lonLat) { │ │ │ │ + var digits = parseInt(this.numDigits); │ │ │ │ + var newHtml = │ │ │ │ + this.prefix + │ │ │ │ + lonLat.lon.toFixed(digits) + │ │ │ │ + this.separator + │ │ │ │ + lonLat.lat.toFixed(digits) + │ │ │ │ + this.suffix; │ │ │ │ + return newHtml; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var pos = posY * 8 + posX; │ │ │ │ - href = "http://maps.google.com/mapfiles/kml/pal" + │ │ │ │ - palette + "/icon" + pos + file_extension; │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Control.MousePosition" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/ZoomBox.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - style["graphicOpacity"] = 1; // fully opaque │ │ │ │ - style["externalGraphic"] = href; │ │ │ │ - } │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Handler/Box.js │ │ │ │ + */ │ │ │ │ │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.ZoomBox │ │ │ │ + * The ZoomBox control enables zooming directly to a given extent, by drawing │ │ │ │ + * a box on the map. The box is drawn by holding down shift, whilst dragging │ │ │ │ + * the mouse. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + /** │ │ │ │ + * Property: type │ │ │ │ + * {OpenLayers.Control.TYPE} │ │ │ │ + */ │ │ │ │ + type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ │ │ │ │ - // hotSpots define the offset for an Icon │ │ │ │ - var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode, │ │ │ │ - "*", │ │ │ │ - "hotSpot")[0]; │ │ │ │ - if (hotSpotNode) { │ │ │ │ - var x = parseFloat(hotSpotNode.getAttribute("x")); │ │ │ │ - var y = parseFloat(hotSpotNode.getAttribute("y")); │ │ │ │ + /** │ │ │ │ + * Property: out │ │ │ │ + * {Boolean} Should the control be used for zooming out? │ │ │ │ + */ │ │ │ │ + out: false, │ │ │ │ │ │ │ │ - var xUnits = hotSpotNode.getAttribute("xunits"); │ │ │ │ - if (xUnits == "pixels") { │ │ │ │ - style["graphicXOffset"] = -x * scale; │ │ │ │ - } else if (xUnits == "insetPixels") { │ │ │ │ - style["graphicXOffset"] = -width + (x * scale); │ │ │ │ - } else if (xUnits == "fraction") { │ │ │ │ - style["graphicXOffset"] = -width * x; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: keyMask │ │ │ │ + * {Integer} Zoom only occurs if the keyMask matches the combination of │ │ │ │ + * keys down. Use bitwise operators and one or more of the │ │ │ │ + * <OpenLayers.Handler> constants to construct a keyMask. Leave null if │ │ │ │ + * not used mask. Default is null. │ │ │ │ + */ │ │ │ │ + keyMask: null, │ │ │ │ │ │ │ │ - var yUnits = hotSpotNode.getAttribute("yunits"); │ │ │ │ - if (yUnits == "pixels") { │ │ │ │ - style["graphicYOffset"] = -height + (y * scale) + 1; │ │ │ │ - } else if (yUnits == "insetPixels") { │ │ │ │ - style["graphicYOffset"] = -(y * scale) + 1; │ │ │ │ - } else if (yUnits == "fraction") { │ │ │ │ - style["graphicYOffset"] = -height * (1 - y) + 1; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: alwaysZoom │ │ │ │ + * {Boolean} Always zoom in/out when box drawn, even if the zoom level does │ │ │ │ + * not change. │ │ │ │ + */ │ │ │ │ + alwaysZoom: false, │ │ │ │ │ │ │ │ - style["graphicWidth"] = width; │ │ │ │ - style["graphicHeight"] = height; │ │ │ │ - break; │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomOnClick │ │ │ │ + * {Boolean} Should we zoom when no box was dragged, i.e. the user only │ │ │ │ + * clicked? Default is true. │ │ │ │ + */ │ │ │ │ + zoomOnClick: true, │ │ │ │ │ │ │ │ - case "balloonstyle": │ │ │ │ - var balloonStyle = OpenLayers.Util.getXmlNodeValue( │ │ │ │ - styleTypeNode); │ │ │ │ - if (balloonStyle) { │ │ │ │ - style["balloonStyle"] = balloonStyle.replace( │ │ │ │ - this.regExes.straightBracket, "${$1}"); │ │ │ │ - } │ │ │ │ - break; │ │ │ │ - case "labelstyle": │ │ │ │ - var kmlColor = this.parseProperty(styleTypeNode, "*", "color"); │ │ │ │ - var color = this.parseKmlColor(kmlColor); │ │ │ │ - if (color) { │ │ │ │ - style["fontColor"] = color.color; │ │ │ │ - style["fontOpacity"] = color.opacity; │ │ │ │ - } │ │ │ │ - break; │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + this.handler = new OpenLayers.Handler.Box(this, { │ │ │ │ + done: this.zoomBox │ │ │ │ + }, { │ │ │ │ + keyMask: this.keyMask │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ │ │ │ │ - default: │ │ │ │ + /** │ │ │ │ + * Method: zoomBox │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>} │ │ │ │ + */ │ │ │ │ + zoomBox: function(position) { │ │ │ │ + if (position instanceof OpenLayers.Bounds) { │ │ │ │ + var bounds, │ │ │ │ + targetCenterPx = position.getCenterPixel(); │ │ │ │ + if (!this.out) { │ │ │ │ + var minXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.left, │ │ │ │ + y: position.bottom │ │ │ │ + }); │ │ │ │ + var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.right, │ │ │ │ + y: position.top │ │ │ │ + }); │ │ │ │ + bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, │ │ │ │ + maxXY.lon, maxXY.lat); │ │ │ │ + } else { │ │ │ │ + var pixWidth = position.right - position.left; │ │ │ │ + var pixHeight = position.bottom - position.top; │ │ │ │ + var zoomFactor = Math.min((this.map.size.h / pixHeight), │ │ │ │ + (this.map.size.w / pixWidth)); │ │ │ │ + var extent = this.map.getExtent(); │ │ │ │ + var center = this.map.getLonLatFromPixel(targetCenterPx); │ │ │ │ + var xmin = center.lon - (extent.getWidth() / 2) * zoomFactor; │ │ │ │ + var xmax = center.lon + (extent.getWidth() / 2) * zoomFactor; │ │ │ │ + var ymin = center.lat - (extent.getHeight() / 2) * zoomFactor; │ │ │ │ + var ymax = center.lat + (extent.getHeight() / 2) * zoomFactor; │ │ │ │ + bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax); │ │ │ │ + } │ │ │ │ + // always zoom in/out │ │ │ │ + var lastZoom = this.map.getZoom(), │ │ │ │ + size = this.map.getSize(), │ │ │ │ + centerPx = { │ │ │ │ + x: size.w / 2, │ │ │ │ + y: size.h / 2 │ │ │ │ + }, │ │ │ │ + zoom = this.map.getZoomForExtent(bounds), │ │ │ │ + oldRes = this.map.getResolution(), │ │ │ │ + newRes = this.map.getResolutionForZoom(zoom); │ │ │ │ + if (oldRes == newRes) { │ │ │ │ + this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx)); │ │ │ │ + } else { │ │ │ │ + var zoomOriginPx = { │ │ │ │ + x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / │ │ │ │ + (oldRes - newRes), │ │ │ │ + y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / │ │ │ │ + (oldRes - newRes) │ │ │ │ + }; │ │ │ │ + this.map.zoomTo(zoom, zoomOriginPx); │ │ │ │ + } │ │ │ │ + if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) { │ │ │ │ + this.map.zoomTo(lastZoom + (this.out ? -1 : 1)); │ │ │ │ + } │ │ │ │ + } else if (this.zoomOnClick) { // it's a pixel │ │ │ │ + if (!this.out) { │ │ │ │ + this.map.zoomTo(this.map.getZoom() + 1, position); │ │ │ │ + } else { │ │ │ │ + this.map.zoomTo(this.map.getZoom() - 1, position); │ │ │ │ } │ │ │ │ } │ │ │ │ - │ │ │ │ - // Some polygons have no line color, so we use the fillColor for that │ │ │ │ - if (!style["strokeColor"] && style["fillColor"]) { │ │ │ │ - style["strokeColor"] = style["fillColor"]; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var id = node.getAttribute("id"); │ │ │ │ - if (id && style) { │ │ │ │ - style.id = id; │ │ │ │ - } │ │ │ │ - │ │ │ │ - return style; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: parseStyleMaps │ │ │ │ - * Parses <StyleMap> nodes, but only uses the 'normal' key │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * nodes - {Array} of {DOMElement} data to read/parse. │ │ │ │ - * options - {Object} Hash of options │ │ │ │ - * │ │ │ │ - */ │ │ │ │ - parseStyleMaps: function(nodes, options) { │ │ │ │ - // Only the default or "normal" part of the StyleMap is processed now │ │ │ │ - // To do the select or "highlight" bit, we'd need to change lots more │ │ │ │ + CLASS_NAME: "OpenLayers.Control.ZoomBox" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/DragPan.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - for (var i = 0, len = nodes.length; i < len; i++) { │ │ │ │ - var node = nodes[i]; │ │ │ │ - var pairs = this.getElementsByTagNameNS(node, "*", │ │ │ │ - "Pair"); │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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 id = node.getAttribute("id"); │ │ │ │ - for (var j = 0, jlen = pairs.length; j < jlen; j++) { │ │ │ │ - var pair = pairs[j]; │ │ │ │ - // Use the shortcut in the SLD format to quickly retrieve the │ │ │ │ - // value of a node. Maybe it's good to have a method in │ │ │ │ - // Format.XML to do this │ │ │ │ - var key = this.parseProperty(pair, "*", "key"); │ │ │ │ - var styleUrl = this.parseProperty(pair, "*", "styleUrl"); │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Handler/Drag.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - if (styleUrl && key == "normal") { │ │ │ │ - this.styles[(options.styleBaseUrl || "") + "#" + id] = │ │ │ │ - this.styles[(options.styleBaseUrl || "") + styleUrl]; │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.DragPan │ │ │ │ + * The DragPan control pans the map with a drag of the mouse. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - // TODO: implement the "select" part │ │ │ │ - //if (styleUrl && key == "highlight") { │ │ │ │ - //} │ │ │ │ + /** │ │ │ │ + * Property: type │ │ │ │ + * {OpenLayers.Control.TYPES} │ │ │ │ + */ │ │ │ │ + type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: panned │ │ │ │ + * {Boolean} The map moved. │ │ │ │ + */ │ │ │ │ + panned: false, │ │ │ │ │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: interval │ │ │ │ + * {Integer} The number of milliseconds that should ellapse before │ │ │ │ + * panning the map again. Defaults to 0 milliseconds, which means that │ │ │ │ + * no separate cycle is used for panning. In most cases you won't want │ │ │ │ + * to change this value. For slow machines/devices larger values can be │ │ │ │ + * tried out. │ │ │ │ + */ │ │ │ │ + interval: 0, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * APIProperty: documentDrag │ │ │ │ + * {Boolean} If set to true, mouse dragging will continue even if the │ │ │ │ + * mouse cursor leaves the map viewport. Default is false. │ │ │ │ + */ │ │ │ │ + documentDrag: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseFeatures │ │ │ │ - * Loop through all Placemark nodes and parse them. │ │ │ │ - * Will create a list of features │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * nodes - {Array} of {DOMElement} data to read/parse. │ │ │ │ - * options - {Object} Hash of options │ │ │ │ - * │ │ │ │ + * Property: kinetic │ │ │ │ + * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object. │ │ │ │ */ │ │ │ │ - parseFeatures: function(nodes, options) { │ │ │ │ - var features = []; │ │ │ │ - for (var i = 0, len = nodes.length; i < len; i++) { │ │ │ │ - var featureNode = nodes[i]; │ │ │ │ - var feature = this.parseFeature.apply(this, [featureNode]); │ │ │ │ - if (feature) { │ │ │ │ + kinetic: null, │ │ │ │ │ │ │ │ - // Create reference to styleUrl │ │ │ │ - if (this.extractStyles && feature.attributes && │ │ │ │ - feature.attributes.styleUrl) { │ │ │ │ - feature.style = this.getStyle(feature.attributes.styleUrl, options); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: enableKinetic │ │ │ │ + * {Boolean} Set this option to enable "kinetic dragging". Can be │ │ │ │ + * set to true or to an object. If set to an object this │ │ │ │ + * object will be passed to the {<OpenLayers.Kinetic>} │ │ │ │ + * constructor. Defaults to true. │ │ │ │ + * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is │ │ │ │ + * included in your build config. │ │ │ │ + */ │ │ │ │ + enableKinetic: true, │ │ │ │ │ │ │ │ - if (this.extractStyles) { │ │ │ │ - // Make sure that <Style> nodes within a placemark are │ │ │ │ - // processed as well │ │ │ │ - var inlineStyleNode = this.getElementsByTagNameNS(featureNode, │ │ │ │ - "*", │ │ │ │ - "Style")[0]; │ │ │ │ - if (inlineStyleNode) { │ │ │ │ - var inlineStyle = this.parseStyle(inlineStyleNode); │ │ │ │ - if (inlineStyle) { │ │ │ │ - feature.style = OpenLayers.Util.extend( │ │ │ │ - feature.style, inlineStyle │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: kineticInterval │ │ │ │ + * {Integer} Interval in milliseconds between 2 steps in the "kinetic │ │ │ │ + * scrolling". Applies only if enableKinetic is set. Defaults │ │ │ │ + * to 10 milliseconds. │ │ │ │ + */ │ │ │ │ + kineticInterval: 10, │ │ │ │ │ │ │ │ - // check if gx:Track elements should be parsed │ │ │ │ - if (this.extractTracks) { │ │ │ │ - var tracks = this.getElementsByTagNameNS( │ │ │ │ - featureNode, this.namespaces.gx, "Track" │ │ │ │ - ); │ │ │ │ - if (tracks && tracks.length > 0) { │ │ │ │ - var track = tracks[0]; │ │ │ │ - var container = { │ │ │ │ - features: [], │ │ │ │ - feature: feature │ │ │ │ - }; │ │ │ │ - this.readNode(track, container); │ │ │ │ - if (container.features.length > 0) { │ │ │ │ - features.push.apply(features, container.features); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - // add feature to list of features │ │ │ │ - features.push(feature); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - throw "Bad Placemark: " + i; │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * Creates a Drag handler, using <panMap> and │ │ │ │ + * <panMapDone> as callbacks. │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + if (this.enableKinetic && OpenLayers.Kinetic) { │ │ │ │ + var config = { │ │ │ │ + interval: this.kineticInterval │ │ │ │ + }; │ │ │ │ + if (typeof this.enableKinetic === "object") { │ │ │ │ + config = OpenLayers.Util.extend(config, this.enableKinetic); │ │ │ │ } │ │ │ │ + this.kinetic = new OpenLayers.Kinetic(config); │ │ │ │ } │ │ │ │ - │ │ │ │ - // add new features to existing feature list │ │ │ │ - this.features = this.features.concat(features); │ │ │ │ + this.handler = new OpenLayers.Handler.Drag(this, { │ │ │ │ + "move": this.panMap, │ │ │ │ + "done": this.panMapDone, │ │ │ │ + "down": this.panMapStart │ │ │ │ + }, { │ │ │ │ + interval: this.interval, │ │ │ │ + documentDrag: this.documentDrag │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: panMapStart │ │ │ │ */ │ │ │ │ - readers: { │ │ │ │ - "kml": { │ │ │ │ - "when": function(node, container) { │ │ │ │ - container.whens.push(OpenLayers.Date.parse( │ │ │ │ - this.getChildValue(node) │ │ │ │ - )); │ │ │ │ - }, │ │ │ │ - "_trackPointAttribute": function(node, container) { │ │ │ │ - var name = node.nodeName.split(":").pop(); │ │ │ │ - container.attributes[name].push(this.getChildValue(node)); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "gx": { │ │ │ │ - "Track": function(node, container) { │ │ │ │ - var obj = { │ │ │ │ - whens: [], │ │ │ │ - points: [], │ │ │ │ - angles: [] │ │ │ │ - }; │ │ │ │ - if (this.trackAttributes) { │ │ │ │ - var name; │ │ │ │ - obj.attributes = {}; │ │ │ │ - for (var i = 0, ii = this.trackAttributes.length; i < ii; ++i) { │ │ │ │ - name = this.trackAttributes[i]; │ │ │ │ - obj.attributes[name] = []; │ │ │ │ - if (!(name in this.readers.kml)) { │ │ │ │ - this.readers.kml[name] = this.readers.kml._trackPointAttribute; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - if (obj.whens.length !== obj.points.length) { │ │ │ │ - throw new Error("gx:Track with unequal number of when (" + │ │ │ │ - obj.whens.length + ") and gx:coord (" + │ │ │ │ - obj.points.length + ") elements."); │ │ │ │ - } │ │ │ │ - var hasAngles = obj.angles.length > 0; │ │ │ │ - if (hasAngles && obj.whens.length !== obj.angles.length) { │ │ │ │ - throw new Error("gx:Track with unequal number of when (" + │ │ │ │ - obj.whens.length + ") and gx:angles (" + │ │ │ │ - obj.angles.length + ") elements."); │ │ │ │ - } │ │ │ │ - var feature, point, angles; │ │ │ │ - for (var i = 0, ii = obj.whens.length; i < ii; ++i) { │ │ │ │ - feature = container.feature.clone(); │ │ │ │ - feature.fid = container.feature.fid || container.feature.id; │ │ │ │ - point = obj.points[i]; │ │ │ │ - feature.geometry = point; │ │ │ │ - if ("z" in point) { │ │ │ │ - feature.attributes.altitude = point.z; │ │ │ │ - } │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - feature.geometry.transform( │ │ │ │ - this.externalProjection, this.internalProjection │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (this.trackAttributes) { │ │ │ │ - for (var j = 0, jj = this.trackAttributes.length; j < jj; ++j) { │ │ │ │ - var name = this.trackAttributes[j]; │ │ │ │ - feature.attributes[name] = obj.attributes[name][i]; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - feature.attributes.when = obj.whens[i]; │ │ │ │ - feature.attributes.trackId = container.feature.id; │ │ │ │ - if (hasAngles) { │ │ │ │ - angles = obj.angles[i]; │ │ │ │ - feature.attributes.heading = parseFloat(angles[0]); │ │ │ │ - feature.attributes.tilt = parseFloat(angles[1]); │ │ │ │ - feature.attributes.roll = parseFloat(angles[2]); │ │ │ │ - } │ │ │ │ - container.features.push(feature); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "coord": function(node, container) { │ │ │ │ - var str = this.getChildValue(node); │ │ │ │ - var coords = str.replace(this.regExes.trimSpace, "").split(/\s+/); │ │ │ │ - var point = new OpenLayers.Geometry.Point(coords[0], coords[1]); │ │ │ │ - if (coords.length > 2) { │ │ │ │ - point.z = parseFloat(coords[2]); │ │ │ │ - } │ │ │ │ - container.points.push(point); │ │ │ │ - }, │ │ │ │ - "angles": function(node, container) { │ │ │ │ - var str = this.getChildValue(node); │ │ │ │ - var parts = str.replace(this.regExes.trimSpace, "").split(/\s+/); │ │ │ │ - container.angles.push(parts); │ │ │ │ - } │ │ │ │ + panMapStart: function() { │ │ │ │ + if (this.kinetic) { │ │ │ │ + this.kinetic.begin(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseFeature │ │ │ │ - * This function is the core of the KML parsing code in OpenLayers. │ │ │ │ - * It creates the geometries that are then attached to the returned │ │ │ │ - * feature, and calls parseAttributes() to get attribute data out. │ │ │ │ + * Method: panMap │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ + * xy - {<OpenLayers.Pixel>} Pixel of the mouse position │ │ │ │ + */ │ │ │ │ + panMap: function(xy) { │ │ │ │ + if (this.kinetic) { │ │ │ │ + this.kinetic.update(xy); │ │ │ │ + } │ │ │ │ + this.panned = true; │ │ │ │ + this.map.pan( │ │ │ │ + this.handler.last.x - xy.x, │ │ │ │ + this.handler.last.y - xy.y, { │ │ │ │ + dragging: true, │ │ │ │ + animate: false │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: panMapDone │ │ │ │ + * Finish the panning operation. Only call setCenter (through <panMap>) │ │ │ │ + * if the map has actually been moved. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A vector feature. │ │ │ │ + * Parameters: │ │ │ │ + * xy - {<OpenLayers.Pixel>} Pixel of the mouse position │ │ │ │ */ │ │ │ │ - parseFeature: function(node) { │ │ │ │ - // only accept one geometry per feature - look for highest "order" │ │ │ │ - var order = ["MultiGeometry", "Polygon", "LineString", "Point"]; │ │ │ │ - var type, nodeList, geometry, parser; │ │ │ │ - for (var i = 0, len = order.length; i < len; ++i) { │ │ │ │ - type = order[i]; │ │ │ │ - this.internalns = node.namespaceURI ? │ │ │ │ - node.namespaceURI : this.kmlns; │ │ │ │ - nodeList = this.getElementsByTagNameNS(node, │ │ │ │ - this.internalns, type); │ │ │ │ - if (nodeList.length > 0) { │ │ │ │ - // only deal with first geometry of this type │ │ │ │ - var parser = this.parseGeometry[type.toLowerCase()]; │ │ │ │ - if (parser) { │ │ │ │ - geometry = parser.apply(this, [nodeList[0]]); │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - geometry.transform(this.externalProjection, │ │ │ │ - this.internalProjection); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - throw new TypeError("Unsupported geometry type: " + type); │ │ │ │ + panMapDone: function(xy) { │ │ │ │ + if (this.panned) { │ │ │ │ + var res = null; │ │ │ │ + if (this.kinetic) { │ │ │ │ + res = this.kinetic.end(xy); │ │ │ │ + } │ │ │ │ + this.map.pan( │ │ │ │ + this.handler.last.x - xy.x, │ │ │ │ + this.handler.last.y - xy.y, { │ │ │ │ + dragging: !!res, │ │ │ │ + animate: false │ │ │ │ } │ │ │ │ - // stop looking for different geometry types │ │ │ │ - break; │ │ │ │ + ); │ │ │ │ + if (res) { │ │ │ │ + var self = this; │ │ │ │ + this.kinetic.move(res, function(x, y, end) { │ │ │ │ + self.map.pan(x, y, { │ │ │ │ + dragging: !end, │ │ │ │ + animate: false │ │ │ │ + }); │ │ │ │ + }); │ │ │ │ } │ │ │ │ + this.panned = false; │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // construct feature (optionally with attributes) │ │ │ │ - var attributes; │ │ │ │ - if (this.extractAttributes) { │ │ │ │ - attributes = this.parseAttributes(node); │ │ │ │ - } │ │ │ │ - var feature = new OpenLayers.Feature.Vector(geometry, attributes); │ │ │ │ + CLASS_NAME: "OpenLayers.Control.DragPan" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/Navigation.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - var fid = node.getAttribute("id") || node.getAttribute("name"); │ │ │ │ - if (fid != null) { │ │ │ │ - feature.fid = fid; │ │ │ │ - } │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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 feature; │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control/ZoomBox.js │ │ │ │ + * @requires OpenLayers/Control/DragPan.js │ │ │ │ + * @requires OpenLayers/Handler/MouseWheel.js │ │ │ │ + * @requires OpenLayers/Handler/Click.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.Navigation │ │ │ │ + * The navigation control handles map browsing with mouse events (dragging, │ │ │ │ + * double-clicking, and scrolling the wheel). Create a new navigation │ │ │ │ + * control with the <OpenLayers.Control.Navigation> control. │ │ │ │ + * │ │ │ │ + * Note that this control is added to the map by default (if no controls │ │ │ │ + * array is sent in the options object to the <OpenLayers.Map> │ │ │ │ + * constructor). │ │ │ │ + * │ │ │ │ + * Inherits: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: dragPan │ │ │ │ + * {<OpenLayers.Control.DragPan>} │ │ │ │ + */ │ │ │ │ + dragPan: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getStyle │ │ │ │ - * Retrieves a style from a style hash using styleUrl as the key │ │ │ │ - * If the styleUrl doesn't exist yet, we try to fetch it │ │ │ │ - * Internet │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * styleUrl - {String} URL of style │ │ │ │ - * options - {Object} Hash of options │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} - (reference to) Style hash │ │ │ │ + * APIProperty: dragPanOptions │ │ │ │ + * {Object} Options passed to the DragPan control. │ │ │ │ */ │ │ │ │ - getStyle: function(styleUrl, options) { │ │ │ │ + dragPanOptions: null, │ │ │ │ │ │ │ │ - var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl); │ │ │ │ + /** │ │ │ │ + * Property: pinchZoom │ │ │ │ + * {<OpenLayers.Control.PinchZoom>} │ │ │ │ + */ │ │ │ │ + pinchZoom: null, │ │ │ │ │ │ │ │ - var newOptions = OpenLayers.Util.extend({}, options); │ │ │ │ - newOptions.depth++; │ │ │ │ - newOptions.styleBaseUrl = styleBaseUrl; │ │ │ │ + /** │ │ │ │ + * APIProperty: pinchZoomOptions │ │ │ │ + * {Object} Options passed to the PinchZoom control. │ │ │ │ + */ │ │ │ │ + pinchZoomOptions: null, │ │ │ │ │ │ │ │ - // Fetch remote Style URLs (if not fetched before) │ │ │ │ - if (!this.styles[styleUrl] && │ │ │ │ - !OpenLayers.String.startsWith(styleUrl, "#") && │ │ │ │ - newOptions.depth <= this.maxDepth && │ │ │ │ - !this.fetched[styleBaseUrl]) { │ │ │ │ + /** │ │ │ │ + * APIProperty: documentDrag │ │ │ │ + * {Boolean} Allow panning of the map by dragging outside map viewport. │ │ │ │ + * Default is false. │ │ │ │ + */ │ │ │ │ + documentDrag: false, │ │ │ │ │ │ │ │ - var data = this.fetchLink(styleBaseUrl); │ │ │ │ - if (data) { │ │ │ │ - this.parseData(data, newOptions); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: zoomBox │ │ │ │ + * {<OpenLayers.Control.ZoomBox>} │ │ │ │ + */ │ │ │ │ + zoomBox: null, │ │ │ │ │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomBoxEnabled │ │ │ │ + * {Boolean} Whether the user can draw a box to zoom │ │ │ │ + */ │ │ │ │ + zoomBoxEnabled: true, │ │ │ │ │ │ │ │ - // return requested style │ │ │ │ - var style = OpenLayers.Util.extend({}, this.styles[styleUrl]); │ │ │ │ - return style; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomWheelEnabled │ │ │ │ + * {Boolean} Whether the mousewheel should zoom the map │ │ │ │ + */ │ │ │ │ + zoomWheelEnabled: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: parseGeometry │ │ │ │ - * Properties of this object are the functions that parse geometries based │ │ │ │ - * on their type. │ │ │ │ + * Property: mouseWheelOptions │ │ │ │ + * {Object} Options passed to the MouseWheel control (only useful if │ │ │ │ + * <zoomWheelEnabled> is set to true). Default is no options for maps │ │ │ │ + * with fractionalZoom set to true, otherwise │ │ │ │ + * {cumulative: false, interval: 50, maxDelta: 6} │ │ │ │ */ │ │ │ │ - parseGeometry: { │ │ │ │ + mouseWheelOptions: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: parseGeometry.point │ │ │ │ - * Given a KML node representing a point geometry, create an OpenLayers │ │ │ │ - * point geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} A KML Point node. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry.Point>} A point geometry. │ │ │ │ - */ │ │ │ │ - point: function(node) { │ │ │ │ - var nodeList = this.getElementsByTagNameNS(node, this.internalns, │ │ │ │ - "coordinates"); │ │ │ │ - var coords = []; │ │ │ │ - if (nodeList.length > 0) { │ │ │ │ - var coordString = nodeList[0].firstChild.nodeValue; │ │ │ │ - coordString = coordString.replace(this.regExes.removeSpace, ""); │ │ │ │ - coords = coordString.split(","); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: handleRightClicks │ │ │ │ + * {Boolean} Whether or not to handle right clicks. Default is false. │ │ │ │ + */ │ │ │ │ + handleRightClicks: false, │ │ │ │ │ │ │ │ - var point = null; │ │ │ │ - if (coords.length > 1) { │ │ │ │ - // preserve third dimension │ │ │ │ - if (coords.length == 2) { │ │ │ │ - coords[2] = null; │ │ │ │ - } │ │ │ │ - point = new OpenLayers.Geometry.Point(coords[0], coords[1], │ │ │ │ - coords[2]); │ │ │ │ - } else { │ │ │ │ - throw "Bad coordinate string: " + coordString; │ │ │ │ - } │ │ │ │ - return point; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomBoxKeyMask │ │ │ │ + * {Integer} <OpenLayers.Handler> key code of the key, which has to be │ │ │ │ + * pressed, while drawing the zoom box with the mouse on the screen. │ │ │ │ + * You should probably set handleRightClicks to true if you use this │ │ │ │ + * with MOD_CTRL, to disable the context menu for machines which use │ │ │ │ + * CTRL-Click as a right click. │ │ │ │ + * Default: <OpenLayers.Handler.MOD_SHIFT> │ │ │ │ + */ │ │ │ │ + zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: parseGeometry.linestring │ │ │ │ - * Given a KML node representing a linestring geometry, create an │ │ │ │ - * OpenLayers linestring geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} A KML LineString node. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry.LineString>} A linestring geometry. │ │ │ │ - */ │ │ │ │ - linestring: function(node, ring) { │ │ │ │ - var nodeList = this.getElementsByTagNameNS(node, this.internalns, │ │ │ │ - "coordinates"); │ │ │ │ - var line = null; │ │ │ │ - if (nodeList.length > 0) { │ │ │ │ - var coordString = this.getChildValue(nodeList[0]); │ │ │ │ + /** │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * true. │ │ │ │ + */ │ │ │ │ + autoActivate: true, │ │ │ │ │ │ │ │ - coordString = coordString.replace(this.regExes.trimSpace, │ │ │ │ - ""); │ │ │ │ - coordString = coordString.replace(this.regExes.trimComma, │ │ │ │ - ","); │ │ │ │ - var pointList = coordString.split(this.regExes.splitSpace); │ │ │ │ - var numPoints = pointList.length; │ │ │ │ - var points = new Array(numPoints); │ │ │ │ - var coords, numCoords; │ │ │ │ - for (var i = 0; i < numPoints; ++i) { │ │ │ │ - coords = pointList[i].split(","); │ │ │ │ - numCoords = coords.length; │ │ │ │ - if (numCoords > 1) { │ │ │ │ - if (coords.length == 2) { │ │ │ │ - coords[2] = null; │ │ │ │ - } │ │ │ │ - points[i] = new OpenLayers.Geometry.Point(coords[0], │ │ │ │ - coords[1], │ │ │ │ - coords[2]); │ │ │ │ - } else { │ │ │ │ - throw "Bad LineString point coordinates: " + │ │ │ │ - pointList[i]; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (numPoints) { │ │ │ │ - if (ring) { │ │ │ │ - line = new OpenLayers.Geometry.LinearRing(points); │ │ │ │ - } else { │ │ │ │ - line = new OpenLayers.Geometry.LineString(points); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - throw "Bad LineString coordinates: " + coordString; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.Navigation │ │ │ │ + * Create a new navigation control │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * the control │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + this.handlers = {}; │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - return line; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * The destroy method is used to perform any clean up before the control │ │ │ │ + * is dereferenced. Typically this is where event listeners are removed │ │ │ │ + * to prevent memory leaks. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: parseGeometry.polygon │ │ │ │ - * Given a KML node representing a polygon geometry, create an │ │ │ │ - * OpenLayers polygon geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} A KML Polygon node. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry.Polygon>} A polygon geometry. │ │ │ │ - */ │ │ │ │ - polygon: function(node) { │ │ │ │ - var nodeList = this.getElementsByTagNameNS(node, this.internalns, │ │ │ │ - "LinearRing"); │ │ │ │ - var numRings = nodeList.length; │ │ │ │ - var components = new Array(numRings); │ │ │ │ - if (numRings > 0) { │ │ │ │ - // this assumes exterior ring first, inner rings after │ │ │ │ - var ring; │ │ │ │ - for (var i = 0, len = nodeList.length; i < len; ++i) { │ │ │ │ - ring = this.parseGeometry.linestring.apply(this, │ │ │ │ - [nodeList[i], true]); │ │ │ │ - if (ring) { │ │ │ │ - components[i] = ring; │ │ │ │ - } else { │ │ │ │ - throw "Bad LinearRing geometry: " + i; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return new OpenLayers.Geometry.Polygon(components); │ │ │ │ - }, │ │ │ │ + if (this.dragPan) { │ │ │ │ + this.dragPan.destroy(); │ │ │ │ + } │ │ │ │ + this.dragPan = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: parseGeometry.multigeometry │ │ │ │ - * Given a KML node representing a multigeometry, create an │ │ │ │ - * OpenLayers geometry collection. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} A KML MultiGeometry node. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry.Collection>} A geometry collection. │ │ │ │ - */ │ │ │ │ - multigeometry: function(node) { │ │ │ │ - var child, parser; │ │ │ │ - var parts = []; │ │ │ │ - var children = node.childNodes; │ │ │ │ - for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ - child = children[i]; │ │ │ │ - if (child.nodeType == 1) { │ │ │ │ - var type = (child.prefix) ? │ │ │ │ - child.nodeName.split(":")[1] : │ │ │ │ - child.nodeName; │ │ │ │ - var parser = this.parseGeometry[type.toLowerCase()]; │ │ │ │ - if (parser) { │ │ │ │ - parts.push(parser.apply(this, [child])); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return new OpenLayers.Geometry.Collection(parts); │ │ │ │ + if (this.zoomBox) { │ │ │ │ + this.zoomBox.destroy(); │ │ │ │ + } │ │ │ │ + this.zoomBox = null; │ │ │ │ + │ │ │ │ + if (this.pinchZoom) { │ │ │ │ + this.pinchZoom.destroy(); │ │ │ │ } │ │ │ │ + this.pinchZoom = null; │ │ │ │ │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseAttributes │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An attributes object. │ │ │ │ + * Method: activate │ │ │ │ */ │ │ │ │ - parseAttributes: function(node) { │ │ │ │ - var attributes = {}; │ │ │ │ - │ │ │ │ - // Extended Data is parsed first. │ │ │ │ - var edNodes = node.getElementsByTagName("ExtendedData"); │ │ │ │ - if (edNodes.length) { │ │ │ │ - attributes = this.parseExtendedData(edNodes[0]); │ │ │ │ + activate: function() { │ │ │ │ + this.dragPan.activate(); │ │ │ │ + if (this.zoomWheelEnabled) { │ │ │ │ + this.handlers.wheel.activate(); │ │ │ │ } │ │ │ │ - │ │ │ │ - // assume attribute nodes are type 1 children with a type 3 or 4 child │ │ │ │ - var child, grandchildren, grandchild; │ │ │ │ - var children = node.childNodes; │ │ │ │ - │ │ │ │ - for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ - child = children[i]; │ │ │ │ - if (child.nodeType == 1) { │ │ │ │ - grandchildren = child.childNodes; │ │ │ │ - if (grandchildren.length >= 1 && grandchildren.length <= 3) { │ │ │ │ - var grandchild; │ │ │ │ - switch (grandchildren.length) { │ │ │ │ - case 1: │ │ │ │ - grandchild = grandchildren[0]; │ │ │ │ - break; │ │ │ │ - case 2: │ │ │ │ - var c1 = grandchildren[0]; │ │ │ │ - var c2 = grandchildren[1]; │ │ │ │ - grandchild = (c1.nodeType == 3 || c1.nodeType == 4) ? │ │ │ │ - c1 : c2; │ │ │ │ - break; │ │ │ │ - case 3: │ │ │ │ - default: │ │ │ │ - grandchild = grandchildren[1]; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - if (grandchild.nodeType == 3 || grandchild.nodeType == 4) { │ │ │ │ - var name = (child.prefix) ? │ │ │ │ - child.nodeName.split(":")[1] : │ │ │ │ - child.nodeName; │ │ │ │ - var value = OpenLayers.Util.getXmlNodeValue(grandchild); │ │ │ │ - if (value) { │ │ │ │ - value = value.replace(this.regExes.trimSpace, ""); │ │ │ │ - attributes[name] = value; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + this.handlers.click.activate(); │ │ │ │ + if (this.zoomBoxEnabled) { │ │ │ │ + this.zoomBox.activate(); │ │ │ │ } │ │ │ │ - return attributes; │ │ │ │ + if (this.pinchZoom) { │ │ │ │ + this.pinchZoom.activate(); │ │ │ │ + } │ │ │ │ + return OpenLayers.Control.prototype.activate.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseExtendedData │ │ │ │ - * Parse ExtendedData from KML. Limited support for schemas/datatypes. │ │ │ │ - * See http://code.google.com/apis/kml/documentation/kmlreference.html#extendeddata │ │ │ │ - * for more information on extendeddata. │ │ │ │ + * Method: deactivate │ │ │ │ */ │ │ │ │ - parseExtendedData: function(node) { │ │ │ │ - var attributes = {}; │ │ │ │ - var i, len, data, key; │ │ │ │ - var dataNodes = node.getElementsByTagName("Data"); │ │ │ │ - for (i = 0, len = dataNodes.length; i < len; i++) { │ │ │ │ - data = dataNodes[i]; │ │ │ │ - key = data.getAttribute("name"); │ │ │ │ - var ed = {}; │ │ │ │ - var valueNode = data.getElementsByTagName("value"); │ │ │ │ - if (valueNode.length) { │ │ │ │ - ed['value'] = this.getChildValue(valueNode[0]); │ │ │ │ - } │ │ │ │ - if (this.kvpAttributes) { │ │ │ │ - attributes[key] = ed['value']; │ │ │ │ - } else { │ │ │ │ - var nameNode = data.getElementsByTagName("displayName"); │ │ │ │ - if (nameNode.length) { │ │ │ │ - ed['displayName'] = this.getChildValue(nameNode[0]); │ │ │ │ - } │ │ │ │ - attributes[key] = ed; │ │ │ │ - } │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.pinchZoom) { │ │ │ │ + this.pinchZoom.deactivate(); │ │ │ │ } │ │ │ │ - var simpleDataNodes = node.getElementsByTagName("SimpleData"); │ │ │ │ - for (i = 0, len = simpleDataNodes.length; i < len; i++) { │ │ │ │ - var ed = {}; │ │ │ │ - data = simpleDataNodes[i]; │ │ │ │ - key = data.getAttribute("name"); │ │ │ │ - ed['value'] = this.getChildValue(data); │ │ │ │ - if (this.kvpAttributes) { │ │ │ │ - attributes[key] = ed['value']; │ │ │ │ - } else { │ │ │ │ - ed['displayName'] = key; │ │ │ │ - attributes[key] = ed; │ │ │ │ - } │ │ │ │ + this.zoomBox.deactivate(); │ │ │ │ + this.dragPan.deactivate(); │ │ │ │ + this.handlers.click.deactivate(); │ │ │ │ + this.handlers.wheel.deactivate(); │ │ │ │ + return OpenLayers.Control.prototype.deactivate.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + // disable right mouse context menu for support of right click events │ │ │ │ + if (this.handleRightClicks) { │ │ │ │ + this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False; │ │ │ │ } │ │ │ │ │ │ │ │ - return attributes; │ │ │ │ + var clickCallbacks = { │ │ │ │ + 'click': this.defaultClick, │ │ │ │ + 'dblclick': this.defaultDblClick, │ │ │ │ + 'dblrightclick': this.defaultDblRightClick │ │ │ │ + }; │ │ │ │ + var clickOptions = { │ │ │ │ + 'double': true, │ │ │ │ + 'stopDouble': true │ │ │ │ + }; │ │ │ │ + this.handlers.click = new OpenLayers.Handler.Click( │ │ │ │ + this, clickCallbacks, clickOptions │ │ │ │ + ); │ │ │ │ + this.dragPan = new OpenLayers.Control.DragPan( │ │ │ │ + OpenLayers.Util.extend({ │ │ │ │ + map: this.map, │ │ │ │ + documentDrag: this.documentDrag │ │ │ │ + }, this.dragPanOptions) │ │ │ │ + ); │ │ │ │ + this.zoomBox = new OpenLayers.Control.ZoomBox({ │ │ │ │ + map: this.map, │ │ │ │ + keyMask: this.zoomBoxKeyMask │ │ │ │ + }); │ │ │ │ + this.dragPan.draw(); │ │ │ │ + this.zoomBox.draw(); │ │ │ │ + var wheelOptions = this.map.fractionalZoom ? {} : { │ │ │ │ + cumulative: false, │ │ │ │ + interval: 50, │ │ │ │ + maxDelta: 6 │ │ │ │ + }; │ │ │ │ + this.handlers.wheel = new OpenLayers.Handler.MouseWheel( │ │ │ │ + this, { │ │ │ │ + up: this.wheelUp, │ │ │ │ + down: this.wheelDown │ │ │ │ + }, │ │ │ │ + OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions) │ │ │ │ + ); │ │ │ │ + if (OpenLayers.Control.PinchZoom) { │ │ │ │ + this.pinchZoom = new OpenLayers.Control.PinchZoom( │ │ │ │ + OpenLayers.Util.extend({ │ │ │ │ + map: this.map │ │ │ │ + }, this.pinchZoomOptions)); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseProperty │ │ │ │ - * Convenience method to find a node and return its value │ │ │ │ + * Method: defaultClick │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * xmlNode - {<DOMElement>} │ │ │ │ - * namespace - {String} namespace of the node to find │ │ │ │ - * tagName - {String} name of the property to parse │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The value for the requested property (defaults to null) │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - parseProperty: function(xmlNode, namespace, tagName) { │ │ │ │ - var value; │ │ │ │ - var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName); │ │ │ │ - try { │ │ │ │ - value = OpenLayers.Util.getXmlNodeValue(nodeList[0]); │ │ │ │ - } catch (e) { │ │ │ │ - value = null; │ │ │ │ + defaultClick: function(evt) { │ │ │ │ + if (evt.lastTouches && evt.lastTouches.length == 2) { │ │ │ │ + this.map.zoomOut(); │ │ │ │ } │ │ │ │ - │ │ │ │ - return value; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Accept Feature Collection, and return a string. │ │ │ │ + * Method: defaultDblClick │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} An array of features. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A KML string. │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - write: function(features) { │ │ │ │ - if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ - features = [features]; │ │ │ │ - } │ │ │ │ - var kml = this.createElementNS(this.kmlns, "kml"); │ │ │ │ - var folder = this.createFolderXML(); │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - folder.appendChild(this.createPlacemarkXML(features[i])); │ │ │ │ - } │ │ │ │ - kml.appendChild(folder); │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [kml]); │ │ │ │ + defaultDblClick: function(evt) { │ │ │ │ + this.map.zoomTo(this.map.zoom + 1, evt.xy); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createFolderXML │ │ │ │ - * Creates and returns a KML folder node │ │ │ │ + * Method: defaultDblRightClick │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - createFolderXML: function() { │ │ │ │ - // Folder │ │ │ │ - var folder = this.createElementNS(this.kmlns, "Folder"); │ │ │ │ - │ │ │ │ - // Folder name │ │ │ │ - if (this.foldersName) { │ │ │ │ - var folderName = this.createElementNS(this.kmlns, "name"); │ │ │ │ - var folderNameText = this.createTextNode(this.foldersName); │ │ │ │ - folderName.appendChild(folderNameText); │ │ │ │ - folder.appendChild(folderName); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // Folder description │ │ │ │ - if (this.foldersDesc) { │ │ │ │ - var folderDesc = this.createElementNS(this.kmlns, "description"); │ │ │ │ - var folderDescText = this.createTextNode(this.foldersDesc); │ │ │ │ - folderDesc.appendChild(folderDescText); │ │ │ │ - folder.appendChild(folderDesc); │ │ │ │ - } │ │ │ │ - │ │ │ │ - return folder; │ │ │ │ + defaultDblRightClick: function(evt) { │ │ │ │ + this.map.zoomTo(this.map.zoom - 1, evt.xy); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createPlacemarkXML │ │ │ │ - * Creates and returns a KML placemark node representing the given feature. │ │ │ │ - * │ │ │ │ + * Method: wheelChange │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * evt - {Event} │ │ │ │ + * deltaZ - {Integer} │ │ │ │ */ │ │ │ │ - createPlacemarkXML: function(feature) { │ │ │ │ - // Placemark name │ │ │ │ - var placemarkName = this.createElementNS(this.kmlns, "name"); │ │ │ │ - var label = (feature.style && feature.style.label) ? feature.style.label : feature.id; │ │ │ │ - var name = feature.attributes.name || label; │ │ │ │ - placemarkName.appendChild(this.createTextNode(name)); │ │ │ │ - │ │ │ │ - // Placemark description │ │ │ │ - var placemarkDesc = this.createElementNS(this.kmlns, "description"); │ │ │ │ - var desc = feature.attributes.description || this.placemarksDesc; │ │ │ │ - placemarkDesc.appendChild(this.createTextNode(desc)); │ │ │ │ - │ │ │ │ - // Placemark │ │ │ │ - var placemarkNode = this.createElementNS(this.kmlns, "Placemark"); │ │ │ │ - if (feature.fid != null) { │ │ │ │ - placemarkNode.setAttribute("id", feature.fid); │ │ │ │ + wheelChange: function(evt, deltaZ) { │ │ │ │ + if (!this.map.fractionalZoom) { │ │ │ │ + deltaZ = Math.round(deltaZ); │ │ │ │ } │ │ │ │ - placemarkNode.appendChild(placemarkName); │ │ │ │ - placemarkNode.appendChild(placemarkDesc); │ │ │ │ - │ │ │ │ - // Geometry node (Point, LineString, etc. nodes) │ │ │ │ - var geometryNode = this.buildGeometryNode(feature.geometry); │ │ │ │ - placemarkNode.appendChild(geometryNode); │ │ │ │ - │ │ │ │ - // output attributes as extendedData │ │ │ │ - if (feature.attributes) { │ │ │ │ - var edNode = this.buildExtendedData(feature.attributes); │ │ │ │ - if (edNode) { │ │ │ │ - placemarkNode.appendChild(edNode); │ │ │ │ - } │ │ │ │ + var currentZoom = this.map.getZoom(), │ │ │ │ + newZoom = currentZoom + deltaZ; │ │ │ │ + newZoom = Math.max(newZoom, 0); │ │ │ │ + newZoom = Math.min(newZoom, this.map.getNumZoomLevels()); │ │ │ │ + if (newZoom === currentZoom) { │ │ │ │ + return; │ │ │ │ } │ │ │ │ - │ │ │ │ - return placemarkNode; │ │ │ │ + this.map.zoomTo(newZoom, evt.xy); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: buildGeometryNode │ │ │ │ - * Builds and returns a KML geometry node with the given geometry. │ │ │ │ + /** │ │ │ │ + * Method: wheelUp │ │ │ │ + * User spun scroll wheel up │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * evt - {Event} │ │ │ │ + * delta - {Integer} │ │ │ │ + */ │ │ │ │ + wheelUp: function(evt, delta) { │ │ │ │ + this.wheelChange(evt, delta || 1); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: wheelDown │ │ │ │ + * User spun scroll wheel down │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + * delta - {Integer} │ │ │ │ */ │ │ │ │ - buildGeometryNode: function(geometry) { │ │ │ │ - var className = geometry.CLASS_NAME; │ │ │ │ - var type = className.substring(className.lastIndexOf(".") + 1); │ │ │ │ - var builder = this.buildGeometry[type.toLowerCase()]; │ │ │ │ - var node = null; │ │ │ │ - if (builder) { │ │ │ │ - node = builder.apply(this, [geometry]); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ + wheelDown: function(evt, delta) { │ │ │ │ + this.wheelChange(evt, delta || -1); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: buildGeometry │ │ │ │ - * Object containing methods to do the actual geometry node building │ │ │ │ - * based on geometry type. │ │ │ │ + * Method: disableZoomBox │ │ │ │ */ │ │ │ │ - buildGeometry: { │ │ │ │ - // TBD: Anybody care about namespace aliases here (these nodes have │ │ │ │ - // no prefixes)? │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: buildGeometry.point │ │ │ │ - * Given an OpenLayers point geometry, create a KML point. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry.Point>} A point geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A KML point node. │ │ │ │ - */ │ │ │ │ - point: function(geometry) { │ │ │ │ - var kml = this.createElementNS(this.kmlns, "Point"); │ │ │ │ - kml.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ - return kml; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: buildGeometry.multipoint │ │ │ │ - * Given an OpenLayers multipoint geometry, create a KML │ │ │ │ - * GeometryCollection. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry.Point>} A multipoint geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A KML GeometryCollection node. │ │ │ │ - */ │ │ │ │ - multipoint: function(geometry) { │ │ │ │ - return this.buildGeometry.collection.apply(this, [geometry]); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: buildGeometry.linestring │ │ │ │ - * Given an OpenLayers linestring geometry, create a KML linestring. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry.LineString>} A linestring geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A KML linestring node. │ │ │ │ - */ │ │ │ │ - linestring: function(geometry) { │ │ │ │ - var kml = this.createElementNS(this.kmlns, "LineString"); │ │ │ │ - kml.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ - return kml; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: buildGeometry.multilinestring │ │ │ │ - * Given an OpenLayers multilinestring geometry, create a KML │ │ │ │ - * GeometryCollection. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry.Point>} A multilinestring geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A KML GeometryCollection node. │ │ │ │ - */ │ │ │ │ - multilinestring: function(geometry) { │ │ │ │ - return this.buildGeometry.collection.apply(this, [geometry]); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: buildGeometry.linearring │ │ │ │ - * Given an OpenLayers linearring geometry, create a KML linearring. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry.LinearRing>} A linearring geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A KML linearring node. │ │ │ │ - */ │ │ │ │ - linearring: function(geometry) { │ │ │ │ - var kml = this.createElementNS(this.kmlns, "LinearRing"); │ │ │ │ - kml.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ - return kml; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: buildGeometry.polygon │ │ │ │ - * Given an OpenLayers polygon geometry, create a KML polygon. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry.Polygon>} A polygon geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A KML polygon node. │ │ │ │ - */ │ │ │ │ - polygon: function(geometry) { │ │ │ │ - var kml = this.createElementNS(this.kmlns, "Polygon"); │ │ │ │ - var rings = geometry.components; │ │ │ │ - var ringMember, ringGeom, type; │ │ │ │ - for (var i = 0, len = rings.length; i < len; ++i) { │ │ │ │ - type = (i == 0) ? "outerBoundaryIs" : "innerBoundaryIs"; │ │ │ │ - ringMember = this.createElementNS(this.kmlns, type); │ │ │ │ - ringGeom = this.buildGeometry.linearring.apply(this, │ │ │ │ - [rings[i]]); │ │ │ │ - ringMember.appendChild(ringGeom); │ │ │ │ - kml.appendChild(ringMember); │ │ │ │ - } │ │ │ │ - return kml; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: buildGeometry.multipolygon │ │ │ │ - * Given an OpenLayers multipolygon geometry, create a KML │ │ │ │ - * GeometryCollection. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry.Point>} A multipolygon geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A KML GeometryCollection node. │ │ │ │ - */ │ │ │ │ - multipolygon: function(geometry) { │ │ │ │ - return this.buildGeometry.collection.apply(this, [geometry]); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: buildGeometry.collection │ │ │ │ - * Given an OpenLayers geometry collection, create a KML MultiGeometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry.Collection>} A geometry collection. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A KML MultiGeometry node. │ │ │ │ - */ │ │ │ │ - collection: function(geometry) { │ │ │ │ - var kml = this.createElementNS(this.kmlns, "MultiGeometry"); │ │ │ │ - var child; │ │ │ │ - for (var i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ - child = this.buildGeometryNode.apply(this, │ │ │ │ - [geometry.components[i]]); │ │ │ │ - if (child) { │ │ │ │ - kml.appendChild(child); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return kml; │ │ │ │ - } │ │ │ │ + disableZoomBox: function() { │ │ │ │ + this.zoomBoxEnabled = false; │ │ │ │ + this.zoomBox.deactivate(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: buildCoordinatesNode │ │ │ │ - * Builds and returns the KML coordinates node with the given geometry │ │ │ │ - * <coordinates>...</coordinates> │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * Method: enableZoomBox │ │ │ │ */ │ │ │ │ - buildCoordinatesNode: function(geometry) { │ │ │ │ - var coordinatesNode = this.createElementNS(this.kmlns, "coordinates"); │ │ │ │ - │ │ │ │ - var path; │ │ │ │ - var points = geometry.components; │ │ │ │ - if (points) { │ │ │ │ - // LineString or LinearRing │ │ │ │ - var point; │ │ │ │ - var numPoints = points.length; │ │ │ │ - var parts = new Array(numPoints); │ │ │ │ - for (var i = 0; i < numPoints; ++i) { │ │ │ │ - point = points[i]; │ │ │ │ - parts[i] = this.buildCoordinates(point); │ │ │ │ - } │ │ │ │ - path = parts.join(" "); │ │ │ │ - } else { │ │ │ │ - // Point │ │ │ │ - path = this.buildCoordinates(geometry); │ │ │ │ + enableZoomBox: function() { │ │ │ │ + this.zoomBoxEnabled = true; │ │ │ │ + if (this.active) { │ │ │ │ + this.zoomBox.activate(); │ │ │ │ } │ │ │ │ - │ │ │ │ - var txtNode = this.createTextNode(path); │ │ │ │ - coordinatesNode.appendChild(txtNode); │ │ │ │ - │ │ │ │ - return coordinatesNode; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: buildCoordinates │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} │ │ │ │ - * │ │ │ │ - * Returns │ │ │ │ - * {String} a coordinate pair │ │ │ │ + * Method: disableZoomWheel │ │ │ │ */ │ │ │ │ - buildCoordinates: function(point) { │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - point = point.clone(); │ │ │ │ - point.transform(this.internalProjection, │ │ │ │ - this.externalProjection); │ │ │ │ - } │ │ │ │ - return point.x + "," + point.y; │ │ │ │ + │ │ │ │ + disableZoomWheel: function() { │ │ │ │ + this.zoomWheelEnabled = false; │ │ │ │ + this.handlers.wheel.deactivate(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: buildExtendedData │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * attributes - {Object} │ │ │ │ - * │ │ │ │ - * Returns │ │ │ │ - * {DOMElement} A KML ExtendedData node or {null} if no attributes. │ │ │ │ + * Method: enableZoomWheel │ │ │ │ */ │ │ │ │ - buildExtendedData: function(attributes) { │ │ │ │ - var extendedData = this.createElementNS(this.kmlns, "ExtendedData"); │ │ │ │ - for (var attributeName in attributes) { │ │ │ │ - // empty, name, description, styleUrl attributes ignored │ │ │ │ - if (attributes[attributeName] && attributeName != "name" && attributeName != "description" && attributeName != "styleUrl") { │ │ │ │ - var data = this.createElementNS(this.kmlns, "Data"); │ │ │ │ - data.setAttribute("name", attributeName); │ │ │ │ - var value = this.createElementNS(this.kmlns, "value"); │ │ │ │ - if (typeof attributes[attributeName] == "object") { │ │ │ │ - // cater for object attributes with 'value' properties │ │ │ │ - // other object properties will output an empty node │ │ │ │ - if (attributes[attributeName].value) { │ │ │ │ - value.appendChild(this.createTextNode(attributes[attributeName].value)); │ │ │ │ - } │ │ │ │ - if (attributes[attributeName].displayName) { │ │ │ │ - var displayName = this.createElementNS(this.kmlns, "displayName"); │ │ │ │ - // displayName always written as CDATA │ │ │ │ - displayName.appendChild(this.getXMLDoc().createCDATASection(attributes[attributeName].displayName)); │ │ │ │ - data.appendChild(displayName); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - value.appendChild(this.createTextNode(attributes[attributeName])); │ │ │ │ - } │ │ │ │ - data.appendChild(value); │ │ │ │ - extendedData.appendChild(data); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.isSimpleContent(extendedData)) { │ │ │ │ - return null; │ │ │ │ - } else { │ │ │ │ - return extendedData; │ │ │ │ + │ │ │ │ + enableZoomWheel: function() { │ │ │ │ + this.zoomWheelEnabled = true; │ │ │ │ + if (this.active) { │ │ │ │ + this.handlers.wheel.activate(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.KML" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Navigation" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/WMSDescribeLayer.js │ │ │ │ + OpenLayers/Control/DrawFeature.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.WMSDescribeLayer │ │ │ │ - * Read SLD WMS DescribeLayer response │ │ │ │ - * DescribeLayer is meant to couple WMS to WFS and WCS │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Control.DrawFeature │ │ │ │ + * The DrawFeature control draws point, line or polygon features on a vector │ │ │ │ + * layer when active. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.WMSDescribeLayer = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ +OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ - * {String} Version number to assume if none found. Default is "1.1.1". │ │ │ │ + * Property: layer │ │ │ │ + * {<OpenLayers.Layer.Vector>} │ │ │ │ */ │ │ │ │ - defaultVersion: "1.1.1", │ │ │ │ + layer: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.WMSDescribeLayer │ │ │ │ - * Create a new parser for WMS DescribeLayer responses. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * Property: callbacks │ │ │ │ + * {Object} The functions that are sent to the handler for callback │ │ │ │ */ │ │ │ │ + callbacks: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read DescribeLayer data from a string, and return the response. │ │ │ │ - * The OGC currently defines 2 formats which are allowed for output, │ │ │ │ - * so we need to parse these 2 types │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Array} Array of {<LayerDescription>} objects which have: │ │ │ │ - * - {String} owsType: WFS/WCS │ │ │ │ - * - {String} owsURL: the online resource │ │ │ │ - * - {String} typeName: the name of the typename on the service │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * featureadded - Triggered when a feature is added │ │ │ │ */ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMSDescribeLayer" │ │ │ │ - │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/XLS.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + /** │ │ │ │ + * APIProperty: multi │ │ │ │ + * {Boolean} Cast features to multi-part geometries before passing to the │ │ │ │ + * layer. Default is false. │ │ │ │ + */ │ │ │ │ + multi: false, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * APIProperty: featureAdded │ │ │ │ + * {Function} Called after each feature is added │ │ │ │ + */ │ │ │ │ + featureAdded: function() {}, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.XLS │ │ │ │ - * Read/Write XLS (OpenLS). Create a new instance with the <OpenLayers.Format.XLS> │ │ │ │ - * constructor. Currently only implemented for Location Utility Services, more │ │ │ │ - * specifically only for Geocoding. No support for Reverse Geocoding as yet. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.XLS = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + /** │ │ │ │ + * APIProperty: handlerOptions │ │ │ │ + * {Object} Used to set non-default properties on the control's handler │ │ │ │ + */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ - * {String} Version number to assume if none found. Default is "1.1.0". │ │ │ │ + * Constructor: OpenLayers.Control.DrawFeature │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} │ │ │ │ + * handler - {<OpenLayers.Handler>} │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - defaultVersion: "1.1.0", │ │ │ │ + initialize: function(layer, handler, options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + this.callbacks = OpenLayers.Util.extend({ │ │ │ │ + done: this.drawFeature, │ │ │ │ + modify: function(vertex, feature) { │ │ │ │ + this.layer.events.triggerEvent( │ │ │ │ + "sketchmodified", { │ │ │ │ + vertex: vertex, │ │ │ │ + feature: feature │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + create: function(vertex, feature) { │ │ │ │ + this.layer.events.triggerEvent( │ │ │ │ + "sketchstarted", { │ │ │ │ + vertex: vertex, │ │ │ │ + feature: feature │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + this.callbacks │ │ │ │ + ); │ │ │ │ + this.layer = layer; │ │ │ │ + this.handlerOptions = this.handlerOptions || {}; │ │ │ │ + this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( │ │ │ │ + this.handlerOptions.layerOptions, { │ │ │ │ + renderers: layer.renderers, │ │ │ │ + rendererOptions: layer.rendererOptions │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + if (!("multi" in this.handlerOptions)) { │ │ │ │ + this.handlerOptions.multi = this.multi; │ │ │ │ + } │ │ │ │ + var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary; │ │ │ │ + if (sketchStyle) { │ │ │ │ + this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( │ │ │ │ + this.handlerOptions.layerOptions, { │ │ │ │ + styleMap: new OpenLayers.StyleMap({ │ │ │ │ + "default": sketchStyle │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + this.handler = new handler(this, this.callbacks, this.handlerOptions); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: stringifyOutput │ │ │ │ - * {Boolean} If true, write will return a string otherwise a DOMElement. │ │ │ │ - * Default is true. │ │ │ │ + * Method: drawFeature │ │ │ │ */ │ │ │ │ - stringifyOutput: true, │ │ │ │ + drawFeature: function(geometry) { │ │ │ │ + var feature = new OpenLayers.Feature.Vector(geometry); │ │ │ │ + var proceed = this.layer.events.triggerEvent( │ │ │ │ + "sketchcomplete", { │ │ │ │ + feature: feature │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + if (proceed !== false) { │ │ │ │ + feature.state = OpenLayers.State.INSERT; │ │ │ │ + this.layer.addFeatures([feature]); │ │ │ │ + this.featureAdded(feature); │ │ │ │ + this.events.triggerEvent("featureadded", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.XLS │ │ │ │ - * Create a new parser for XLS. │ │ │ │ + * APIMethod: insertXY │ │ │ │ + * Insert a point in the current sketch given x & y coordinates. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * x - {Number} The x-coordinate of the point. │ │ │ │ + * y - {Number} The y-coordinate of the point. │ │ │ │ */ │ │ │ │ + insertXY: function(x, y) { │ │ │ │ + if (this.handler && this.handler.line) { │ │ │ │ + this.handler.insertXY(x, y); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Write out an XLS request. │ │ │ │ + * APIMethod: insertDeltaXY │ │ │ │ + * Insert a point given offsets from the previously inserted point. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * request - {Object} An object representing the LUS request. │ │ │ │ - * options - {Object} Optional configuration object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} An XLS document string. │ │ │ │ + * dx - {Number} The x-coordinate offset of the point. │ │ │ │ + * dy - {Number} The y-coordinate offset of the point. │ │ │ │ */ │ │ │ │ + insertDeltaXY: function(dx, dy) { │ │ │ │ + if (this.handler && this.handler.line) { │ │ │ │ + this.handler.insertDeltaXY(dx, dy); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read an XLS doc and return an object representing the result. │ │ │ │ + * APIMethod: insertDirectionLength │ │ │ │ + * Insert a point in the current sketch given a direction and a length. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * data - {String | DOMElement} Data to read. │ │ │ │ - * options - {Object} Options for the reader. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object representing the GeocodeResponse. │ │ │ │ + * direction - {Number} Degrees clockwise from the positive x-axis. │ │ │ │ + * length - {Number} Distance from the previously drawn point. │ │ │ │ */ │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.XLS" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/SOSCapabilities.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/XML/VersionedOGC.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.SOSCapabilities │ │ │ │ - * Read SOS Capabilities. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML.VersionedOGC> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.SOSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + insertDirectionLength: function(direction, length) { │ │ │ │ + if (this.handler && this.handler.line) { │ │ │ │ + this.handler.insertDirectionLength(direction, length); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: defaultVersion │ │ │ │ - * {String} Version number to assume if none found. Default is "1.0.0". │ │ │ │ + * APIMethod: insertDeflectionLength │ │ │ │ + * Insert a point in the current sketch given a deflection and a length. │ │ │ │ + * The deflection should be degrees clockwise from the previously │ │ │ │ + * digitized segment. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * deflection - {Number} Degrees clockwise from the previous segment. │ │ │ │ + * length - {Number} Distance from the previously drawn point. │ │ │ │ */ │ │ │ │ - defaultVersion: "1.0.0", │ │ │ │ + insertDeflectionLength: function(deflection, length) { │ │ │ │ + if (this.handler && this.handler.line) { │ │ │ │ + this.handler.insertDeflectionLength(deflection, length); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.SOSCapabilities │ │ │ │ - * Create a new parser for SOS Capabilities. │ │ │ │ + * APIMethod: undo │ │ │ │ + * Remove the most recently added point in the current sketch geometry. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} An edit was undone. │ │ │ │ */ │ │ │ │ + undo: function() { │ │ │ │ + return this.handler.undo && this.handler.undo(); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read capabilities data from a string, and return information about │ │ │ │ - * the service (offering and observedProperty mostly). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * APIMethod: redo │ │ │ │ + * Reinsert the most recently removed point resulting from an <undo> call. │ │ │ │ + * The undo stack is deleted whenever a point is added by other means. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Info about the SOS │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} An edit was redone. │ │ │ │ + */ │ │ │ │ + redo: function() { │ │ │ │ + return this.handler.redo && this.handler.redo(); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: finishSketch │ │ │ │ + * Finishes the sketch without including the currently drawn point. │ │ │ │ + * This method can be called to terminate drawing programmatically │ │ │ │ + * instead of waiting for the user to end the sketch. │ │ │ │ */ │ │ │ │ + finishSketch: function() { │ │ │ │ + this.handler.finishGeometry(); │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.SOSCapabilities" │ │ │ │ + /** │ │ │ │ + * APIMethod: cancel │ │ │ │ + * Cancel the current sketch. This removes the current sketch and keeps │ │ │ │ + * the drawing control active. │ │ │ │ + */ │ │ │ │ + cancel: function() { │ │ │ │ + this.handler.cancel(); │ │ │ │ + }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Control.DrawFeature" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/WFST/v1_0_0.js │ │ │ │ + OpenLayers/Control/EditingToolbar.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/WFST/v1.js │ │ │ │ - * @requires OpenLayers/Format/Filter/v1_0_0.js │ │ │ │ + * @requires OpenLayers/Control/Panel.js │ │ │ │ + * @requires OpenLayers/Control/Navigation.js │ │ │ │ + * @requires OpenLayers/Control/DrawFeature.js │ │ │ │ + * @requires OpenLayers/Handler/Point.js │ │ │ │ + * @requires OpenLayers/Handler/Path.js │ │ │ │ + * @requires OpenLayers/Handler/Polygon.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.WFST.v1_0_0 │ │ │ │ - * A format for creating WFS v1.0.0 transactions. Create a new instance with the │ │ │ │ - * <OpenLayers.Format.WFST.v1_0_0> constructor. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Control.EditingToolbar │ │ │ │ + * The EditingToolbar is a panel of 4 controls to draw polygons, lines, │ │ │ │ + * points, or to navigate the map by panning. By default it appears in the │ │ │ │ + * upper right corner of the map. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.Filter.v1_0_0> │ │ │ │ - * - <OpenLayers.Format.WFST.v1> │ │ │ │ + * - <OpenLayers.Control.Panel> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: version │ │ │ │ - * {String} WFS version number. │ │ │ │ - */ │ │ │ │ - version: "1.0.0", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: srsNameInQuery │ │ │ │ - * {Boolean} If true the reference system is passed in Query requests │ │ │ │ - * via the "srsName" attribute to the "wfs:Query" element, this │ │ │ │ - * property defaults to false as it isn't WFS 1.0.0 compliant. │ │ │ │ - */ │ │ │ │ - srsNameInQuery: false, │ │ │ │ +OpenLayers.Control.EditingToolbar = OpenLayers.Class( │ │ │ │ + OpenLayers.Control.Panel, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: schemaLocations │ │ │ │ - * {Object} Properties are namespace aliases, values are schema locations. │ │ │ │ + * APIProperty: citeCompliant │ │ │ │ + * {Boolean} If set to true, coordinates of features drawn in a map extent │ │ │ │ + * crossing the date line won't exceed the world bounds. Default is false. │ │ │ │ */ │ │ │ │ - schemaLocations: { │ │ │ │ - "wfs": "http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd" │ │ │ │ - }, │ │ │ │ + citeCompliant: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.WFST.v1_0_0 │ │ │ │ - * A class for parsing and generating WFS v1.0.0 transactions. │ │ │ │ + * Constructor: OpenLayers.Control.EditingToolbar │ │ │ │ + * Create an editing toolbar for a given layer. │ │ │ │ * │ │ │ │ * 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'. │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]); │ │ │ │ - OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]); │ │ │ │ + initialize: function(layer, options) { │ │ │ │ + OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); │ │ │ │ + │ │ │ │ + this.addControls( │ │ │ │ + [new OpenLayers.Control.Navigation()] │ │ │ │ + ); │ │ │ │ + var controls = [ │ │ │ │ + new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, { │ │ │ │ + displayClass: 'olControlDrawFeaturePoint', │ │ │ │ + handlerOptions: { │ │ │ │ + citeCompliant: this.citeCompliant │ │ │ │ + } │ │ │ │ + }), │ │ │ │ + new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, { │ │ │ │ + displayClass: 'olControlDrawFeaturePath', │ │ │ │ + handlerOptions: { │ │ │ │ + citeCompliant: this.citeCompliant │ │ │ │ + } │ │ │ │ + }), │ │ │ │ + new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, { │ │ │ │ + displayClass: 'olControlDrawFeaturePolygon', │ │ │ │ + handlerOptions: { │ │ │ │ + citeCompliant: this.citeCompliant │ │ │ │ + } │ │ │ │ + }) │ │ │ │ + ]; │ │ │ │ + this.addControls(controls); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: 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. │ │ │ │ + * Method: draw │ │ │ │ + * calls the default draw, and then activates mouse defaults. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} The input object, modified (or a new one if none was provided). │ │ │ │ - */ │ │ │ │ - readNode: function(node, obj, first) { │ │ │ │ - // Not the superclass, only the mixin classes inherit from │ │ │ │ - // Format.GML.v2. We need this because we don't want to get readNode │ │ │ │ - // from the superclass's superclass, which is OpenLayers.Format.XML. │ │ │ │ - return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wfs": OpenLayers.Util.applyDefaults({ │ │ │ │ - "WFS_TransactionResponse": function(node, obj) { │ │ │ │ - obj.insertIds = []; │ │ │ │ - obj.success = false; │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "InsertResult": function(node, container) { │ │ │ │ - var obj = { │ │ │ │ - fids: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - container.insertIds = container.insertIds.concat(obj.fids); │ │ │ │ - }, │ │ │ │ - "TransactionResult": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "Status": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "SUCCESS": function(node, obj) { │ │ │ │ - obj.success = true; │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]), │ │ │ │ - "gml": OpenLayers.Format.GML.v2.prototype.readers["gml"], │ │ │ │ - "feature": OpenLayers.Format.GML.v2.prototype.readers["feature"], │ │ │ │ - "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.readers["ogc"] │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: writers │ │ │ │ - * As a compliment to the readers property, this structure contains public │ │ │ │ - * writing functions grouped by namespace alias and named like the │ │ │ │ - * node names they produce. │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - writers: { │ │ │ │ - "wfs": OpenLayers.Util.applyDefaults({ │ │ │ │ - "Query": function(options) { │ │ │ │ - options = OpenLayers.Util.extend({ │ │ │ │ - featureNS: this.featureNS, │ │ │ │ - featurePrefix: this.featurePrefix, │ │ │ │ - featureType: this.featureType, │ │ │ │ - srsName: this.srsName, │ │ │ │ - srsNameInQuery: this.srsNameInQuery │ │ │ │ - }, options); │ │ │ │ - var prefix = options.featurePrefix; │ │ │ │ - var node = this.createElementNSPlus("wfs:Query", { │ │ │ │ - attributes: { │ │ │ │ - typeName: (prefix ? prefix + ":" : "") + │ │ │ │ - options.featureType │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - if (options.srsNameInQuery && options.srsName) { │ │ │ │ - node.setAttribute("srsName", options.srsName); │ │ │ │ - } │ │ │ │ - if (options.featureNS) { │ │ │ │ - node.setAttribute("xmlns:" + prefix, options.featureNS); │ │ │ │ - } │ │ │ │ - if (options.propertyNames) { │ │ │ │ - for (var i = 0, len = options.propertyNames.length; i < len; i++) { │ │ │ │ - this.writeNode( │ │ │ │ - "ogc:PropertyName", { │ │ │ │ - property: options.propertyNames[i] │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (options.filter) { │ │ │ │ - this.setFilterProperty(options.filter); │ │ │ │ - this.writeNode("ogc:Filter", options.filter, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]), │ │ │ │ - "gml": OpenLayers.Format.GML.v2.prototype.writers["gml"], │ │ │ │ - "feature": OpenLayers.Format.GML.v2.prototype.writers["feature"], │ │ │ │ - "ogc": OpenLayers.Format.Filter.v1_0_0.prototype.writers["ogc"] │ │ │ │ + draw: function() { │ │ │ │ + var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments); │ │ │ │ + if (this.defaultControl === null) { │ │ │ │ + this.defaultControl = this.controls[0]; │ │ │ │ + } │ │ │ │ + return div; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WFST.v1_0_0" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.EditingToolbar" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/ArcXML/Features.js │ │ │ │ + OpenLayers/Control/LayerSwitcher.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/ArcXML.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Lang.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ + * @requires OpenLayers/Events/buttonclick.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.ArcXML.Features │ │ │ │ - * Read/Write ArcXML features. Create a new instance with the │ │ │ │ - * <OpenLayers.Format.ArcXML.Features> constructor. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Control.LayerSwitcher │ │ │ │ + * The LayerSwitcher control displays a table of contents for the map. This │ │ │ │ + * allows the user interface to switch between BaseLasyers and to show or hide │ │ │ │ + * Overlays. By default the switcher is shown minimized on the right edge of │ │ │ │ + * the map, the user may expand it by clicking on the handle. │ │ │ │ + * │ │ │ │ + * To create the LayerSwitcher outside of the map, pass the Id of a html div │ │ │ │ + * as the first argument to the constructor. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.ArcXML.Features = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ +OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: layerStates │ │ │ │ + * {Array(Object)} Basically a copy of the "state" of the map's layers │ │ │ │ + * the last time the control was drawn. We have this in order to avoid │ │ │ │ + * unnecessarily redrawing the control. │ │ │ │ + */ │ │ │ │ + layerStates: null, │ │ │ │ + │ │ │ │ + // DOM Elements │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.ArcXML.Features │ │ │ │ - * Create a new parser/writer for ArcXML Features. Create an instance of this class │ │ │ │ - * to get a set of features from an ArcXML response. │ │ │ │ + * Property: layersDiv │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + layersDiv: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: baseLayersDiv │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + baseLayersDiv: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: baseLayers │ │ │ │ + * {Array(Object)} │ │ │ │ + */ │ │ │ │ + baseLayers: null, │ │ │ │ + │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: dataLbl │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + dataLbl: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: dataLayersDiv │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + dataLayersDiv: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: dataLayers │ │ │ │ + * {Array(Object)} │ │ │ │ + */ │ │ │ │ + dataLayers: null, │ │ │ │ + │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: minimizeDiv │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + minimizeDiv: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: maximizeDiv │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + maximizeDiv: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: ascending │ │ │ │ + * {Boolean} │ │ │ │ + */ │ │ │ │ + ascending: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.LayerSwitcher │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ + this.layerStates = []; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read data from a string of ArcXML, and return a set of OpenLayers features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * APIMethod: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + │ │ │ │ + //clear out layers info and unregister their events │ │ │ │ + this.clearLayersArray("base"); │ │ │ │ + this.clearLayersArray("data"); │ │ │ │ + │ │ │ │ + this.map.events.un({ │ │ │ │ + buttonclick: this.onButtonClick, │ │ │ │ + addlayer: this.redraw, │ │ │ │ + changelayer: this.redraw, │ │ │ │ + removelayer: this.redraw, │ │ │ │ + changebaselayer: this.redraw, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ + │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} A collection of features. │ │ │ │ + * Properties: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - read: function(data) { │ │ │ │ - var axl = new OpenLayers.Format.ArcXML(); │ │ │ │ - var parsed = axl.read(data); │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ │ │ │ │ - return parsed.features.feature; │ │ │ │ - } │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WMSDescribeLayer/v1_1.js │ │ │ │ - ====================================================================== */ │ │ │ │ + this.map.events.on({ │ │ │ │ + addlayer: this.redraw, │ │ │ │ + changelayer: this.redraw, │ │ │ │ + removelayer: this.redraw, │ │ │ │ + changebaselayer: this.redraw, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + if (this.outsideViewport) { │ │ │ │ + this.events.attachToElement(this.div); │ │ │ │ + this.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ + } else { │ │ │ │ + this.map.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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: draw │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A reference to the DIV DOMElement containing the │ │ │ │ + * switcher tabs. │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WMSDescribeLayer.js │ │ │ │ - * @requires OpenLayers/Format/OGCExceptionReport.js │ │ │ │ - */ │ │ │ │ + // create layout divs │ │ │ │ + this.loadContents(); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WMSDescribeLayer.v1_1_1 │ │ │ │ - * Read SLD WMS DescribeLayer response for WMS 1.1.X │ │ │ │ - * WMS 1.1.X is tightly coupled to SLD 1.0.0 │ │ │ │ - * │ │ │ │ - * Example DescribeLayer request: │ │ │ │ - * http://demo.opengeo.org/geoserver/wms?request=DescribeLayer&version=1.1.1&layers=topp:states │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WMSDescribeLayer> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WMSDescribeLayer.v1_1_1 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WMSDescribeLayer, { │ │ │ │ + // set mode to minimize │ │ │ │ + if (!this.outsideViewport) { │ │ │ │ + this.minimizeControl(); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WMSDescribeLayer │ │ │ │ - * Create a new parser for WMS DescribeLayer responses. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.WMSDescribeLayer.prototype.initialize.apply(this, │ │ │ │ - [options]); │ │ │ │ - }, │ │ │ │ + // populate div with current info │ │ │ │ + this.redraw(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read DescribeLayer data from a string, and return the response. │ │ │ │ - * The OGC defines 2 formats which are allowed for output, │ │ │ │ - * so we need to parse these 2 types for version 1.1.X │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Object with a layerDescriptions property, which holds an Array │ │ │ │ - * of {<LayerDescription>} objects which have: │ │ │ │ - * - {String} owsType: WFS/WCS │ │ │ │ - * - {String} owsURL: the online resource │ │ │ │ - * - {String} typeName: the name of the typename on the owsType service │ │ │ │ - * - {String} layerName: the name of the WMS layer we did a lookup for │ │ │ │ - */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + return this.div; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: onButtonClick │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + */ │ │ │ │ + onButtonClick: function(evt) { │ │ │ │ + var button = evt.buttonElement; │ │ │ │ + if (button === this.minimizeDiv) { │ │ │ │ + this.minimizeControl(); │ │ │ │ + } else if (button === this.maximizeDiv) { │ │ │ │ + this.maximizeControl(); │ │ │ │ + } else if (button._layerSwitcher === this.id) { │ │ │ │ + if (button["for"]) { │ │ │ │ + button = document.getElementById(button["for"]); │ │ │ │ } │ │ │ │ - var root = data.documentElement; │ │ │ │ - var children = root.childNodes; │ │ │ │ - var describelayer = { │ │ │ │ - layerDescriptions: [] │ │ │ │ - }; │ │ │ │ - var childNode, nodeName; │ │ │ │ - for (var i = 0; i < children.length; ++i) { │ │ │ │ - childNode = children[i]; │ │ │ │ - nodeName = childNode.nodeName; │ │ │ │ - if (nodeName == 'LayerDescription') { │ │ │ │ - var layerName = childNode.getAttribute('name'); │ │ │ │ - var owsType = ''; │ │ │ │ - var owsURL = ''; │ │ │ │ - var typeName = ''; │ │ │ │ - // check for owsType and owsURL attributes │ │ │ │ - if (childNode.getAttribute('owsType')) { │ │ │ │ - owsType = childNode.getAttribute('owsType'); │ │ │ │ - owsURL = childNode.getAttribute('owsURL'); │ │ │ │ - } else { │ │ │ │ - // look for wfs or wcs attribute │ │ │ │ - if (childNode.getAttribute('wfs') != '') { │ │ │ │ - owsType = 'WFS'; │ │ │ │ - owsURL = childNode.getAttribute('wfs'); │ │ │ │ - } else if (childNode.getAttribute('wcs') != '') { │ │ │ │ - owsType = 'WCS'; │ │ │ │ - owsURL = childNode.getAttribute('wcs'); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - // look for Query child │ │ │ │ - var query = childNode.getElementsByTagName('Query'); │ │ │ │ - if (query.length > 0) { │ │ │ │ - typeName = query[0].getAttribute('typeName'); │ │ │ │ - if (!typeName) { │ │ │ │ - // because of Ionic bug │ │ │ │ - typeName = query[0].getAttribute('typename'); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var layerDescription = { │ │ │ │ - layerName: layerName, │ │ │ │ - owsType: owsType, │ │ │ │ - owsURL: owsURL, │ │ │ │ - typeName: typeName │ │ │ │ - }; │ │ │ │ - describelayer.layerDescriptions.push(layerDescription); │ │ │ │ + if (!button.disabled) { │ │ │ │ + if (button.type == "radio") { │ │ │ │ + button.checked = true; │ │ │ │ + this.map.setBaseLayer(this.map.getLayer(button._layer)); │ │ │ │ + } else { │ │ │ │ + button.checked = !button.checked; │ │ │ │ + this.updateMap(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - //TODO do this in deprecated.js instead: │ │ │ │ - // array style index for backwards compatibility │ │ │ │ - describelayer.length = describelayer.layerDescriptions.length; │ │ │ │ - describelayer[describelayer.length - 1] = layerDescription; │ │ │ │ + /** │ │ │ │ + * Method: clearLayersArray │ │ │ │ + * User specifies either "base" or "data". we then clear all the │ │ │ │ + * corresponding listeners, the div, and reinitialize a new array. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layersType - {String} │ │ │ │ + */ │ │ │ │ + clearLayersArray: function(layersType) { │ │ │ │ + this[layersType + "LayersDiv"].innerHTML = ""; │ │ │ │ + this[layersType + "Layers"] = []; │ │ │ │ + }, │ │ │ │ │ │ │ │ - } else if (nodeName == 'ServiceException') { │ │ │ │ - // an exception must have occurred, so parse it │ │ │ │ - var parser = new OpenLayers.Format.OGCExceptionReport(); │ │ │ │ - return { │ │ │ │ - error: parser.read(data) │ │ │ │ - }; │ │ │ │ - } │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: checkRedraw │ │ │ │ + * Checks if the layer state has changed since the last redraw() call. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The layer state changed since the last redraw() call. │ │ │ │ + */ │ │ │ │ + checkRedraw: function() { │ │ │ │ + if (!this.layerStates.length || │ │ │ │ + (this.map.layers.length != this.layerStates.length)) { │ │ │ │ + return true; │ │ │ │ + } │ │ │ │ + │ │ │ │ + for (var i = 0, len = this.layerStates.length; i < len; i++) { │ │ │ │ + var layerState = this.layerStates[i]; │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + if ((layerState.name != layer.name) || │ │ │ │ + (layerState.inRange != layer.inRange) || │ │ │ │ + (layerState.id != layer.id) || │ │ │ │ + (layerState.visibility != layer.visibility)) { │ │ │ │ + return true; │ │ │ │ } │ │ │ │ - return describelayer; │ │ │ │ - }, │ │ │ │ + } │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMSDescribeLayer.v1_1_1" │ │ │ │ + return false; │ │ │ │ + }, │ │ │ │ │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * Method: redraw │ │ │ │ + * Goes through and takes the current state of the Map and rebuilds the │ │ │ │ + * control to display that state. Groups base layers into a │ │ │ │ + * radio-button group and lists each data layer with a checkbox. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A reference to the DIV DOMElement containing the control │ │ │ │ + */ │ │ │ │ + redraw: function() { │ │ │ │ + //if the state hasn't changed since last redraw, no need │ │ │ │ + // to do anything. Just return the existing div. │ │ │ │ + if (!this.checkRedraw()) { │ │ │ │ + return this.div; │ │ │ │ + } │ │ │ │ │ │ │ │ -// Version alias - workaround for http://trac.osgeo.org/mapserver/ticket/2257 │ │ │ │ -OpenLayers.Format.WMSDescribeLayer.v1_1_0 = │ │ │ │ - OpenLayers.Format.WMSDescribeLayer.v1_1_1; │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WMSCapabilities/v1.js │ │ │ │ - ====================================================================== */ │ │ │ │ + //clear out previous layers │ │ │ │ + this.clearLayersArray("base"); │ │ │ │ + this.clearLayersArray("data"); │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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 containsOverlays = false; │ │ │ │ + var containsBaseLayers = false; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WMSCapabilities.js │ │ │ │ - * @requires OpenLayers/Format/OGCExceptionReport.js │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - */ │ │ │ │ + // Save state -- for checking layer if the map state changed. │ │ │ │ + // We save this before redrawing, because in the process of redrawing │ │ │ │ + // we will trigger more visibility changes, and we want to not redraw │ │ │ │ + // and enter an infinite loop. │ │ │ │ + var len = this.map.layers.length; │ │ │ │ + this.layerStates = new Array(len); │ │ │ │ + for (var i = 0; i < len; i++) { │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + this.layerStates[i] = { │ │ │ │ + 'name': layer.name, │ │ │ │ + 'visibility': layer.visibility, │ │ │ │ + 'inRange': layer.inRange, │ │ │ │ + 'id': layer.id │ │ │ │ + }; │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WMSCapabilities.v1 │ │ │ │ - * Abstract class not to be instantiated directly. Creates │ │ │ │ - * the common parts for both WMS 1.1.X and WMS 1.3.X. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WMSCapabilities.v1 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.XML, { │ │ │ │ + var layers = this.map.layers.slice(); │ │ │ │ + if (!this.ascending) { │ │ │ │ + layers.reverse(); │ │ │ │ + } │ │ │ │ + for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ + var layer = layers[i]; │ │ │ │ + var baseLayer = layer.isBaseLayer; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ - */ │ │ │ │ - namespaces: { │ │ │ │ - wms: "http://www.opengis.net/wms", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ - }, │ │ │ │ + if (layer.displayInLayerSwitcher) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ - */ │ │ │ │ - defaultPrefix: "wms", │ │ │ │ + if (baseLayer) { │ │ │ │ + containsBaseLayers = true; │ │ │ │ + } else { │ │ │ │ + containsOverlays = true; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WMSCapabilities.v1 │ │ │ │ - * Create an instance of one of the subclasses. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + // only check a baselayer if it is *the* baselayer, check data │ │ │ │ + // layers if they are visible │ │ │ │ + var checked = (baseLayer) ? (layer == this.map.baseLayer) : │ │ │ │ + layer.getVisibility(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read capabilities data from a string, and return a list of layers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} List of named layers. │ │ │ │ - */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - } │ │ │ │ - var raw = data; │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ - data = data.documentElement; │ │ │ │ - } │ │ │ │ - var capabilities = {}; │ │ │ │ - this.readNode(data, capabilities); │ │ │ │ - if (capabilities.service === undefined) { │ │ │ │ - // an exception must have occurred, so parse it │ │ │ │ - var parser = new OpenLayers.Format.OGCExceptionReport(); │ │ │ │ - capabilities.error = parser.read(raw); │ │ │ │ - } │ │ │ │ - return capabilities; │ │ │ │ - }, │ │ │ │ + // create input element │ │ │ │ + var inputElem = document.createElement("input"), │ │ │ │ + // The input shall have an id attribute so we can use │ │ │ │ + // labels to interact with them. │ │ │ │ + inputId = OpenLayers.Util.createUniqueID( │ │ │ │ + this.id + "_input_" │ │ │ │ + ); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wms": { │ │ │ │ - "Service": function(node, obj) { │ │ │ │ - obj.service = {}; │ │ │ │ - this.readChildNodes(node, obj.service); │ │ │ │ - }, │ │ │ │ - "Name": function(node, obj) { │ │ │ │ - obj.name = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Title": function(node, obj) { │ │ │ │ - obj.title = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Abstract": function(node, obj) { │ │ │ │ - obj["abstract"] = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "BoundingBox": function(node, obj) { │ │ │ │ - var bbox = {}; │ │ │ │ - bbox.bbox = [ │ │ │ │ - parseFloat(node.getAttribute("minx")), │ │ │ │ - parseFloat(node.getAttribute("miny")), │ │ │ │ - parseFloat(node.getAttribute("maxx")), │ │ │ │ - parseFloat(node.getAttribute("maxy")) │ │ │ │ - ]; │ │ │ │ - var res = { │ │ │ │ - x: parseFloat(node.getAttribute("resx")), │ │ │ │ - y: parseFloat(node.getAttribute("resy")) │ │ │ │ - }; │ │ │ │ + inputElem.id = inputId; │ │ │ │ + inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name; │ │ │ │ + inputElem.type = (baseLayer) ? "radio" : "checkbox"; │ │ │ │ + inputElem.value = layer.name; │ │ │ │ + inputElem.checked = checked; │ │ │ │ + inputElem.defaultChecked = checked; │ │ │ │ + inputElem.className = "olButton"; │ │ │ │ + inputElem._layer = layer.id; │ │ │ │ + inputElem._layerSwitcher = this.id; │ │ │ │ │ │ │ │ - if (!(isNaN(res.x) && isNaN(res.y))) { │ │ │ │ - bbox.res = res; │ │ │ │ - } │ │ │ │ - // return the bbox so that descendant classes can set the │ │ │ │ - // CRS and SRS and add it to the obj │ │ │ │ - return bbox; │ │ │ │ - }, │ │ │ │ - "OnlineResource": function(node, obj) { │ │ │ │ - obj.href = this.getAttributeNS(node, this.namespaces.xlink, │ │ │ │ - "href"); │ │ │ │ - }, │ │ │ │ - "ContactInformation": function(node, obj) { │ │ │ │ - obj.contactInformation = {}; │ │ │ │ - this.readChildNodes(node, obj.contactInformation); │ │ │ │ - }, │ │ │ │ - "ContactPersonPrimary": function(node, obj) { │ │ │ │ - obj.personPrimary = {}; │ │ │ │ - this.readChildNodes(node, obj.personPrimary); │ │ │ │ - }, │ │ │ │ - "ContactPerson": function(node, obj) { │ │ │ │ - obj.person = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "ContactOrganization": function(node, obj) { │ │ │ │ - obj.organization = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "ContactPosition": function(node, obj) { │ │ │ │ - obj.position = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "ContactAddress": function(node, obj) { │ │ │ │ - obj.contactAddress = {}; │ │ │ │ - this.readChildNodes(node, obj.contactAddress); │ │ │ │ - }, │ │ │ │ - "AddressType": function(node, obj) { │ │ │ │ - obj.type = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Address": function(node, obj) { │ │ │ │ - obj.address = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "City": function(node, obj) { │ │ │ │ - obj.city = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "StateOrProvince": function(node, obj) { │ │ │ │ - obj.stateOrProvince = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "PostCode": function(node, obj) { │ │ │ │ - obj.postcode = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Country": function(node, obj) { │ │ │ │ - obj.country = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "ContactVoiceTelephone": function(node, obj) { │ │ │ │ - obj.phone = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "ContactFacsimileTelephone": function(node, obj) { │ │ │ │ - obj.fax = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "ContactElectronicMailAddress": function(node, obj) { │ │ │ │ - obj.email = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Fees": function(node, obj) { │ │ │ │ - var fees = this.getChildValue(node); │ │ │ │ - if (fees && fees.toLowerCase() != "none") { │ │ │ │ - obj.fees = fees; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "AccessConstraints": function(node, obj) { │ │ │ │ - var constraints = this.getChildValue(node); │ │ │ │ - if (constraints && constraints.toLowerCase() != "none") { │ │ │ │ - obj.accessConstraints = constraints; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Capability": function(node, obj) { │ │ │ │ - obj.capability = { │ │ │ │ - nestedLayers: [], │ │ │ │ - layers: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj.capability); │ │ │ │ - }, │ │ │ │ - "Request": function(node, obj) { │ │ │ │ - obj.request = {}; │ │ │ │ - this.readChildNodes(node, obj.request); │ │ │ │ - }, │ │ │ │ - "GetCapabilities": function(node, obj) { │ │ │ │ - obj.getcapabilities = { │ │ │ │ - formats: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj.getcapabilities); │ │ │ │ - }, │ │ │ │ - "Format": function(node, obj) { │ │ │ │ - if (OpenLayers.Util.isArray(obj.formats)) { │ │ │ │ - obj.formats.push(this.getChildValue(node)); │ │ │ │ - } else { │ │ │ │ - obj.format = this.getChildValue(node); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "DCPType": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "HTTP": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "Get": function(node, obj) { │ │ │ │ - obj.get = {}; │ │ │ │ - this.readChildNodes(node, obj.get); │ │ │ │ - // backwards compatibility │ │ │ │ - if (!obj.href) { │ │ │ │ - obj.href = obj.get.href; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Post": function(node, obj) { │ │ │ │ - obj.post = {}; │ │ │ │ - this.readChildNodes(node, obj.post); │ │ │ │ - // backwards compatibility │ │ │ │ - if (!obj.href) { │ │ │ │ - obj.href = obj.get.href; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "GetMap": function(node, obj) { │ │ │ │ - obj.getmap = { │ │ │ │ - formats: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj.getmap); │ │ │ │ - }, │ │ │ │ - "GetFeatureInfo": function(node, obj) { │ │ │ │ - obj.getfeatureinfo = { │ │ │ │ - formats: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj.getfeatureinfo); │ │ │ │ - }, │ │ │ │ - "Exception": function(node, obj) { │ │ │ │ - obj.exception = { │ │ │ │ - formats: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj.exception); │ │ │ │ - }, │ │ │ │ - "Layer": function(node, obj) { │ │ │ │ - var parentLayer, capability; │ │ │ │ - if (obj.capability) { │ │ │ │ - capability = obj.capability; │ │ │ │ - parentLayer = obj; │ │ │ │ - } else { │ │ │ │ - capability = obj; │ │ │ │ - } │ │ │ │ - var attrNode = node.getAttributeNode("queryable"); │ │ │ │ - var queryable = (attrNode && attrNode.specified) ? │ │ │ │ - node.getAttribute("queryable") : null; │ │ │ │ - attrNode = node.getAttributeNode("cascaded"); │ │ │ │ - var cascaded = (attrNode && attrNode.specified) ? │ │ │ │ - node.getAttribute("cascaded") : null; │ │ │ │ - attrNode = node.getAttributeNode("opaque"); │ │ │ │ - var opaque = (attrNode && attrNode.specified) ? │ │ │ │ - node.getAttribute('opaque') : null; │ │ │ │ - var noSubsets = node.getAttribute('noSubsets'); │ │ │ │ - var fixedWidth = node.getAttribute('fixedWidth'); │ │ │ │ - var fixedHeight = node.getAttribute('fixedHeight'); │ │ │ │ - var parent = parentLayer || {}, │ │ │ │ - extend = OpenLayers.Util.extend; │ │ │ │ - var layer = { │ │ │ │ - nestedLayers: [], │ │ │ │ - styles: parentLayer ? [].concat(parentLayer.styles) : [], │ │ │ │ - srs: parentLayer ? extend({}, parent.srs) : {}, │ │ │ │ - metadataURLs: [], │ │ │ │ - bbox: parentLayer ? extend({}, parent.bbox) : {}, │ │ │ │ - llbbox: parent.llbbox, │ │ │ │ - dimensions: parentLayer ? extend({}, parent.dimensions) : {}, │ │ │ │ - authorityURLs: parentLayer ? extend({}, parent.authorityURLs) : {}, │ │ │ │ - identifiers: {}, │ │ │ │ - keywords: [], │ │ │ │ - queryable: (queryable && queryable !== "") ? │ │ │ │ - (queryable === "1" || queryable === "true") : (parent.queryable || false), │ │ │ │ - cascaded: (cascaded !== null) ? parseInt(cascaded) : (parent.cascaded || 0), │ │ │ │ - opaque: opaque ? │ │ │ │ - (opaque === "1" || opaque === "true") : (parent.opaque || false), │ │ │ │ - noSubsets: (noSubsets !== null) ? │ │ │ │ - (noSubsets === "1" || noSubsets === "true") : (parent.noSubsets || false), │ │ │ │ - fixedWidth: (fixedWidth != null) ? │ │ │ │ - parseInt(fixedWidth) : (parent.fixedWidth || 0), │ │ │ │ - fixedHeight: (fixedHeight != null) ? │ │ │ │ - parseInt(fixedHeight) : (parent.fixedHeight || 0), │ │ │ │ - minScale: parent.minScale, │ │ │ │ - maxScale: parent.maxScale, │ │ │ │ - attribution: parent.attribution │ │ │ │ - }; │ │ │ │ - obj.nestedLayers.push(layer); │ │ │ │ - layer.capability = capability; │ │ │ │ - this.readChildNodes(node, layer); │ │ │ │ - delete layer.capability; │ │ │ │ - if (layer.name) { │ │ │ │ - var parts = layer.name.split(":"), │ │ │ │ - request = capability.request, │ │ │ │ - gfi = request.getfeatureinfo; │ │ │ │ - if (parts.length > 0) { │ │ │ │ - layer.prefix = parts[0]; │ │ │ │ - } │ │ │ │ - capability.layers.push(layer); │ │ │ │ - if (layer.formats === undefined) { │ │ │ │ - layer.formats = request.getmap.formats; │ │ │ │ - } │ │ │ │ - if (layer.infoFormats === undefined && gfi) { │ │ │ │ - layer.infoFormats = gfi.formats; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Attribution": function(node, obj) { │ │ │ │ - obj.attribution = {}; │ │ │ │ - this.readChildNodes(node, obj.attribution); │ │ │ │ - }, │ │ │ │ - "LogoURL": function(node, obj) { │ │ │ │ - obj.logo = { │ │ │ │ - width: node.getAttribute("width"), │ │ │ │ - height: node.getAttribute("height") │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj.logo); │ │ │ │ - }, │ │ │ │ - "Style": function(node, obj) { │ │ │ │ - var style = {}; │ │ │ │ - obj.styles.push(style); │ │ │ │ - this.readChildNodes(node, style); │ │ │ │ - }, │ │ │ │ - "LegendURL": function(node, obj) { │ │ │ │ - var legend = { │ │ │ │ - width: node.getAttribute("width"), │ │ │ │ - height: node.getAttribute("height") │ │ │ │ - }; │ │ │ │ - obj.legend = legend; │ │ │ │ - this.readChildNodes(node, legend); │ │ │ │ - }, │ │ │ │ - "MetadataURL": function(node, obj) { │ │ │ │ - var metadataURL = { │ │ │ │ - type: node.getAttribute("type") │ │ │ │ - }; │ │ │ │ - obj.metadataURLs.push(metadataURL); │ │ │ │ - this.readChildNodes(node, metadataURL); │ │ │ │ - }, │ │ │ │ - "DataURL": function(node, obj) { │ │ │ │ - obj.dataURL = {}; │ │ │ │ - this.readChildNodes(node, obj.dataURL); │ │ │ │ - }, │ │ │ │ - "FeatureListURL": function(node, obj) { │ │ │ │ - obj.featureListURL = {}; │ │ │ │ - this.readChildNodes(node, obj.featureListURL); │ │ │ │ - }, │ │ │ │ - "AuthorityURL": function(node, obj) { │ │ │ │ - var name = node.getAttribute("name"); │ │ │ │ - var authority = {}; │ │ │ │ - this.readChildNodes(node, authority); │ │ │ │ - obj.authorityURLs[name] = authority.href; │ │ │ │ - }, │ │ │ │ - "Identifier": function(node, obj) { │ │ │ │ - var authority = node.getAttribute("authority"); │ │ │ │ - obj.identifiers[authority] = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "KeywordList": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "SRS": function(node, obj) { │ │ │ │ - obj.srs[this.getChildValue(node)] = true; │ │ │ │ + if (!baseLayer && !layer.inRange) { │ │ │ │ + inputElem.disabled = true; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // create span │ │ │ │ + var labelSpan = document.createElement("label"); │ │ │ │ + // this isn't the DOM attribute 'for', but an arbitrary name we │ │ │ │ + // use to find the appropriate input element in <onButtonClick> │ │ │ │ + labelSpan["for"] = inputElem.id; │ │ │ │ + OpenLayers.Element.addClass(labelSpan, "labelSpan olButton"); │ │ │ │ + labelSpan._layer = layer.id; │ │ │ │ + labelSpan._layerSwitcher = this.id; │ │ │ │ + if (!baseLayer && !layer.inRange) { │ │ │ │ + labelSpan.style.color = "gray"; │ │ │ │ } │ │ │ │ + labelSpan.innerHTML = layer.name; │ │ │ │ + labelSpan.style.verticalAlign = (baseLayer) ? "bottom" : │ │ │ │ + "baseline"; │ │ │ │ + // create line break │ │ │ │ + var br = document.createElement("br"); │ │ │ │ + │ │ │ │ + │ │ │ │ + var groupArray = (baseLayer) ? this.baseLayers : │ │ │ │ + this.dataLayers; │ │ │ │ + groupArray.push({ │ │ │ │ + 'layer': layer, │ │ │ │ + 'inputElem': inputElem, │ │ │ │ + 'labelSpan': labelSpan │ │ │ │ + }); │ │ │ │ + │ │ │ │ + │ │ │ │ + var groupDiv = (baseLayer) ? this.baseLayersDiv : │ │ │ │ + this.dataLayersDiv; │ │ │ │ + groupDiv.appendChild(inputElem); │ │ │ │ + groupDiv.appendChild(labelSpan); │ │ │ │ + groupDiv.appendChild(br); │ │ │ │ } │ │ │ │ - }, │ │ │ │ + } │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1" │ │ │ │ + // if no overlays, dont display the overlay label │ │ │ │ + this.dataLbl.style.display = (containsOverlays) ? "" : "none"; │ │ │ │ │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WMSCapabilities/v1_1.js │ │ │ │ - ====================================================================== */ │ │ │ │ + // if no baselayers, dont display the baselayer label │ │ │ │ + this.baseLbl.style.display = (containsBaseLayers) ? "" : "none"; │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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 this.div; │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WMSCapabilities/v1.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Method: updateMap │ │ │ │ + * Cycles through the loaded data and base layer input arrays and makes │ │ │ │ + * the necessary calls to the Map object such that that the map's │ │ │ │ + * visual state corresponds to what the user has selected in │ │ │ │ + * the control. │ │ │ │ + */ │ │ │ │ + updateMap: function() { │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WMSCapabilities.v1_1 │ │ │ │ - * Abstract class not to be instantiated directly. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WMSCapabilities.v1> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WMSCapabilities.v1_1 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WMSCapabilities.v1, { │ │ │ │ + // set the newly selected base layer │ │ │ │ + for (var i = 0, len = this.baseLayers.length; i < len; i++) { │ │ │ │ + var layerEntry = this.baseLayers[i]; │ │ │ │ + if (layerEntry.inputElem.checked) { │ │ │ │ + this.map.setBaseLayer(layerEntry.layer, false); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wms": OpenLayers.Util.applyDefaults({ │ │ │ │ - "WMT_MS_Capabilities": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "Keyword": function(node, obj) { │ │ │ │ - if (obj.keywords) { │ │ │ │ - obj.keywords.push(this.getChildValue(node)); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "DescribeLayer": function(node, obj) { │ │ │ │ - obj.describelayer = { │ │ │ │ - formats: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj.describelayer); │ │ │ │ - }, │ │ │ │ - "GetLegendGraphic": function(node, obj) { │ │ │ │ - obj.getlegendgraphic = { │ │ │ │ - formats: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj.getlegendgraphic); │ │ │ │ - }, │ │ │ │ - "GetStyles": function(node, obj) { │ │ │ │ - obj.getstyles = { │ │ │ │ - formats: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj.getstyles); │ │ │ │ - }, │ │ │ │ - "PutStyles": function(node, obj) { │ │ │ │ - obj.putstyles = { │ │ │ │ - formats: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj.putstyles); │ │ │ │ - }, │ │ │ │ - "UserDefinedSymbolization": function(node, obj) { │ │ │ │ - var userSymbols = { │ │ │ │ - supportSLD: parseInt(node.getAttribute("SupportSLD")) == 1, │ │ │ │ - userLayer: parseInt(node.getAttribute("UserLayer")) == 1, │ │ │ │ - userStyle: parseInt(node.getAttribute("UserStyle")) == 1, │ │ │ │ - remoteWFS: parseInt(node.getAttribute("RemoteWFS")) == 1 │ │ │ │ - }; │ │ │ │ - obj.userSymbols = userSymbols; │ │ │ │ - }, │ │ │ │ - "LatLonBoundingBox": function(node, obj) { │ │ │ │ - obj.llbbox = [ │ │ │ │ - parseFloat(node.getAttribute("minx")), │ │ │ │ - parseFloat(node.getAttribute("miny")), │ │ │ │ - parseFloat(node.getAttribute("maxx")), │ │ │ │ - parseFloat(node.getAttribute("maxy")) │ │ │ │ - ]; │ │ │ │ - }, │ │ │ │ - "BoundingBox": function(node, obj) { │ │ │ │ - var bbox = OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"].BoundingBox.apply(this, [node, obj]); │ │ │ │ - bbox.srs = node.getAttribute("SRS"); │ │ │ │ - obj.bbox[bbox.srs] = bbox; │ │ │ │ - }, │ │ │ │ - "ScaleHint": function(node, obj) { │ │ │ │ - var min = node.getAttribute("min"); │ │ │ │ - var max = node.getAttribute("max"); │ │ │ │ - var rad2 = Math.pow(2, 0.5); │ │ │ │ - var ipm = OpenLayers.INCHES_PER_UNIT["m"]; │ │ │ │ - if (min != 0) { │ │ │ │ - obj.maxScale = parseFloat( │ │ │ │ - ((min / rad2) * ipm * │ │ │ │ - OpenLayers.DOTS_PER_INCH).toPrecision(13) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (max != Number.POSITIVE_INFINITY) { │ │ │ │ - obj.minScale = parseFloat( │ │ │ │ - ((max / rad2) * ipm * │ │ │ │ - OpenLayers.DOTS_PER_INCH).toPrecision(13) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Dimension": function(node, obj) { │ │ │ │ - var name = node.getAttribute("name").toLowerCase(); │ │ │ │ - var dim = { │ │ │ │ - name: name, │ │ │ │ - units: node.getAttribute("units"), │ │ │ │ - unitsymbol: node.getAttribute("unitSymbol") │ │ │ │ - }; │ │ │ │ - obj.dimensions[dim.name] = dim; │ │ │ │ - }, │ │ │ │ - "Extent": function(node, obj) { │ │ │ │ - var name = node.getAttribute("name").toLowerCase(); │ │ │ │ - if (name in obj["dimensions"]) { │ │ │ │ - var extent = obj.dimensions[name]; │ │ │ │ - extent.nearestVal = │ │ │ │ - node.getAttribute("nearestValue") === "1"; │ │ │ │ - extent.multipleVal = │ │ │ │ - node.getAttribute("multipleValues") === "1"; │ │ │ │ - extent.current = node.getAttribute("current") === "1"; │ │ │ │ - extent["default"] = node.getAttribute("default") || ""; │ │ │ │ - var values = this.getChildValue(node); │ │ │ │ - extent.values = values.split(","); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"]) │ │ │ │ - }, │ │ │ │ + // set the correct visibilities for the overlays │ │ │ │ + for (var i = 0, len = this.dataLayers.length; i < len; i++) { │ │ │ │ + var layerEntry = this.dataLayers[i]; │ │ │ │ + layerEntry.layer.setVisibility(layerEntry.inputElem.checked); │ │ │ │ + } │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1" │ │ │ │ + }, │ │ │ │ │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WMSCapabilities/v1_3.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * Method: maximizeControl │ │ │ │ + * Set up the labels and divs for the control │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * e - {Event} │ │ │ │ + */ │ │ │ │ + maximizeControl: function(e) { │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + // set the div's width and height to empty values, so │ │ │ │ + // the div dimensions can be controlled by CSS │ │ │ │ + this.div.style.width = ""; │ │ │ │ + this.div.style.height = ""; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WMSCapabilities/v1.js │ │ │ │ - */ │ │ │ │ + this.showControls(false); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WMSCapabilities/v1_3 │ │ │ │ - * Abstract base class for WMS Capabilities version 1.3.X. │ │ │ │ - * SLD 1.1.0 adds in the extra operations DescribeLayer and GetLegendGraphic, │ │ │ │ - * see: http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WMSCapabilities.v1> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WMSCapabilities.v1_3 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WMSCapabilities.v1, { │ │ │ │ + if (e != null) { │ │ │ │ + OpenLayers.Event.stop(e); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wms": OpenLayers.Util.applyDefaults({ │ │ │ │ - "WMS_Capabilities": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "LayerLimit": function(node, obj) { │ │ │ │ - obj.layerLimit = parseInt(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "MaxWidth": function(node, obj) { │ │ │ │ - obj.maxWidth = parseInt(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "MaxHeight": function(node, obj) { │ │ │ │ - obj.maxHeight = parseInt(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "BoundingBox": function(node, obj) { │ │ │ │ - var bbox = OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"].BoundingBox.apply(this, [node, obj]); │ │ │ │ - bbox.srs = node.getAttribute("CRS"); │ │ │ │ - obj.bbox[bbox.srs] = bbox; │ │ │ │ - }, │ │ │ │ - "CRS": function(node, obj) { │ │ │ │ - // CRS is the synonym of SRS │ │ │ │ - this.readers.wms.SRS.apply(this, [node, obj]); │ │ │ │ - }, │ │ │ │ - "EX_GeographicBoundingBox": function(node, obj) { │ │ │ │ - // replacement of LatLonBoundingBox │ │ │ │ - obj.llbbox = []; │ │ │ │ - this.readChildNodes(node, obj.llbbox); │ │ │ │ + /** │ │ │ │ + * Method: minimizeControl │ │ │ │ + * Hide all the contents of the control, shrink the size, │ │ │ │ + * add the maximize icon │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * e - {Event} │ │ │ │ + */ │ │ │ │ + minimizeControl: function(e) { │ │ │ │ │ │ │ │ - }, │ │ │ │ - "westBoundLongitude": function(node, obj) { │ │ │ │ - obj[0] = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "eastBoundLongitude": function(node, obj) { │ │ │ │ - obj[2] = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "southBoundLatitude": function(node, obj) { │ │ │ │ - obj[1] = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "northBoundLatitude": function(node, obj) { │ │ │ │ - obj[3] = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "MinScaleDenominator": function(node, obj) { │ │ │ │ - obj.maxScale = parseFloat(this.getChildValue(node)).toPrecision(16); │ │ │ │ - }, │ │ │ │ - "MaxScaleDenominator": function(node, obj) { │ │ │ │ - obj.minScale = parseFloat(this.getChildValue(node)).toPrecision(16); │ │ │ │ - }, │ │ │ │ - "Dimension": function(node, obj) { │ │ │ │ - // dimension has extra attributes: default, multipleValues, │ │ │ │ - // nearestValue, current which used to be part of Extent. It now │ │ │ │ - // also contains the values. │ │ │ │ - var name = node.getAttribute("name").toLowerCase(); │ │ │ │ - var dim = { │ │ │ │ - name: name, │ │ │ │ - units: node.getAttribute("units"), │ │ │ │ - unitsymbol: node.getAttribute("unitSymbol"), │ │ │ │ - nearestVal: node.getAttribute("nearestValue") === "1", │ │ │ │ - multipleVal: node.getAttribute("multipleValues") === "1", │ │ │ │ - "default": node.getAttribute("default") || "", │ │ │ │ - current: node.getAttribute("current") === "1", │ │ │ │ - values: this.getChildValue(node).split(",") │ │ │ │ + // to minimize the control we set its div's width │ │ │ │ + // and height to 0px, we cannot just set "display" │ │ │ │ + // to "none" because it would hide the maximize │ │ │ │ + // div │ │ │ │ + this.div.style.width = "0px"; │ │ │ │ + this.div.style.height = "0px"; │ │ │ │ │ │ │ │ - }; │ │ │ │ - // Theoretically there can be more dimensions with the same │ │ │ │ - // name, but with a different unit. Until we meet such a case, │ │ │ │ - // let's just keep the same structure as the WMS 1.1 │ │ │ │ - // GetCapabilities parser uses. We will store the last │ │ │ │ - // one encountered. │ │ │ │ - obj.dimensions[dim.name] = dim; │ │ │ │ - }, │ │ │ │ - "Keyword": function(node, obj) { │ │ │ │ - // TODO: should we change the structure of keyword in v1.js? │ │ │ │ - // Make it an object with a value instead of a string? │ │ │ │ - var keyword = { │ │ │ │ - value: this.getChildValue(node), │ │ │ │ - vocabulary: node.getAttribute("vocabulary") │ │ │ │ - }; │ │ │ │ - if (obj.keywords) { │ │ │ │ - obj.keywords.push(keyword); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.WMSCapabilities.v1.prototype.readers["wms"]), │ │ │ │ - "sld": { │ │ │ │ - "UserDefinedSymbolization": function(node, obj) { │ │ │ │ - this.readers.wms.UserDefinedSymbolization.apply(this, [node, obj]); │ │ │ │ - // add the two extra attributes │ │ │ │ - obj.userSymbols.inlineFeature = parseInt(node.getAttribute("InlineFeature")) == 1; │ │ │ │ - obj.userSymbols.remoteWCS = parseInt(node.getAttribute("RemoteWCS")) == 1; │ │ │ │ - }, │ │ │ │ - "DescribeLayer": function(node, obj) { │ │ │ │ - this.readers.wms.DescribeLayer.apply(this, [node, obj]); │ │ │ │ - }, │ │ │ │ - "GetLegendGraphic": function(node, obj) { │ │ │ │ - this.readers.wms.GetLegendGraphic.apply(this, [node, obj]); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + this.showControls(true); │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_3" │ │ │ │ + if (e != null) { │ │ │ │ + OpenLayers.Event.stop(e); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WMSCapabilities/v1_3_0.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * Method: showControls │ │ │ │ + * Hide/Show all LayerSwitcher controls depending on whether we are │ │ │ │ + * minimized or not │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * minimize - {Boolean} │ │ │ │ + */ │ │ │ │ + showControls: function(minimize) { │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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.maximizeDiv.style.display = minimize ? "" : "none"; │ │ │ │ + this.minimizeDiv.style.display = minimize ? "none" : ""; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WMSCapabilities/v1_3.js │ │ │ │ - */ │ │ │ │ + this.layersDiv.style.display = minimize ? "none" : ""; │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WMSCapabilities/v1_3_0 │ │ │ │ - * Read WMS Capabilities version 1.3.0. │ │ │ │ - * SLD 1.1.0 adds in the extra operations DescribeLayer and GetLegendGraphic, │ │ │ │ - * see: http://schemas.opengis.net/sld/1.1.0/sld_capabilities.xsd │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WMSCapabilities.v1_3> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WMSCapabilities.v1_3_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WMSCapabilities.v1_3, { │ │ │ │ + /** │ │ │ │ + * Method: loadContents │ │ │ │ + * Set up the labels and divs for the control │ │ │ │ + */ │ │ │ │ + loadContents: function() { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: version │ │ │ │ - * {String} The specific parser version. │ │ │ │ - */ │ │ │ │ - version: "1.3.0", │ │ │ │ + // layers list div │ │ │ │ + this.layersDiv = document.createElement("div"); │ │ │ │ + this.layersDiv.id = this.id + "_layersDiv"; │ │ │ │ + OpenLayers.Element.addClass(this.layersDiv, "layersDiv"); │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_3_0" │ │ │ │ + this.baseLbl = document.createElement("div"); │ │ │ │ + this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer"); │ │ │ │ + OpenLayers.Element.addClass(this.baseLbl, "baseLbl"); │ │ │ │ │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WMSCapabilities/v1_1_0.js │ │ │ │ - ====================================================================== */ │ │ │ │ + this.baseLayersDiv = document.createElement("div"); │ │ │ │ + OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv"); │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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.dataLbl = document.createElement("div"); │ │ │ │ + this.dataLbl.innerHTML = OpenLayers.i18n("Overlays"); │ │ │ │ + OpenLayers.Element.addClass(this.dataLbl, "dataLbl"); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WMSCapabilities/v1_1.js │ │ │ │ - */ │ │ │ │ + this.dataLayersDiv = document.createElement("div"); │ │ │ │ + OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv"); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WMSCapabilities/v1_1_0 │ │ │ │ - * Read WMS Capabilities version 1.1.0. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WMSCapabilities.v1_1> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WMSCapabilities.v1_1_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WMSCapabilities.v1_1, { │ │ │ │ + if (this.ascending) { │ │ │ │ + this.layersDiv.appendChild(this.baseLbl); │ │ │ │ + this.layersDiv.appendChild(this.baseLayersDiv); │ │ │ │ + this.layersDiv.appendChild(this.dataLbl); │ │ │ │ + this.layersDiv.appendChild(this.dataLayersDiv); │ │ │ │ + } else { │ │ │ │ + this.layersDiv.appendChild(this.dataLbl); │ │ │ │ + this.layersDiv.appendChild(this.dataLayersDiv); │ │ │ │ + this.layersDiv.appendChild(this.baseLbl); │ │ │ │ + this.layersDiv.appendChild(this.baseLayersDiv); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: version │ │ │ │ - * {String} The specific parser version. │ │ │ │ - */ │ │ │ │ - version: "1.1.0", │ │ │ │ + this.div.appendChild(this.layersDiv); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_0 │ │ │ │ - * Create a new parser for WMS capabilities version 1.1.0. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + // maximize button div │ │ │ │ + var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png'); │ │ │ │ + this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ + "OpenLayers_Control_MaximizeDiv", │ │ │ │ + null, │ │ │ │ + null, │ │ │ │ + img, │ │ │ │ + "absolute"); │ │ │ │ + OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton"); │ │ │ │ + this.maximizeDiv.style.display = "none"; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wms": OpenLayers.Util.applyDefaults({ │ │ │ │ - "SRS": function(node, obj) { │ │ │ │ - var srs = this.getChildValue(node); │ │ │ │ - var values = srs.split(/ +/); │ │ │ │ - for (var i = 0, len = values.length; i < len; i++) { │ │ │ │ - obj.srs[values[i]] = true; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers["wms"]) │ │ │ │ - }, │ │ │ │ + this.div.appendChild(this.maximizeDiv); │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_0" │ │ │ │ + // minimize button div │ │ │ │ + var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png'); │ │ │ │ + this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ + "OpenLayers_Control_MinimizeDiv", │ │ │ │ + null, │ │ │ │ + null, │ │ │ │ + img, │ │ │ │ + "absolute"); │ │ │ │ + OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton"); │ │ │ │ + this.minimizeDiv.style.display = "none"; │ │ │ │ │ │ │ │ - }); │ │ │ │ + this.div.appendChild(this.minimizeDiv); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Control.LayerSwitcher" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/WMSCapabilities/v1_1_1.js │ │ │ │ + OpenLayers/Control/ScaleLine.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/WMSCapabilities/v1_1.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.WMSCapabilities/v1_1_1 │ │ │ │ - * Read WMS Capabilities version 1.1.1. │ │ │ │ - * │ │ │ │ - * Note on <ScaleHint> parsing: If the 'min' attribute is set to "0", no │ │ │ │ - * maxScale will be set on the layer object. If the 'max' attribute is set to │ │ │ │ - * "Infinity", no minScale will be set. This makes it easy to create proper │ │ │ │ - * {<OpenLayers.Layer.WMS>} configurations directly from the layer object │ │ │ │ - * literals returned by this format, because no minScale/maxScale modifications │ │ │ │ - * need to be made. │ │ │ │ + * Class: OpenLayers.Control.ScaleLine │ │ │ │ + * The ScaleLine displays a small line indicator representing the current │ │ │ │ + * map scale on the map. By default it is drawn in the lower left corner of │ │ │ │ + * the map. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WMSCapabilities.v1_1> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + * │ │ │ │ + * Is a very close copy of: │ │ │ │ + * - <OpenLayers.Control.Scale> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.WMSCapabilities.v1_1_1 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WMSCapabilities.v1_1, { │ │ │ │ +OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: version │ │ │ │ - * {String} The specific parser version. │ │ │ │ - */ │ │ │ │ - version: "1.1.1", │ │ │ │ + /** │ │ │ │ + * Property: maxWidth │ │ │ │ + * {Integer} Maximum width of the scale line in pixels. Default is 100. │ │ │ │ + */ │ │ │ │ + maxWidth: 100, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_1 │ │ │ │ - * Create a new parser for WMS capabilities version 1.1.1. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Property: topOutUnits │ │ │ │ + * {String} Units for zoomed out on top bar. Default is km. │ │ │ │ + */ │ │ │ │ + topOutUnits: "km", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wms": OpenLayers.Util.applyDefaults({ │ │ │ │ - "SRS": function(node, obj) { │ │ │ │ - obj.srs[this.getChildValue(node)] = true; │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.WMSCapabilities.v1_1.prototype.readers["wms"]) │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: topInUnits │ │ │ │ + * {String} Units for zoomed in on top bar. Default is m. │ │ │ │ + */ │ │ │ │ + topInUnits: "m", │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_1" │ │ │ │ + /** │ │ │ │ + * Property: bottomOutUnits │ │ │ │ + * {String} Units for zoomed out on bottom bar. Default is mi. │ │ │ │ + */ │ │ │ │ + bottomOutUnits: "mi", │ │ │ │ │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WMSCapabilities/v1_1_1_WMSC.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * Property: bottomInUnits │ │ │ │ + * {String} Units for zoomed in on bottom bar. Default is ft. │ │ │ │ + */ │ │ │ │ + bottomInUnits: "ft", │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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: eTop │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + eTop: null, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WMSCapabilities/v1_1_1.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Property: eBottom │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + eBottom: null, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WMSCapabilities/v1_1_1_WMSC │ │ │ │ - * Read WMS-C Capabilities version 1.1.1. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WMSCapabilities.v1_1_1> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WMSCapabilities.v1_1_1, { │ │ │ │ + /** │ │ │ │ + * APIProperty: geodesic │ │ │ │ + * {Boolean} Use geodesic measurement. Default is false. The recommended │ │ │ │ + * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to │ │ │ │ + * true, the scale will be calculated based on the horizontal size of the │ │ │ │ + * pixel in the center of the map viewport. │ │ │ │ + */ │ │ │ │ + geodesic: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: version │ │ │ │ - * {String} The specific parser version. │ │ │ │ - */ │ │ │ │ - version: "1.1.1", │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.ScaleLine │ │ │ │ + * Create a new scale line control. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be used │ │ │ │ + * to extend the control. │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: profile │ │ │ │ - * {String} The specific profile │ │ │ │ - */ │ │ │ │ - profile: "WMSC", │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ + if (!this.eTop) { │ │ │ │ + // stick in the top bar │ │ │ │ + this.eTop = document.createElement("div"); │ │ │ │ + this.eTop.className = this.displayClass + "Top"; │ │ │ │ + var theLen = this.topInUnits.length; │ │ │ │ + this.div.appendChild(this.eTop); │ │ │ │ + if ((this.topOutUnits == "") || (this.topInUnits == "")) { │ │ │ │ + this.eTop.style.visibility = "hidden"; │ │ │ │ + } else { │ │ │ │ + this.eTop.style.visibility = "visible"; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WMSCapabilities.v1_1_1 │ │ │ │ - * Create a new parser for WMS-C capabilities version 1.1.1. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + // and the bottom bar │ │ │ │ + this.eBottom = document.createElement("div"); │ │ │ │ + this.eBottom.className = this.displayClass + "Bottom"; │ │ │ │ + this.div.appendChild(this.eBottom); │ │ │ │ + if ((this.bottomOutUnits == "") || (this.bottomInUnits == "")) { │ │ │ │ + this.eBottom.style.visibility = "hidden"; │ │ │ │ + } else { │ │ │ │ + this.eBottom.style.visibility = "visible"; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.map.events.register('moveend', this, this.update); │ │ │ │ + this.update(); │ │ │ │ + return this.div; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wms": OpenLayers.Util.applyDefaults({ │ │ │ │ - "VendorSpecificCapabilities": function(node, obj) { │ │ │ │ - obj.vendorSpecific = { │ │ │ │ - tileSets: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj.vendorSpecific); │ │ │ │ - }, │ │ │ │ - "TileSet": function(node, vendorSpecific) { │ │ │ │ - var tileset = { │ │ │ │ - srs: {}, │ │ │ │ - bbox: {}, │ │ │ │ - resolutions: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, tileset); │ │ │ │ - vendorSpecific.tileSets.push(tileset); │ │ │ │ - }, │ │ │ │ - "Resolutions": function(node, tileset) { │ │ │ │ - var res = this.getChildValue(node).split(" "); │ │ │ │ - for (var i = 0, len = res.length; i < len; i++) { │ │ │ │ - if (res[i] != "") { │ │ │ │ - tileset.resolutions.push(parseFloat(res[i])); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Width": function(node, tileset) { │ │ │ │ - tileset.width = parseInt(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "Height": function(node, tileset) { │ │ │ │ - tileset.height = parseInt(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "Layers": function(node, tileset) { │ │ │ │ - tileset.layers = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Styles": function(node, tileset) { │ │ │ │ - tileset.styles = this.getChildValue(node); │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.WMSCapabilities.v1_1_1.prototype.readers["wms"]) │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: getBarLen │ │ │ │ + * Given a number, round it down to the nearest 1,2,5 times a power of 10. │ │ │ │ + * That seems a fairly useful set of number groups to use. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * maxLen - {float} the number we're rounding down from │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} the rounded number (less than or equal to maxLen) │ │ │ │ + */ │ │ │ │ + getBarLen: function(maxLen) { │ │ │ │ + // nearest power of 10 lower than maxLen │ │ │ │ + var digits = parseInt(Math.log(maxLen) / Math.log(10)); │ │ │ │ + var pow10 = Math.pow(10, digits); │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMSCapabilities.v1_1_1_WMSC" │ │ │ │ + // ok, find first character │ │ │ │ + var firstChar = parseInt(maxLen / pow10); │ │ │ │ │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WPSCapabilities/v1_0_0.js │ │ │ │ - ====================================================================== */ │ │ │ │ + // right, put it into the correct bracket │ │ │ │ + var barLen; │ │ │ │ + if (firstChar > 5) { │ │ │ │ + barLen = 5; │ │ │ │ + } else if (firstChar > 2) { │ │ │ │ + barLen = 2; │ │ │ │ + } else { │ │ │ │ + barLen = 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. */ │ │ │ │ + // scale it up the correct power of 10 │ │ │ │ + return barLen * pow10; │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WPSCapabilities.js │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Method: update │ │ │ │ + * Update the size of the bars, and the labels they contain. │ │ │ │ + */ │ │ │ │ + update: function() { │ │ │ │ + var res = this.map.getResolution(); │ │ │ │ + if (!res) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WPSCapabilities.v1_0_0 │ │ │ │ - * Read WPS Capabilities version 1.0.0. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WPSCapabilities.v1_0_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.XML, { │ │ │ │ + var curMapUnits = this.map.getUnits(); │ │ │ │ + var inches = OpenLayers.INCHES_PER_UNIT; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ - */ │ │ │ │ - namespaces: { │ │ │ │ - ows: "http://www.opengis.net/ows/1.1", │ │ │ │ - wps: "http://www.opengis.net/wps/1.0.0", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink" │ │ │ │ - }, │ │ │ │ + // convert maxWidth to map units │ │ │ │ + var maxSizeData = this.maxWidth * res * inches[curMapUnits]; │ │ │ │ + var geodesicRatio = 1; │ │ │ │ + if (this.geodesic === true) { │ │ │ │ + var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w || │ │ │ │ + 0.000001) * this.maxWidth; │ │ │ │ + var maxSizeKilometers = maxSizeData / inches["km"]; │ │ │ │ + geodesicRatio = maxSizeGeodesic / maxSizeKilometers; │ │ │ │ + maxSizeData *= geodesicRatio; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: regExes │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ - */ │ │ │ │ - regExes: { │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ - }, │ │ │ │ + // decide whether to use large or small scale units │ │ │ │ + var topUnits; │ │ │ │ + var bottomUnits; │ │ │ │ + if (maxSizeData > 100000) { │ │ │ │ + topUnits = this.topOutUnits; │ │ │ │ + bottomUnits = this.bottomOutUnits; │ │ │ │ + } else { │ │ │ │ + topUnits = this.topInUnits; │ │ │ │ + bottomUnits = this.bottomInUnits; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WPSCapabilities.v1_0_0 │ │ │ │ - * Create a new parser for WPS capabilities version 1.0.0. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ - }, │ │ │ │ + // and to map units units │ │ │ │ + var topMax = maxSizeData / inches[topUnits]; │ │ │ │ + var bottomMax = maxSizeData / inches[bottomUnits]; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read capabilities data from a string, and return info about the WPS. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Information about the WPS service. │ │ │ │ - */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - } │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ - data = data.documentElement; │ │ │ │ - } │ │ │ │ - var capabilities = {}; │ │ │ │ - this.readNode(data, capabilities); │ │ │ │ - return capabilities; │ │ │ │ - }, │ │ │ │ + // now trim this down to useful block length │ │ │ │ + var topRounded = this.getBarLen(topMax); │ │ │ │ + var bottomRounded = this.getBarLen(bottomMax); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wps": { │ │ │ │ - "Capabilities": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "ProcessOfferings": function(node, obj) { │ │ │ │ - obj.processOfferings = {}; │ │ │ │ - this.readChildNodes(node, obj.processOfferings); │ │ │ │ - }, │ │ │ │ - "Process": function(node, processOfferings) { │ │ │ │ - var processVersion = this.getAttributeNS(node, this.namespaces.wps, "processVersion"); │ │ │ │ - var process = { │ │ │ │ - processVersion: processVersion │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, process); │ │ │ │ - processOfferings[process.identifier] = process; │ │ │ │ - }, │ │ │ │ - "Languages": function(node, obj) { │ │ │ │ - obj.languages = []; │ │ │ │ - this.readChildNodes(node, obj.languages); │ │ │ │ - }, │ │ │ │ - "Default": function(node, languages) { │ │ │ │ - var language = { │ │ │ │ - isDefault: true │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, language); │ │ │ │ - languages.push(language); │ │ │ │ - }, │ │ │ │ - "Supported": function(node, languages) { │ │ │ │ - var language = {}; │ │ │ │ - this.readChildNodes(node, language); │ │ │ │ - languages.push(language); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ - }, │ │ │ │ + // and back to display units │ │ │ │ + topMax = topRounded / inches[curMapUnits] * inches[topUnits]; │ │ │ │ + bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits]; │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WPSCapabilities.v1_0_0" │ │ │ │ + // and to pixel units │ │ │ │ + var topPx = topMax / res / geodesicRatio; │ │ │ │ + var bottomPx = bottomMax / res / geodesicRatio; │ │ │ │ + │ │ │ │ + // now set the pixel widths │ │ │ │ + // and the values inside them │ │ │ │ + │ │ │ │ + if (this.eBottom.style.visibility == "visible") { │ │ │ │ + this.eBottom.style.width = Math.round(bottomPx) + "px"; │ │ │ │ + this.eBottom.innerHTML = bottomRounded + " " + bottomUnits; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.eTop.style.visibility == "visible") { │ │ │ │ + this.eTop.style.width = Math.round(topPx) + "px"; │ │ │ │ + this.eTop.innerHTML = topRounded + " " + topUnits; │ │ │ │ + } │ │ │ │ + │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Control.ScaleLine" │ │ │ │ +}); │ │ │ │ │ │ │ │ - }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/CSWGetRecords/v2_0_2.js │ │ │ │ + OpenLayers/Control/Graticule.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Format/CSWGetRecords.js │ │ │ │ - * @requires OpenLayers/Format/Filter/v1_0_0.js │ │ │ │ - * @requires OpenLayers/Format/Filter/v1_1_0.js │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1_0_0.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Lang.js │ │ │ │ + * @requires OpenLayers/Rule.js │ │ │ │ + * @requires OpenLayers/StyleMap.js │ │ │ │ + * @requires OpenLayers/Layer/Vector.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.CSWGetRecords.v2_0_2 │ │ │ │ - * A format for creating CSWGetRecords v2.0.2 transactions. │ │ │ │ - * Create a new instance with the │ │ │ │ - * <OpenLayers.Format.CSWGetRecords.v2_0_2> constructor. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Control.Graticule │ │ │ │ + * The Graticule displays a grid of latitude/longitude lines reprojected on │ │ │ │ + * the map. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + * │ │ │ │ */ │ │ │ │ -OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ +OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * true. │ │ │ │ */ │ │ │ │ - namespaces: { │ │ │ │ - csw: "http://www.opengis.net/cat/csw/2.0.2", │ │ │ │ - dc: "http://purl.org/dc/elements/1.1/", │ │ │ │ - dct: "http://purl.org/dc/terms/", │ │ │ │ - gmd: "http://www.isotc211.org/2005/gmd", │ │ │ │ - geonet: "http://www.fao.org/geonetwork", │ │ │ │ - ogc: "http://www.opengis.net/ogc", │ │ │ │ - ows: "http://www.opengis.net/ows", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ - }, │ │ │ │ + autoActivate: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ - * {String} The default prefix (used by Format.XML). │ │ │ │ + * APIProperty: intervals │ │ │ │ + * {Array(Float)} A list of possible graticule widths in degrees. │ │ │ │ */ │ │ │ │ - defaultPrefix: "csw", │ │ │ │ + intervals: [45, 30, 20, 10, 5, 2, 1, │ │ │ │ + 0.5, 0.2, 0.1, 0.05, 0.01, │ │ │ │ + 0.005, 0.002, 0.001 │ │ │ │ + ], │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: version │ │ │ │ - * {String} CSW version number. │ │ │ │ + * APIProperty: displayInLayerSwitcher │ │ │ │ + * {Boolean} Allows the Graticule control to be switched on and off by │ │ │ │ + * LayerSwitcher control. Defaults is true. │ │ │ │ */ │ │ │ │ - version: "2.0.2", │ │ │ │ + displayInLayerSwitcher: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} http://www.opengis.net/cat/csw/2.0.2 │ │ │ │ - * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd │ │ │ │ + * APIProperty: visible │ │ │ │ + * {Boolean} should the graticule be initially visible (default=true) │ │ │ │ */ │ │ │ │ - schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd", │ │ │ │ + visible: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: requestId │ │ │ │ - * {String} Value of the requestId attribute of the GetRecords element. │ │ │ │ + * APIProperty: numPoints │ │ │ │ + * {Integer} The number of points to use in each graticule line. Higher │ │ │ │ + * numbers result in a smoother curve for projected maps │ │ │ │ */ │ │ │ │ - requestId: null, │ │ │ │ + numPoints: 50, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: resultType │ │ │ │ - * {String} Value of the resultType attribute of the GetRecords element, │ │ │ │ - * specifies the result type in the GetRecords response, "hits" is │ │ │ │ - * the default. │ │ │ │ + * APIProperty: targetSize │ │ │ │ + * {Integer} The maximum size of the grid in pixels on the map │ │ │ │ */ │ │ │ │ - resultType: null, │ │ │ │ + targetSize: 200, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: outputFormat │ │ │ │ - * {String} Value of the outputFormat attribute of the GetRecords element, │ │ │ │ - * specifies the format of the GetRecords response, │ │ │ │ - * "application/xml" is the default. │ │ │ │ + * APIProperty: layerName │ │ │ │ + * {String} The name to be displayed in the layer switcher, default is set │ │ │ │ + * by {<OpenLayers.Lang>}. │ │ │ │ */ │ │ │ │ - outputFormat: null, │ │ │ │ + layerName: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: outputSchema │ │ │ │ - * {String} Value of the outputSchema attribute of the GetRecords element, │ │ │ │ - * specifies the schema of the GetRecords response. │ │ │ │ + * APIProperty: labelled │ │ │ │ + * {Boolean} Should the graticule lines be labelled?. default=true │ │ │ │ */ │ │ │ │ - outputSchema: null, │ │ │ │ + labelled: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: startPosition │ │ │ │ - * {String} Value of the startPosition attribute of the GetRecords element, │ │ │ │ - * specifies the start position (offset+1) for the GetRecords response, │ │ │ │ - * 1 is the default. │ │ │ │ + * APIProperty: labelFormat │ │ │ │ + * {String} the format of the labels, default = 'dm'. See │ │ │ │ + * <OpenLayers.Util.getFormattedLonLat> for other options. │ │ │ │ */ │ │ │ │ - startPosition: null, │ │ │ │ + labelFormat: 'dm', │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: maxRecords │ │ │ │ - * {String} Value of the maxRecords attribute of the GetRecords element, │ │ │ │ - * specifies the maximum number of records in the GetRecords response, │ │ │ │ - * 10 is the default. │ │ │ │ + * APIProperty: lineSymbolizer │ │ │ │ + * {symbolizer} the symbolizer used to render lines │ │ │ │ */ │ │ │ │ - maxRecords: null, │ │ │ │ + lineSymbolizer: { │ │ │ │ + strokeColor: "#333", │ │ │ │ + strokeWidth: 1, │ │ │ │ + strokeOpacity: 0.5 │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: DistributedSearch │ │ │ │ - * {String} Value of the csw:DistributedSearch element, used when writing │ │ │ │ - * a csw:GetRecords document. │ │ │ │ + * APIProperty: labelSymbolizer │ │ │ │ + * {symbolizer} the symbolizer used to render labels │ │ │ │ */ │ │ │ │ - DistributedSearch: null, │ │ │ │ + labelSymbolizer: {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: ResponseHandler │ │ │ │ - * {Array({String})} Values of the csw:ResponseHandler elements, used when │ │ │ │ - * writting a csw:GetRecords document. │ │ │ │ + * Property: gratLayer │ │ │ │ + * {<OpenLayers.Layer.Vector>} vector layer used to draw the graticule on │ │ │ │ */ │ │ │ │ - ResponseHandler: null, │ │ │ │ + gratLayer: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: Query │ │ │ │ - * {String} Value of the csw:Query element, used when writing a csw:GetRecords │ │ │ │ - * document. │ │ │ │ + * Constructor: OpenLayers.Control.Graticule │ │ │ │ + * Create a new graticule control to display a grid of latitude longitude │ │ │ │ + * lines. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be used │ │ │ │ + * to extend the control. │ │ │ │ */ │ │ │ │ - Query: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + options.layerName = options.layerName || OpenLayers.i18n("Graticule"); │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + │ │ │ │ + this.labelSymbolizer.stroke = false; │ │ │ │ + this.labelSymbolizer.fill = false; │ │ │ │ + this.labelSymbolizer.label = "${label}"; │ │ │ │ + this.labelSymbolizer.labelAlign = "${labelAlign}"; │ │ │ │ + this.labelSymbolizer.labelXOffset = "${xOffset}"; │ │ │ │ + this.labelSymbolizer.labelYOffset = "${yOffset}"; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: regExes │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ + * APIMethod: destroy │ │ │ │ */ │ │ │ │ - regExes: { │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ + destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + if (this.gratLayer) { │ │ │ │ + this.gratLayer.destroy(); │ │ │ │ + this.gratLayer = null; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.CSWGetRecords.v2_0_2 │ │ │ │ - * A class for parsing and generating CSWGetRecords v2.0.2 transactions. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * Method: draw │ │ │ │ * │ │ │ │ - * Valid options properties (documented as class properties): │ │ │ │ - * - requestId │ │ │ │ - * - resultType │ │ │ │ - * - outputFormat │ │ │ │ - * - outputSchema │ │ │ │ - * - startPosition │ │ │ │ - * - maxRecords │ │ │ │ - * - DistributedSearch │ │ │ │ - * - ResponseHandler │ │ │ │ - * - Query │ │ │ │ + * initializes the graticule layer and does the initial update │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ + if (!this.gratLayer) { │ │ │ │ + var gratStyle = new OpenLayers.Style({}, { │ │ │ │ + rules: [new OpenLayers.Rule({ │ │ │ │ + 'symbolizer': { │ │ │ │ + "Point": this.labelSymbolizer, │ │ │ │ + "Line": this.lineSymbolizer │ │ │ │ + } │ │ │ │ + })] │ │ │ │ + }); │ │ │ │ + this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, { │ │ │ │ + styleMap: new OpenLayers.StyleMap({ │ │ │ │ + 'default': gratStyle │ │ │ │ + }), │ │ │ │ + visibility: this.visible, │ │ │ │ + displayInLayerSwitcher: this.displayInLayerSwitcher │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Parse the response from a GetRecords request. │ │ │ │ + * APIMethod: activate │ │ │ │ */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - } │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ - data = data.documentElement; │ │ │ │ + activate: function() { │ │ │ │ + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.map.addLayer(this.gratLayer); │ │ │ │ + this.map.events.register('moveend', this, this.update); │ │ │ │ + this.update(); │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ } │ │ │ │ - var obj = {}; │ │ │ │ - this.readNode(data, obj); │ │ │ │ - return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ + * APIMethod: deactivate │ │ │ │ */ │ │ │ │ - readers: { │ │ │ │ - "csw": { │ │ │ │ - "GetRecordsResponse": function(node, obj) { │ │ │ │ - obj.records = []; │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - var version = this.getAttributeNS(node, "", 'version'); │ │ │ │ - if (version != "") { │ │ │ │ - obj.version = version; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "RequestId": function(node, obj) { │ │ │ │ - obj.RequestId = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "SearchStatus": function(node, obj) { │ │ │ │ - obj.SearchStatus = {}; │ │ │ │ - var timestamp = this.getAttributeNS(node, "", 'timestamp'); │ │ │ │ - if (timestamp != "") { │ │ │ │ - obj.SearchStatus.timestamp = timestamp; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "SearchResults": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - var attrs = node.attributes; │ │ │ │ - var SearchResults = {}; │ │ │ │ - for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ - if ((attrs[i].name == "numberOfRecordsMatched") || │ │ │ │ - (attrs[i].name == "numberOfRecordsReturned") || │ │ │ │ - (attrs[i].name == "nextRecord")) { │ │ │ │ - SearchResults[attrs[i].name] = parseInt(attrs[i].nodeValue); │ │ │ │ - } else { │ │ │ │ - SearchResults[attrs[i].name] = attrs[i].nodeValue; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - obj.SearchResults = SearchResults; │ │ │ │ - }, │ │ │ │ - "SummaryRecord": function(node, obj) { │ │ │ │ - var record = { │ │ │ │ - type: "SummaryRecord" │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, record); │ │ │ │ - obj.records.push(record); │ │ │ │ - }, │ │ │ │ - "BriefRecord": function(node, obj) { │ │ │ │ - var record = { │ │ │ │ - type: "BriefRecord" │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, record); │ │ │ │ - obj.records.push(record); │ │ │ │ - }, │ │ │ │ - "DCMIRecord": function(node, obj) { │ │ │ │ - var record = { │ │ │ │ - type: "DCMIRecord" │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, record); │ │ │ │ - obj.records.push(record); │ │ │ │ - }, │ │ │ │ - "Record": function(node, obj) { │ │ │ │ - var record = { │ │ │ │ - type: "Record" │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, record); │ │ │ │ - obj.records.push(record); │ │ │ │ - }, │ │ │ │ - "*": function(node, obj) { │ │ │ │ - var name = node.localName || node.nodeName.split(":").pop(); │ │ │ │ - obj[name] = this.getChildValue(node); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "geonet": { │ │ │ │ - "info": function(node, obj) { │ │ │ │ - var gninfo = {}; │ │ │ │ - this.readChildNodes(node, gninfo); │ │ │ │ - obj.gninfo = gninfo; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "dc": { │ │ │ │ - // audience, contributor, coverage, creator, date, description, format, │ │ │ │ - // identifier, language, provenance, publisher, relation, rights, │ │ │ │ - // rightsHolder, source, subject, title, type, URI │ │ │ │ - "*": function(node, obj) { │ │ │ │ - var name = node.localName || node.nodeName.split(":").pop(); │ │ │ │ - if (!(OpenLayers.Util.isArray(obj[name]))) { │ │ │ │ - obj[name] = []; │ │ │ │ - } │ │ │ │ - var dc_element = {}; │ │ │ │ - var attrs = node.attributes; │ │ │ │ - for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ - dc_element[attrs[i].name] = attrs[i].nodeValue; │ │ │ │ - } │ │ │ │ - dc_element.value = this.getChildValue(node); │ │ │ │ - if (dc_element.value != "") { │ │ │ │ - obj[name].push(dc_element); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "dct": { │ │ │ │ - // abstract, modified, spatial │ │ │ │ - "*": function(node, obj) { │ │ │ │ - var name = node.localName || node.nodeName.split(":").pop(); │ │ │ │ - if (!(OpenLayers.Util.isArray(obj[name]))) { │ │ │ │ - obj[name] = []; │ │ │ │ - } │ │ │ │ - obj[name].push(this.getChildValue(node)); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "ows": OpenLayers.Util.applyDefaults({ │ │ │ │ - "BoundingBox": function(node, obj) { │ │ │ │ - if (obj.bounds) { │ │ │ │ - obj.BoundingBox = [{ │ │ │ │ - crs: obj.projection, │ │ │ │ - value: [ │ │ │ │ - obj.bounds.left, │ │ │ │ - obj.bounds.bottom, │ │ │ │ - obj.bounds.right, │ │ │ │ - obj.bounds.top │ │ │ │ - ] │ │ │ │ - }]; │ │ │ │ - delete obj.projection; │ │ │ │ - delete obj.bounds; │ │ │ │ - } │ │ │ │ - OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"]["BoundingBox"].apply( │ │ │ │ - this, arguments); │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"]) │ │ │ │ + deactivate: function() { │ │ │ │ + if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.map.events.unregister('moveend', this, this.update); │ │ │ │ + this.map.removeLayer(this.gratLayer); │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ }, │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: write │ │ │ │ - * Given an configuration js object, write a CSWGetRecords request. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} A object mapping the request. │ │ │ │ + * Method: update │ │ │ │ * │ │ │ │ + * calculates the grid to be displayed and actually draws it │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} A serialized CSWGetRecords request. │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - write: function(options) { │ │ │ │ - var node = this.writeNode("csw:GetRecords", options); │ │ │ │ - node.setAttribute("xmlns:gmd", this.namespaces.gmd); │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ - }, │ │ │ │ + update: function() { │ │ │ │ + //wait for the map to be initialized before proceeding │ │ │ │ + var mapBounds = this.map.getExtent(); │ │ │ │ + if (!mapBounds) { │ │ │ │ + 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: { │ │ │ │ - "csw": { │ │ │ │ - "GetRecords": function(options) { │ │ │ │ - if (!options) { │ │ │ │ - options = {}; │ │ │ │ - } │ │ │ │ - var node = this.createElementNSPlus("csw:GetRecords", { │ │ │ │ - attributes: { │ │ │ │ - service: "CSW", │ │ │ │ - version: this.version, │ │ │ │ - requestId: options.requestId || this.requestId, │ │ │ │ - resultType: options.resultType || this.resultType, │ │ │ │ - outputFormat: options.outputFormat || this.outputFormat, │ │ │ │ - outputSchema: options.outputSchema || this.outputSchema, │ │ │ │ - startPosition: options.startPosition || this.startPosition, │ │ │ │ - maxRecords: options.maxRecords || this.maxRecords │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - if (options.DistributedSearch || this.DistributedSearch) { │ │ │ │ - this.writeNode( │ │ │ │ - "csw:DistributedSearch", │ │ │ │ - options.DistributedSearch || this.DistributedSearch, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - var ResponseHandler = options.ResponseHandler || this.ResponseHandler; │ │ │ │ - if (OpenLayers.Util.isArray(ResponseHandler) && ResponseHandler.length > 0) { │ │ │ │ - // ResponseHandler must be a non-empty array │ │ │ │ - for (var i = 0, len = ResponseHandler.length; i < len; i++) { │ │ │ │ - this.writeNode( │ │ │ │ - "csw:ResponseHandler", │ │ │ │ - ResponseHandler[i], │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.writeNode("Query", options.Query || this.Query, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "DistributedSearch": function(options) { │ │ │ │ - var node = this.createElementNSPlus("csw:DistributedSearch", { │ │ │ │ - attributes: { │ │ │ │ - hopCount: options.hopCount │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "ResponseHandler": function(options) { │ │ │ │ - var node = this.createElementNSPlus("csw:ResponseHandler", { │ │ │ │ - value: options.value │ │ │ │ - }); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Query": function(options) { │ │ │ │ - if (!options) { │ │ │ │ - options = {}; │ │ │ │ - } │ │ │ │ - var node = this.createElementNSPlus("csw:Query", { │ │ │ │ - attributes: { │ │ │ │ - typeNames: options.typeNames || "csw:Record" │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - var ElementName = options.ElementName; │ │ │ │ - if (OpenLayers.Util.isArray(ElementName) && ElementName.length > 0) { │ │ │ │ - // ElementName must be a non-empty array │ │ │ │ - for (var i = 0, len = ElementName.length; i < len; i++) { │ │ │ │ - this.writeNode( │ │ │ │ - "csw:ElementName", │ │ │ │ - ElementName[i], │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.writeNode( │ │ │ │ - "csw:ElementSetName", │ │ │ │ - options.ElementSetName || { │ │ │ │ - value: 'summary' │ │ │ │ - }, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (options.Constraint) { │ │ │ │ - this.writeNode( │ │ │ │ - "csw:Constraint", │ │ │ │ - options.Constraint, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - if (options.SortBy) { │ │ │ │ - this.writeNode( │ │ │ │ - "ogc:SortBy", │ │ │ │ - options.SortBy, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ + //clear out the old grid │ │ │ │ + this.gratLayer.destroyFeatures(); │ │ │ │ + │ │ │ │ + //get the projection objects required │ │ │ │ + var llProj = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + var mapProj = this.map.getProjectionObject(); │ │ │ │ + var mapRes = this.map.getResolution(); │ │ │ │ + │ │ │ │ + //if the map is in lon/lat, then the lines are straight and only one │ │ │ │ + //point is required │ │ │ │ + if (mapProj.proj && mapProj.proj.projName == "longlat") { │ │ │ │ + this.numPoints = 1; │ │ │ │ + } │ │ │ │ + │ │ │ │ + //get the map center in EPSG:4326 │ │ │ │ + var mapCenter = this.map.getCenter(); //lon and lat here are really map x and y │ │ │ │ + var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat); │ │ │ │ + OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj); │ │ │ │ + │ │ │ │ + /* This block of code determines the lon/lat interval to use for the │ │ │ │ + * grid by calculating the diagonal size of one grid cell at the map │ │ │ │ + * center. Iterates through the intervals array until the diagonal │ │ │ │ + * length is less than the targetSize option. │ │ │ │ + */ │ │ │ │ + //find lat/lon interval that results in a grid of less than the target size │ │ │ │ + var testSq = this.targetSize * mapRes; │ │ │ │ + testSq *= testSq; //compare squares rather than doing a square root to save time │ │ │ │ + var llInterval; │ │ │ │ + for (var i = 0; i < this.intervals.length; ++i) { │ │ │ │ + llInterval = this.intervals[i]; //could do this for both x and y?? │ │ │ │ + var delta = llInterval / 2; │ │ │ │ + var p1 = mapCenterLL.offset({ │ │ │ │ + x: -delta, │ │ │ │ + y: -delta │ │ │ │ + }); //test coords in EPSG:4326 space │ │ │ │ + var p2 = mapCenterLL.offset({ │ │ │ │ + x: delta, │ │ │ │ + y: delta │ │ │ │ + }); │ │ │ │ + OpenLayers.Projection.transform(p1, llProj, mapProj); // convert them back to map projection │ │ │ │ + OpenLayers.Projection.transform(p2, llProj, mapProj); │ │ │ │ + var distSq = (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y); │ │ │ │ + if (distSq <= testSq) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + //alert(llInterval); │ │ │ │ + │ │ │ │ + //round the LL center to an even number based on the interval │ │ │ │ + mapCenterLL.x = Math.floor(mapCenterLL.x / llInterval) * llInterval; │ │ │ │ + mapCenterLL.y = Math.floor(mapCenterLL.y / llInterval) * llInterval; │ │ │ │ + //TODO adjust for minutses/seconds? │ │ │ │ + │ │ │ │ + /* The following 2 blocks calculate the nodes of the grid along a │ │ │ │ + * line of constant longitude (then latitiude) running through the │ │ │ │ + * center of the map until it reaches the map edge. The calculation │ │ │ │ + * goes from the center in both directions to the edge. │ │ │ │ + */ │ │ │ │ + //get the central longitude line, increment the latitude │ │ │ │ + var iter = 0; │ │ │ │ + var centerLonPoints = [mapCenterLL.clone()]; │ │ │ │ + var newPoint = mapCenterLL.clone(); │ │ │ │ + var mapXY; │ │ │ │ + do { │ │ │ │ + newPoint = newPoint.offset({ │ │ │ │ + x: 0, │ │ │ │ + y: llInterval │ │ │ │ + }); │ │ │ │ + mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); │ │ │ │ + centerLonPoints.unshift(newPoint); │ │ │ │ + } while (mapBounds.containsPixel(mapXY) && ++iter < 1000); │ │ │ │ + newPoint = mapCenterLL.clone(); │ │ │ │ + do { │ │ │ │ + newPoint = newPoint.offset({ │ │ │ │ + x: 0, │ │ │ │ + y: -llInterval │ │ │ │ + }); │ │ │ │ + mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); │ │ │ │ + centerLonPoints.push(newPoint); │ │ │ │ + } while (mapBounds.containsPixel(mapXY) && ++iter < 1000); │ │ │ │ + │ │ │ │ + //get the central latitude line, increment the longitude │ │ │ │ + iter = 0; │ │ │ │ + var centerLatPoints = [mapCenterLL.clone()]; │ │ │ │ + newPoint = mapCenterLL.clone(); │ │ │ │ + do { │ │ │ │ + newPoint = newPoint.offset({ │ │ │ │ + x: -llInterval, │ │ │ │ + y: 0 │ │ │ │ + }); │ │ │ │ + mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); │ │ │ │ + centerLatPoints.unshift(newPoint); │ │ │ │ + } while (mapBounds.containsPixel(mapXY) && ++iter < 1000); │ │ │ │ + newPoint = mapCenterLL.clone(); │ │ │ │ + do { │ │ │ │ + newPoint = newPoint.offset({ │ │ │ │ + x: llInterval, │ │ │ │ + y: 0 │ │ │ │ + }); │ │ │ │ + mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); │ │ │ │ + centerLatPoints.push(newPoint); │ │ │ │ + } while (mapBounds.containsPixel(mapXY) && ++iter < 1000); │ │ │ │ + │ │ │ │ + //now generate a line for each node in the central lat and lon lines │ │ │ │ + //first loop over constant longitude │ │ │ │ + var lines = []; │ │ │ │ + for (var i = 0; i < centerLatPoints.length; ++i) { │ │ │ │ + var lon = centerLatPoints[i].x; │ │ │ │ + var pointList = []; │ │ │ │ + var labelPoint = null; │ │ │ │ + var latEnd = Math.min(centerLonPoints[0].y, 90); │ │ │ │ + var latStart = Math.max(centerLonPoints[centerLonPoints.length - 1].y, -90); │ │ │ │ + var latDelta = (latEnd - latStart) / this.numPoints; │ │ │ │ + var lat = latStart; │ │ │ │ + for (var j = 0; j <= this.numPoints; ++j) { │ │ │ │ + var gridPoint = new OpenLayers.Geometry.Point(lon, lat); │ │ │ │ + gridPoint.transform(llProj, mapProj); │ │ │ │ + pointList.push(gridPoint); │ │ │ │ + lat += latDelta; │ │ │ │ + if (gridPoint.y >= mapBounds.bottom && !labelPoint) { │ │ │ │ + labelPoint = gridPoint; │ │ │ │ } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "ElementName": function(options) { │ │ │ │ - var node = this.createElementNSPlus("csw:ElementName", { │ │ │ │ - value: options.value │ │ │ │ - }); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "ElementSetName": function(options) { │ │ │ │ - var node = this.createElementNSPlus("csw:ElementSetName", { │ │ │ │ - attributes: { │ │ │ │ - typeNames: options.typeNames │ │ │ │ - }, │ │ │ │ - value: options.value │ │ │ │ - }); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Constraint": function(options) { │ │ │ │ - var node = this.createElementNSPlus("csw:Constraint", { │ │ │ │ - attributes: { │ │ │ │ - version: options.version │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - if (options.Filter) { │ │ │ │ - var format = new OpenLayers.Format.Filter({ │ │ │ │ - version: options.version │ │ │ │ - }); │ │ │ │ - node.appendChild(format.write(options.Filter)); │ │ │ │ - } else if (options.CqlText) { │ │ │ │ - var child = this.createElementNSPlus("CqlText", { │ │ │ │ - value: options.CqlText.value │ │ │ │ - }); │ │ │ │ - node.appendChild(child); │ │ │ │ + } │ │ │ │ + if (this.labelled) { │ │ │ │ + //keep track of when this grid line crosses the map bounds to set │ │ │ │ + //the label position │ │ │ │ + //labels along the bottom, add 10 pixel offset up into the map │ │ │ │ + //TODO add option for labels on top │ │ │ │ + var labelPos = new OpenLayers.Geometry.Point(labelPoint.x, mapBounds.bottom); │ │ │ │ + var labelAttrs = { │ │ │ │ + value: lon, │ │ │ │ + label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lon, "lon", this.labelFormat) : "", │ │ │ │ + labelAlign: "cb", │ │ │ │ + xOffset: 0, │ │ │ │ + yOffset: 2 │ │ │ │ + }; │ │ │ │ + this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs)); │ │ │ │ + } │ │ │ │ + var geom = new OpenLayers.Geometry.LineString(pointList); │ │ │ │ + lines.push(new OpenLayers.Feature.Vector(geom)); │ │ │ │ + } │ │ │ │ + │ │ │ │ + //now draw the lines of constant latitude │ │ │ │ + for (var j = 0; j < centerLonPoints.length; ++j) { │ │ │ │ + lat = centerLonPoints[j].y; │ │ │ │ + if (lat < -90 || lat > 90) { //latitudes only valid between -90 and 90 │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ + var pointList = []; │ │ │ │ + var lonStart = centerLatPoints[0].x; │ │ │ │ + var lonEnd = centerLatPoints[centerLatPoints.length - 1].x; │ │ │ │ + var lonDelta = (lonEnd - lonStart) / this.numPoints; │ │ │ │ + var lon = lonStart; │ │ │ │ + var labelPoint = null; │ │ │ │ + for (var i = 0; i <= this.numPoints; ++i) { │ │ │ │ + var gridPoint = new OpenLayers.Geometry.Point(lon, lat); │ │ │ │ + gridPoint.transform(llProj, mapProj); │ │ │ │ + pointList.push(gridPoint); │ │ │ │ + lon += lonDelta; │ │ │ │ + if (gridPoint.x < mapBounds.right) { │ │ │ │ + labelPoint = gridPoint; │ │ │ │ } │ │ │ │ - return node; │ │ │ │ } │ │ │ │ - }, │ │ │ │ - "ogc": OpenLayers.Format.Filter.v1_1_0.prototype.writers["ogc"] │ │ │ │ + if (this.labelled) { │ │ │ │ + //keep track of when this grid line crosses the map bounds to set │ │ │ │ + //the label position │ │ │ │ + //labels along the right, 30 pixel offset left into the map │ │ │ │ + //TODO add option for labels on left │ │ │ │ + var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y); │ │ │ │ + var labelAttrs = { │ │ │ │ + value: lat, │ │ │ │ + label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lat, "lat", this.labelFormat) : "", │ │ │ │ + labelAlign: "rb", │ │ │ │ + xOffset: -2, │ │ │ │ + yOffset: 2 │ │ │ │ + }; │ │ │ │ + this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs)); │ │ │ │ + } │ │ │ │ + var geom = new OpenLayers.Geometry.LineString(pointList); │ │ │ │ + lines.push(new OpenLayers.Feature.Vector(geom)); │ │ │ │ + } │ │ │ │ + this.gratLayer.addFeatures(lines); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.CSWGetRecords.v2_0_2" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Graticule" │ │ │ │ }); │ │ │ │ + │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/SLD/v1_0_0_GeoServer.js │ │ │ │ + OpenLayers/Control/WMSGetFeatureInfo.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/SLD/v1_0_0.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Handler/Click.js │ │ │ │ + * @requires OpenLayers/Handler/Hover.js │ │ │ │ + * @requires OpenLayers/Request.js │ │ │ │ + * @requires OpenLayers/Format/WMSGetFeatureInfo.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.SLD/v1_0_0_GeoServer │ │ │ │ - * Read and write SLD version 1.0.0 with GeoServer-specific enhanced options. │ │ │ │ - * See http://svn.osgeo.org/geotools/trunk/modules/extension/xsd/xsd-sld/src/main/resources/org/geotools/sld/bindings/StyledLayerDescriptor.xsd │ │ │ │ - * for more information. │ │ │ │ + * Class: OpenLayers.Control.WMSGetFeatureInfo │ │ │ │ + * The WMSGetFeatureInfo control uses a WMS query to get information about a point on the map. The │ │ │ │ + * information may be in a display-friendly format such as HTML, or a machine-friendly format such │ │ │ │ + * as GML, depending on the server's capabilities and the client's configuration. This control │ │ │ │ + * handles click or hover events, attempts to parse the results using an OpenLayers.Format, and │ │ │ │ + * fires a 'getfeatureinfo' event with the click position, the raw body of the response, and an │ │ │ │ + * array of features if it successfully read the response. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.SLD.v1_0_0> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.SLD.v1_0_0_GeoServer = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.SLD.v1_0_0, { │ │ │ │ +OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: version │ │ │ │ - * {String} The specific parser version. │ │ │ │ - */ │ │ │ │ - version: "1.0.0", │ │ │ │ + /** │ │ │ │ + * APIProperty: hover │ │ │ │ + * {Boolean} Send GetFeatureInfo requests when mouse stops moving. │ │ │ │ + * Default is false. │ │ │ │ + */ │ │ │ │ + hover: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: profile │ │ │ │ - * {String} The specific profile │ │ │ │ - */ │ │ │ │ - profile: "GeoServer", │ │ │ │ + /** │ │ │ │ + * APIProperty: drillDown │ │ │ │ + * {Boolean} Drill down over all WMS layers in the map. When │ │ │ │ + * using drillDown mode, hover is not possible, and an infoFormat that │ │ │ │ + * returns parseable features is required. Default is false. │ │ │ │ + */ │ │ │ │ + drillDown: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.SLD.v1_0_0_GeoServer │ │ │ │ - * Create a new parser for GeoServer-enhanced SLD version 1.0.0. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * APIProperty: maxFeatures │ │ │ │ + * {Integer} Maximum number of features to return from a WMS query. This │ │ │ │ + * sets the feature_count parameter on WMS GetFeatureInfo │ │ │ │ + * requests. │ │ │ │ + */ │ │ │ │ + maxFeatures: 10, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: OpenLayers.Util.applyDefaults({ │ │ │ │ - "sld": OpenLayers.Util.applyDefaults({ │ │ │ │ - "Priority": function(node, obj) { │ │ │ │ - var value = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (value) { │ │ │ │ - obj.priority = value; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "VendorOption": function(node, obj) { │ │ │ │ - if (!obj.vendorOptions) { │ │ │ │ - obj.vendorOptions = {}; │ │ │ │ - } │ │ │ │ - obj.vendorOptions[node.getAttribute("name")] = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "TextSymbolizer": function(node, rule) { │ │ │ │ - OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld.TextSymbolizer.apply(this, arguments); │ │ │ │ - var symbolizer = this.multipleSymbolizers ? rule.symbolizers[rule.symbolizers.length - 1] : rule.symbolizer["Text"]; │ │ │ │ - if (symbolizer.graphic === undefined) { │ │ │ │ - symbolizer.graphic = false; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.SLD.v1_0_0.prototype.readers["sld"]) │ │ │ │ - }, OpenLayers.Format.SLD.v1_0_0.prototype.readers), │ │ │ │ + /** │ │ │ │ + * APIProperty: clickCallback │ │ │ │ + * {String} The click callback to register in the │ │ │ │ + * {<OpenLayers.Handler.Click>} object created when the hover │ │ │ │ + * option is set to false. Default is "click". │ │ │ │ + */ │ │ │ │ + clickCallback: "click", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: writers │ │ │ │ - * As a compliment to the readers property, this structure contains public │ │ │ │ - * writing functions grouped by namespace alias and named like the │ │ │ │ - * node names they produce. │ │ │ │ - */ │ │ │ │ - writers: OpenLayers.Util.applyDefaults({ │ │ │ │ - "sld": OpenLayers.Util.applyDefaults({ │ │ │ │ - "Priority": function(priority) { │ │ │ │ - return this.writers.sld._OGCExpression.call( │ │ │ │ - this, "sld:Priority", priority │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - "VendorOption": function(option) { │ │ │ │ - return this.createElementNSPlus("sld:VendorOption", { │ │ │ │ - attributes: { │ │ │ │ - name: option.name │ │ │ │ - }, │ │ │ │ - value: option.value │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "TextSymbolizer": function(symbolizer) { │ │ │ │ - var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers; │ │ │ │ - var node = writers["sld"]["TextSymbolizer"].apply(this, arguments); │ │ │ │ - if (symbolizer.graphic !== false && (symbolizer.externalGraphic || symbolizer.graphicName)) { │ │ │ │ - this.writeNode("Graphic", symbolizer, node); │ │ │ │ - } │ │ │ │ - if ("priority" in symbolizer) { │ │ │ │ - this.writeNode("Priority", symbolizer.priority, node); │ │ │ │ - } │ │ │ │ - return this.addVendorOptions(node, symbolizer); │ │ │ │ - }, │ │ │ │ - "PointSymbolizer": function(symbolizer) { │ │ │ │ - var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers; │ │ │ │ - var node = writers["sld"]["PointSymbolizer"].apply(this, arguments); │ │ │ │ - return this.addVendorOptions(node, symbolizer); │ │ │ │ - }, │ │ │ │ - "LineSymbolizer": function(symbolizer) { │ │ │ │ - var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers; │ │ │ │ - var node = writers["sld"]["LineSymbolizer"].apply(this, arguments); │ │ │ │ - return this.addVendorOptions(node, symbolizer); │ │ │ │ - }, │ │ │ │ - "PolygonSymbolizer": function(symbolizer) { │ │ │ │ - var writers = OpenLayers.Format.SLD.v1_0_0.prototype.writers; │ │ │ │ - var node = writers["sld"]["PolygonSymbolizer"].apply(this, arguments); │ │ │ │ - return this.addVendorOptions(node, symbolizer); │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.SLD.v1_0_0.prototype.writers["sld"]) │ │ │ │ - }, OpenLayers.Format.SLD.v1_0_0.prototype.writers), │ │ │ │ + /** │ │ │ │ + * APIProperty: output │ │ │ │ + * {String} Either "features" or "object". When triggering a getfeatureinfo │ │ │ │ + * request should we pass on an array of features or an object with with │ │ │ │ + * a "features" property and other properties (such as the url of the │ │ │ │ + * WMS). Default is "features". │ │ │ │ + */ │ │ │ │ + output: "features", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: addVendorOptions │ │ │ │ - * Add in the VendorOption tags and return the node again. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} A DOM node. │ │ │ │ - * symbolizer - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A DOM node. │ │ │ │ - */ │ │ │ │ - addVendorOptions: function(node, symbolizer) { │ │ │ │ - var options = symbolizer.vendorOptions; │ │ │ │ - if (options) { │ │ │ │ - for (var key in symbolizer.vendorOptions) { │ │ │ │ - this.writeNode("VendorOption", { │ │ │ │ - name: key, │ │ │ │ - value: symbolizer.vendorOptions[key] │ │ │ │ - }, node); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: layers │ │ │ │ + * {Array(<OpenLayers.Layer.WMS>)} The layers to query for feature info. │ │ │ │ + * If omitted, all map WMS layers with a url that matches this <url> or │ │ │ │ + * <layerUrls> will be considered. │ │ │ │ + */ │ │ │ │ + layers: null, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.SLD.v1_0_0_GeoServer" │ │ │ │ + /** │ │ │ │ + * APIProperty: queryVisible │ │ │ │ + * {Boolean} If true, filter out hidden layers when searching the map for │ │ │ │ + * layers to query. Default is false. │ │ │ │ + */ │ │ │ │ + queryVisible: false, │ │ │ │ │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/CSWGetDomain/v2_0_2.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * APIProperty: url │ │ │ │ + * {String} The URL of the WMS service to use. If not provided, the url │ │ │ │ + * of the first eligible layer will be used. │ │ │ │ + */ │ │ │ │ + url: null, │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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: layerUrls │ │ │ │ + * {Array(String)} Optional list of urls for layers that should be queried. │ │ │ │ + * This can be used when the layer url differs from the url used for │ │ │ │ + * making GetFeatureInfo requests (in the case of a layer using cached │ │ │ │ + * tiles). │ │ │ │ + */ │ │ │ │ + layerUrls: null, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Format/CSWGetDomain.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * APIProperty: infoFormat │ │ │ │ + * {String} The mimetype to request from the server. If you are using │ │ │ │ + * drillDown mode and have multiple servers that do not share a common │ │ │ │ + * infoFormat, you can override the control's infoFormat by providing an │ │ │ │ + * INFO_FORMAT parameter in your <OpenLayers.Layer.WMS> instance(s). │ │ │ │ + */ │ │ │ │ + infoFormat: 'text/html', │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.CSWGetDomain.v2_0_2 │ │ │ │ - * A format for creating CSWGetDomain v2.0.2 transactions. │ │ │ │ - * Create a new instance with the │ │ │ │ - * <OpenLayers.Format.CSWGetDomain.v2_0_2> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.CSWGetDomain.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + /** │ │ │ │ + * APIProperty: vendorParams │ │ │ │ + * {Object} Additional parameters that will be added to the request, for │ │ │ │ + * WMS implementations that support them. This could e.g. look like │ │ │ │ + * (start code) │ │ │ │ + * { │ │ │ │ + * radius: 5 │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ + */ │ │ │ │ + vendorParams: {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + * APIProperty: format │ │ │ │ + * {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses. │ │ │ │ + * Default is <OpenLayers.Format.WMSGetFeatureInfo>. │ │ │ │ */ │ │ │ │ - namespaces: { │ │ │ │ - xlink: "http://www.w3.org/1999/xlink", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance", │ │ │ │ - csw: "http://www.opengis.net/cat/csw/2.0.2" │ │ │ │ - }, │ │ │ │ + format: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ - * {String} The default prefix (used by Format.XML). │ │ │ │ + * APIProperty: formatOptions │ │ │ │ + * {Object} Optional properties to set on the format (if one is not provided │ │ │ │ + * in the <format> property. │ │ │ │ */ │ │ │ │ - defaultPrefix: "csw", │ │ │ │ + formatOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: version │ │ │ │ - * {String} CSW version number. │ │ │ │ + * APIProperty: handlerOptions │ │ │ │ + * {Object} Additional options for the handlers used by this control, e.g. │ │ │ │ + * (start code) │ │ │ │ + * { │ │ │ │ + * "click": {delay: 100}, │ │ │ │ + * "hover": {delay: 300} │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ */ │ │ │ │ - version: "2.0.2", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} http://www.opengis.net/cat/csw/2.0.2 │ │ │ │ - * http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd │ │ │ │ + * Property: handler │ │ │ │ + * {Object} Reference to the <OpenLayers.Handler> for this control │ │ │ │ */ │ │ │ │ - schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd", │ │ │ │ + handler: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: PropertyName │ │ │ │ - * {String} Value of the csw:PropertyName element, used when │ │ │ │ - * writing a GetDomain document. │ │ │ │ + * Property: hoverRequest │ │ │ │ + * {<OpenLayers.Request>} contains the currently running hover request │ │ │ │ + * (if any). │ │ │ │ */ │ │ │ │ - PropertyName: null, │ │ │ │ + hoverRequest: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: ParameterName │ │ │ │ - * {String} Value of the csw:ParameterName element, used when │ │ │ │ - * writing a GetDomain document. │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * beforegetfeatureinfo - Triggered before the request is sent. │ │ │ │ + * The event object has an *xy* property with the position of the │ │ │ │ + * mouse click or hover event that triggers the request. │ │ │ │ + * nogetfeatureinfo - no queryable layers were found. │ │ │ │ + * getfeatureinfo - Triggered when a GetFeatureInfo response is received. │ │ │ │ + * The event object has a *text* property with the body of the │ │ │ │ + * response (String), a *features* property with an array of the │ │ │ │ + * parsed features, an *xy* property with the position of the mouse │ │ │ │ + * click or hover event that triggered the request, and a *request* │ │ │ │ + * property with the request itself. If drillDown is set to true and │ │ │ │ + * multiple requests were issued to collect feature info from all │ │ │ │ + * layers, *text* and *request* will only contain the response body │ │ │ │ + * and request object of the last request. │ │ │ │ */ │ │ │ │ - ParameterName: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.CSWGetDomain.v2_0_2 │ │ │ │ - * A class for parsing and generating CSWGetDomain v2.0.2 transactions. │ │ │ │ + * Constructor: <OpenLayers.Control.WMSGetFeatureInfo> │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * options - {Object} │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + options.handlerOptions = options.handlerOptions || {}; │ │ │ │ + │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + │ │ │ │ + if (!this.format) { │ │ │ │ + this.format = new OpenLayers.Format.WMSGetFeatureInfo( │ │ │ │ + options.formatOptions │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.drillDown === true) { │ │ │ │ + this.hover = false; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.hover) { │ │ │ │ + this.handler = new OpenLayers.Handler.Hover( │ │ │ │ + this, { │ │ │ │ + 'move': this.cancelHover, │ │ │ │ + 'pause': this.getInfoForHover │ │ │ │ + }, │ │ │ │ + OpenLayers.Util.extend(this.handlerOptions.hover || {}, { │ │ │ │ + 'delay': 250 │ │ │ │ + })); │ │ │ │ + } else { │ │ │ │ + var callbacks = {}; │ │ │ │ + callbacks[this.clickCallback] = this.getInfoForClick; │ │ │ │ + this.handler = new OpenLayers.Handler.Click( │ │ │ │ + this, callbacks, this.handlerOptions.click || {}); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getInfoForClick │ │ │ │ + * Called on click │ │ │ │ * │ │ │ │ - * Valid options properties: │ │ │ │ - * - PropertyName │ │ │ │ - * - ParameterName │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ */ │ │ │ │ + getInfoForClick: function(evt) { │ │ │ │ + this.events.triggerEvent("beforegetfeatureinfo", { │ │ │ │ + xy: evt.xy │ │ │ │ + }); │ │ │ │ + // Set the cursor to "wait" to tell the user we're working on their │ │ │ │ + // click. │ │ │ │ + OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ + this.request(evt.xy, {}); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Parse the response from a GetDomain request. │ │ │ │ + * Method: getInfoForHover │ │ │ │ + * Pause callback for the hover handler │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {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; │ │ │ │ + getInfoForHover: function(evt) { │ │ │ │ + this.events.triggerEvent("beforegetfeatureinfo", { │ │ │ │ + xy: evt.xy │ │ │ │ + }); │ │ │ │ + this.request(evt.xy, { │ │ │ │ + hover: true │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: cancelHover │ │ │ │ + * Cancel callback for the hover handler │ │ │ │ + */ │ │ │ │ + cancelHover: function() { │ │ │ │ + if (this.hoverRequest) { │ │ │ │ + this.hoverRequest.abort(); │ │ │ │ + this.hoverRequest = null; │ │ │ │ } │ │ │ │ - var obj = {}; │ │ │ │ - this.readNode(data, obj); │ │ │ │ - return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ + * Method: findLayers │ │ │ │ + * Internal method to get the layers, independent of whether we are │ │ │ │ + * inspecting the map or using a client-provided array │ │ │ │ */ │ │ │ │ - readers: { │ │ │ │ - "csw": { │ │ │ │ - "GetDomainResponse": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "DomainValues": function(node, obj) { │ │ │ │ - if (!(OpenLayers.Util.isArray(obj.DomainValues))) { │ │ │ │ - obj.DomainValues = []; │ │ │ │ - } │ │ │ │ - var attrs = node.attributes; │ │ │ │ - var domainValue = {}; │ │ │ │ - for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ - domainValue[attrs[i].name] = attrs[i].nodeValue; │ │ │ │ - } │ │ │ │ - this.readChildNodes(node, domainValue); │ │ │ │ - obj.DomainValues.push(domainValue); │ │ │ │ - }, │ │ │ │ - "PropertyName": function(node, obj) { │ │ │ │ - obj.PropertyName = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "ParameterName": function(node, obj) { │ │ │ │ - obj.ParameterName = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "ListOfValues": function(node, obj) { │ │ │ │ - if (!(OpenLayers.Util.isArray(obj.ListOfValues))) { │ │ │ │ - obj.ListOfValues = []; │ │ │ │ - } │ │ │ │ - this.readChildNodes(node, obj.ListOfValues); │ │ │ │ - }, │ │ │ │ - "Value": function(node, obj) { │ │ │ │ - var attrs = node.attributes; │ │ │ │ - var value = {}; │ │ │ │ - for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ - value[attrs[i].name] = attrs[i].nodeValue; │ │ │ │ + findLayers: function() { │ │ │ │ + │ │ │ │ + var candidates = this.layers || this.map.layers; │ │ │ │ + var layers = []; │ │ │ │ + var layer, url; │ │ │ │ + for (var i = candidates.length - 1; i >= 0; --i) { │ │ │ │ + layer = candidates[i]; │ │ │ │ + if (layer instanceof OpenLayers.Layer.WMS && │ │ │ │ + (!this.queryVisible || layer.getVisibility())) { │ │ │ │ + url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url; │ │ │ │ + // if the control was not configured with a url, set it │ │ │ │ + // to the first layer url │ │ │ │ + if (this.drillDown === false && !this.url) { │ │ │ │ + this.url = url; │ │ │ │ } │ │ │ │ - value.value = this.getChildValue(node); │ │ │ │ - obj.push({ │ │ │ │ - Value: value │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "ConceptualScheme": function(node, obj) { │ │ │ │ - obj.ConceptualScheme = {}; │ │ │ │ - this.readChildNodes(node, obj.ConceptualScheme); │ │ │ │ - }, │ │ │ │ - "Name": function(node, obj) { │ │ │ │ - obj.Name = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Document": function(node, obj) { │ │ │ │ - obj.Document = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Authority": function(node, obj) { │ │ │ │ - obj.Authority = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "RangeOfValues": function(node, obj) { │ │ │ │ - obj.RangeOfValues = {}; │ │ │ │ - this.readChildNodes(node, obj.RangeOfValues); │ │ │ │ - }, │ │ │ │ - "MinValue": function(node, obj) { │ │ │ │ - var attrs = node.attributes; │ │ │ │ - var value = {}; │ │ │ │ - for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ - value[attrs[i].name] = attrs[i].nodeValue; │ │ │ │ + if (this.drillDown === true || this.urlMatches(url)) { │ │ │ │ + layers.push(layer); │ │ │ │ } │ │ │ │ - value.value = this.getChildValue(node); │ │ │ │ - obj.MinValue = value; │ │ │ │ - }, │ │ │ │ - "MaxValue": function(node, obj) { │ │ │ │ - var attrs = node.attributes; │ │ │ │ - var value = {}; │ │ │ │ - for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ - value[attrs[i].name] = attrs[i].nodeValue; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return layers; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: urlMatches │ │ │ │ + * Test to see if the provided url matches either the control <url> or one │ │ │ │ + * of the <layerUrls>. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * url - {String} The url to test. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The provided url matches the control <url> or one of the │ │ │ │ + * <layerUrls>. │ │ │ │ + */ │ │ │ │ + urlMatches: function(url) { │ │ │ │ + var matches = OpenLayers.Util.isEquivalentUrl(this.url, url); │ │ │ │ + if (!matches && this.layerUrls) { │ │ │ │ + for (var i = 0, len = this.layerUrls.length; i < len; ++i) { │ │ │ │ + if (OpenLayers.Util.isEquivalentUrl(this.layerUrls[i], url)) { │ │ │ │ + matches = true; │ │ │ │ + break; │ │ │ │ } │ │ │ │ - value.value = this.getChildValue(node); │ │ │ │ - obj.MaxValue = value; │ │ │ │ } │ │ │ │ } │ │ │ │ + return matches; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Given an configuration js object, write a CSWGetDomain request. │ │ │ │ + * Method: buildWMSOptions │ │ │ │ + * Build an object with the relevant WMS options for the GetFeatureInfo request │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} A object mapping the request. │ │ │ │ + * url - {String} The url to be used for sending the request │ │ │ │ + * layers - {Array(<OpenLayers.Layer.WMS)} An array of layers │ │ │ │ + * clickPosition - {<OpenLayers.Pixel>} The position on the map where the mouse │ │ │ │ + * event occurred. │ │ │ │ + * format - {String} The format from the corresponding GetMap request │ │ │ │ + */ │ │ │ │ + buildWMSOptions: function(url, layers, clickPosition, format) { │ │ │ │ + var layerNames = [], │ │ │ │ + styleNames = []; │ │ │ │ + for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ + if (layers[i].params.LAYERS != null) { │ │ │ │ + layerNames = layerNames.concat(layers[i].params.LAYERS); │ │ │ │ + styleNames = styleNames.concat(this.getStyleNames(layers[i])); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var firstLayer = layers[0]; │ │ │ │ + // use the firstLayer's projection if it matches the map projection - │ │ │ │ + // this assumes that all layers will be available in this projection │ │ │ │ + var projection = this.map.getProjection(); │ │ │ │ + var layerProj = firstLayer.projection; │ │ │ │ + if (layerProj && layerProj.equals(this.map.getProjectionObject())) { │ │ │ │ + projection = layerProj.getCode(); │ │ │ │ + } │ │ │ │ + var params = OpenLayers.Util.extend({ │ │ │ │ + service: "WMS", │ │ │ │ + version: firstLayer.params.VERSION, │ │ │ │ + request: "GetFeatureInfo", │ │ │ │ + exceptions: firstLayer.params.EXCEPTIONS, │ │ │ │ + bbox: this.map.getExtent().toBBOX(null, │ │ │ │ + firstLayer.reverseAxisOrder()), │ │ │ │ + feature_count: this.maxFeatures, │ │ │ │ + height: this.map.getSize().h, │ │ │ │ + width: this.map.getSize().w, │ │ │ │ + format: format, │ │ │ │ + info_format: firstLayer.params.INFO_FORMAT || this.infoFormat │ │ │ │ + }, (parseFloat(firstLayer.params.VERSION) >= 1.3) ? { │ │ │ │ + crs: projection, │ │ │ │ + i: parseInt(clickPosition.x), │ │ │ │ + j: parseInt(clickPosition.y) │ │ │ │ + } : { │ │ │ │ + srs: projection, │ │ │ │ + x: parseInt(clickPosition.x), │ │ │ │ + y: parseInt(clickPosition.y) │ │ │ │ + }); │ │ │ │ + if (layerNames.length != 0) { │ │ │ │ + params = OpenLayers.Util.extend({ │ │ │ │ + layers: layerNames, │ │ │ │ + query_layers: layerNames, │ │ │ │ + styles: styleNames │ │ │ │ + }, params); │ │ │ │ + } │ │ │ │ + OpenLayers.Util.applyDefaults(params, this.vendorParams); │ │ │ │ + return { │ │ │ │ + url: url, │ │ │ │ + params: OpenLayers.Util.upperCaseObject(params), │ │ │ │ + callback: function(request) { │ │ │ │ + this.handleResponse(clickPosition, request, url); │ │ │ │ + }, │ │ │ │ + scope: this │ │ │ │ + }; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getStyleNames │ │ │ │ + * Gets the STYLES parameter for the layer. Make sure the STYLES parameter │ │ │ │ + * matches the LAYERS parameter │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layer - {<OpenLayers.Layer.WMS>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A serialized CSWGetDomain request. │ │ │ │ + * {Array(String)} The STYLES parameter │ │ │ │ */ │ │ │ │ - write: function(options) { │ │ │ │ - var node = this.writeNode("csw:GetDomain", options); │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ + getStyleNames: function(layer) { │ │ │ │ + // in the event of a WMS layer bundling multiple layers but not │ │ │ │ + // specifying styles,we need the same number of commas to specify │ │ │ │ + // the default style for each of the layers. We can't just leave it │ │ │ │ + // blank as we may be including other layers that do specify styles. │ │ │ │ + var styleNames; │ │ │ │ + if (layer.params.STYLES) { │ │ │ │ + styleNames = layer.params.STYLES; │ │ │ │ + } else { │ │ │ │ + if (OpenLayers.Util.isArray(layer.params.LAYERS)) { │ │ │ │ + styleNames = new Array(layer.params.LAYERS.length); │ │ │ │ + } else { // Assume it's a String │ │ │ │ + styleNames = layer.params.LAYERS.replace(/[^,]/g, ""); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return styleNames; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: request │ │ │ │ + * Sends a GetFeatureInfo request to the WMS │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * clickPosition - {<OpenLayers.Pixel>} The position on the map where the │ │ │ │ + * mouse event occurred. │ │ │ │ + * options - {Object} additional options for this method. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * - *hover* {Boolean} true if we do the request for the hover handler │ │ │ │ */ │ │ │ │ - writers: { │ │ │ │ - "csw": { │ │ │ │ - "GetDomain": function(options) { │ │ │ │ - var node = this.createElementNSPlus("csw:GetDomain", { │ │ │ │ - attributes: { │ │ │ │ - service: "CSW", │ │ │ │ - version: this.version │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - if (options.PropertyName || this.PropertyName) { │ │ │ │ - this.writeNode( │ │ │ │ - "csw:PropertyName", │ │ │ │ - options.PropertyName || this.PropertyName, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ - } else if (options.ParameterName || this.ParameterName) { │ │ │ │ - this.writeNode( │ │ │ │ - "csw:ParameterName", │ │ │ │ - options.ParameterName || this.ParameterName, │ │ │ │ - node │ │ │ │ - ); │ │ │ │ + request: function(clickPosition, options) { │ │ │ │ + var layers = this.findLayers(); │ │ │ │ + if (layers.length == 0) { │ │ │ │ + this.events.triggerEvent("nogetfeatureinfo"); │ │ │ │ + // Reset the cursor. │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + │ │ │ │ + options = options || {}; │ │ │ │ + if (this.drillDown === false) { │ │ │ │ + var wmsOptions = this.buildWMSOptions(this.url, layers, │ │ │ │ + clickPosition, layers[0].params.FORMAT); │ │ │ │ + var request = OpenLayers.Request.GET(wmsOptions); │ │ │ │ + │ │ │ │ + if (options.hover === true) { │ │ │ │ + this.hoverRequest = request; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this._requestCount = 0; │ │ │ │ + this._numRequests = 0; │ │ │ │ + this.features = []; │ │ │ │ + // group according to service url to combine requests │ │ │ │ + var services = {}, │ │ │ │ + url; │ │ │ │ + for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ + var layer = layers[i]; │ │ │ │ + var service, found = false; │ │ │ │ + url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url; │ │ │ │ + if (url in services) { │ │ │ │ + services[url].push(layer); │ │ │ │ + } else { │ │ │ │ + this._numRequests++; │ │ │ │ + services[url] = [layer]; │ │ │ │ } │ │ │ │ - this.readChildNodes(node, options); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "PropertyName": function(value) { │ │ │ │ - var node = this.createElementNSPlus("csw:PropertyName", { │ │ │ │ - value: value │ │ │ │ - }); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "ParameterName": function(value) { │ │ │ │ - var node = this.createElementNSPlus("csw:ParameterName", { │ │ │ │ - value: value │ │ │ │ + } │ │ │ │ + var layers; │ │ │ │ + for (var url in services) { │ │ │ │ + layers = services[url]; │ │ │ │ + var wmsOptions = this.buildWMSOptions(url, layers, │ │ │ │ + clickPosition, layers[0].params.FORMAT); │ │ │ │ + OpenLayers.Request.GET(wmsOptions); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: triggerGetFeatureInfo │ │ │ │ + * Trigger the getfeatureinfo event when all is done │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * request - {XMLHttpRequest} The request object │ │ │ │ + * xy - {<OpenLayers.Pixel>} The position on the map where the │ │ │ │ + * mouse event occurred. │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} or │ │ │ │ + * {Array({Object}) when output is "object". The object has a url and a │ │ │ │ + * features property which contains an array of features. │ │ │ │ + */ │ │ │ │ + triggerGetFeatureInfo: function(request, xy, features) { │ │ │ │ + this.events.triggerEvent("getfeatureinfo", { │ │ │ │ + text: request.responseText, │ │ │ │ + features: features, │ │ │ │ + request: request, │ │ │ │ + xy: xy │ │ │ │ + }); │ │ │ │ + │ │ │ │ + // Reset the cursor. │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: handleResponse │ │ │ │ + * Handler for the GetFeatureInfo response. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * xy - {<OpenLayers.Pixel>} The position on the map where the │ │ │ │ + * mouse event occurred. │ │ │ │ + * request - {XMLHttpRequest} The request object. │ │ │ │ + * url - {String} The url which was used for this request. │ │ │ │ + */ │ │ │ │ + handleResponse: function(xy, request, url) { │ │ │ │ + │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText; │ │ │ │ + } │ │ │ │ + var features = this.format.read(doc); │ │ │ │ + if (this.drillDown === false) { │ │ │ │ + this.triggerGetFeatureInfo(request, xy, features); │ │ │ │ + } else { │ │ │ │ + this._requestCount++; │ │ │ │ + if (this.output === "object") { │ │ │ │ + this._features = (this._features || []).concat({ │ │ │ │ + url: url, │ │ │ │ + features: features │ │ │ │ }); │ │ │ │ - return node; │ │ │ │ + } else { │ │ │ │ + this._features = (this._features || []).concat(features); │ │ │ │ + } │ │ │ │ + if (this._requestCount === this._numRequests) { │ │ │ │ + this.triggerGetFeatureInfo(request, xy, this._features.concat()); │ │ │ │ + delete this._features; │ │ │ │ + delete this._requestCount; │ │ │ │ + delete this._numRequests; │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.CSWGetDomain.v2_0_2" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.WMSGetFeatureInfo" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/WMTSCapabilities/v1_0_0.js │ │ │ │ + OpenLayers/Control/NavToolbar.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/WMTSCapabilities.js │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ + * @requires OpenLayers/Control/Panel.js │ │ │ │ + * @requires OpenLayers/Control/Navigation.js │ │ │ │ + * @requires OpenLayers/Control/ZoomBox.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.WMTSCapabilities.v1_0_0 │ │ │ │ - * Read WMTS Capabilities version 1.0.0. │ │ │ │ + * Class: OpenLayers.Control.NavToolbar │ │ │ │ + * This Toolbar is an alternative to the Navigation control that displays │ │ │ │ + * the state of the control, and provides a UI for changing state to │ │ │ │ + * use the zoomBox via a Panel control. │ │ │ │ + * │ │ │ │ + * If you wish to change the properties of the Navigation control used │ │ │ │ + * in the NavToolbar, see: │ │ │ │ + * http://trac.openlayers.org/wiki/Toolbars#SubclassingNavToolbar │ │ │ │ + * │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WMTSCapabilities> │ │ │ │ + * - <OpenLayers.Control.Panel> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.WMTSCapabilities.v1_0_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.OWSCommon.v1_1_0, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: version │ │ │ │ - * {String} The parser version ("1.0.0"). │ │ │ │ - */ │ │ │ │ - version: "1.0.0", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ - */ │ │ │ │ - namespaces: { │ │ │ │ - ows: "http://www.opengis.net/ows/1.1", │ │ │ │ - wmts: "http://www.opengis.net/wmts/1.0", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink" │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: yx │ │ │ │ - * {Object} Members in the yx object are used to determine if a CRS URN │ │ │ │ - * corresponds to a CRS with y,x axis order. Member names are CRS URNs │ │ │ │ - * and values are boolean. Defaults come from the │ │ │ │ - * <OpenLayers.Format.WMTSCapabilities> prototype. │ │ │ │ - */ │ │ │ │ - yx: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ - * {String} The default namespace alias for creating element nodes. │ │ │ │ - */ │ │ │ │ - defaultPrefix: "wmts", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WMTSCapabilities.v1_0_0 │ │ │ │ - * Create a new parser for WMTS capabilities version 1.0.0. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ - this.options = options; │ │ │ │ - var yx = OpenLayers.Util.extend({}, OpenLayers.Format.WMTSCapabilities.prototype.yx); │ │ │ │ - this.yx = OpenLayers.Util.extend(yx, this.yx); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read capabilities data from a string, and return info about the WMTS. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Information about the SOS service. │ │ │ │ - */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - } │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ - data = data.documentElement; │ │ │ │ - } │ │ │ │ - var capabilities = {}; │ │ │ │ - this.readNode(data, capabilities); │ │ │ │ - capabilities.version = this.version; │ │ │ │ - return capabilities; │ │ │ │ - }, │ │ │ │ +OpenLayers.Control.NavToolbar = OpenLayers.Class(OpenLayers.Control.Panel, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wmts": { │ │ │ │ - "Capabilities": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "Contents": function(node, obj) { │ │ │ │ - obj.contents = {}; │ │ │ │ - obj.contents.layers = []; │ │ │ │ - obj.contents.tileMatrixSets = {}; │ │ │ │ - this.readChildNodes(node, obj.contents); │ │ │ │ - }, │ │ │ │ - "Layer": function(node, obj) { │ │ │ │ - var layer = { │ │ │ │ - styles: [], │ │ │ │ - formats: [], │ │ │ │ - dimensions: [], │ │ │ │ - tileMatrixSetLinks: [] │ │ │ │ - }; │ │ │ │ - layer.layers = []; │ │ │ │ - this.readChildNodes(node, layer); │ │ │ │ - obj.layers.push(layer); │ │ │ │ - }, │ │ │ │ - "Style": function(node, obj) { │ │ │ │ - var style = {}; │ │ │ │ - style.isDefault = (node.getAttribute("isDefault") === "true"); │ │ │ │ - this.readChildNodes(node, style); │ │ │ │ - obj.styles.push(style); │ │ │ │ - }, │ │ │ │ - "Format": function(node, obj) { │ │ │ │ - obj.formats.push(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "TileMatrixSetLink": function(node, obj) { │ │ │ │ - var tileMatrixSetLink = {}; │ │ │ │ - this.readChildNodes(node, tileMatrixSetLink); │ │ │ │ - obj.tileMatrixSetLinks.push(tileMatrixSetLink); │ │ │ │ - }, │ │ │ │ - "TileMatrixSet": function(node, obj) { │ │ │ │ - // node could be child of wmts:Contents or wmts:TileMatrixSetLink │ │ │ │ - // duck type wmts:Contents by looking for layers │ │ │ │ - if (obj.layers) { │ │ │ │ - // TileMatrixSet as object type in schema │ │ │ │ - var tileMatrixSet = { │ │ │ │ - matrixIds: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, tileMatrixSet); │ │ │ │ - obj.tileMatrixSets[tileMatrixSet.identifier] = tileMatrixSet; │ │ │ │ - } else { │ │ │ │ - // TileMatrixSet as string type in schema │ │ │ │ - obj.tileMatrixSet = this.getChildValue(node); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "TileMatrix": function(node, obj) { │ │ │ │ - var tileMatrix = { │ │ │ │ - supportedCRS: obj.supportedCRS │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, tileMatrix); │ │ │ │ - obj.matrixIds.push(tileMatrix); │ │ │ │ - }, │ │ │ │ - "ScaleDenominator": function(node, obj) { │ │ │ │ - obj.scaleDenominator = parseFloat(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "TopLeftCorner": function(node, obj) { │ │ │ │ - var topLeftCorner = this.getChildValue(node); │ │ │ │ - var coords = topLeftCorner.split(" "); │ │ │ │ - // decide on axis order for the given CRS │ │ │ │ - var yx; │ │ │ │ - if (obj.supportedCRS) { │ │ │ │ - // extract out version from URN │ │ │ │ - var crs = obj.supportedCRS.replace( │ │ │ │ - /urn:ogc:def:crs:(\w+):.+:(\w+)$/, │ │ │ │ - "urn:ogc:def:crs:$1::$2" │ │ │ │ - ); │ │ │ │ - yx = !!this.yx[crs]; │ │ │ │ - } │ │ │ │ - if (yx) { │ │ │ │ - obj.topLeftCorner = new OpenLayers.LonLat( │ │ │ │ - coords[1], coords[0] │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - obj.topLeftCorner = new OpenLayers.LonLat( │ │ │ │ - coords[0], coords[1] │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "TileWidth": function(node, obj) { │ │ │ │ - obj.tileWidth = parseInt(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "TileHeight": function(node, obj) { │ │ │ │ - obj.tileHeight = parseInt(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "MatrixWidth": function(node, obj) { │ │ │ │ - obj.matrixWidth = parseInt(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "MatrixHeight": function(node, obj) { │ │ │ │ - obj.matrixHeight = parseInt(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "ResourceURL": function(node, obj) { │ │ │ │ - obj.resourceUrl = obj.resourceUrl || {}; │ │ │ │ - var resourceType = node.getAttribute("resourceType"); │ │ │ │ - if (!obj.resourceUrls) { │ │ │ │ - obj.resourceUrls = []; │ │ │ │ - } │ │ │ │ - var resourceUrl = obj.resourceUrl[resourceType] = { │ │ │ │ - format: node.getAttribute("format"), │ │ │ │ - template: node.getAttribute("template"), │ │ │ │ - resourceType: resourceType │ │ │ │ - }; │ │ │ │ - obj.resourceUrls.push(resourceUrl); │ │ │ │ - }, │ │ │ │ - // not used for now, can be added in the future though │ │ │ │ - /*"Themes": function(node, obj) { │ │ │ │ - obj.themes = []; │ │ │ │ - this.readChildNodes(node, obj.themes); │ │ │ │ - }, │ │ │ │ - "Theme": function(node, obj) { │ │ │ │ - var theme = {}; │ │ │ │ - this.readChildNodes(node, theme); │ │ │ │ - obj.push(theme); │ │ │ │ - },*/ │ │ │ │ - "WSDL": function(node, obj) { │ │ │ │ - obj.wsdl = {}; │ │ │ │ - obj.wsdl.href = node.getAttribute("xlink:href"); │ │ │ │ - // TODO: other attributes of <WSDL> element │ │ │ │ - }, │ │ │ │ - "ServiceMetadataURL": function(node, obj) { │ │ │ │ - obj.serviceMetadataUrl = {}; │ │ │ │ - obj.serviceMetadataUrl.href = node.getAttribute("xlink:href"); │ │ │ │ - // TODO: other attributes of <ServiceMetadataURL> element │ │ │ │ - }, │ │ │ │ - "LegendURL": function(node, obj) { │ │ │ │ - obj.legend = {}; │ │ │ │ - obj.legend.href = node.getAttribute("xlink:href"); │ │ │ │ - obj.legend.format = node.getAttribute("format"); │ │ │ │ - }, │ │ │ │ - "Dimension": function(node, obj) { │ │ │ │ - var dimension = { │ │ │ │ - values: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, dimension); │ │ │ │ - obj.dimensions.push(dimension); │ │ │ │ - }, │ │ │ │ - "Default": function(node, obj) { │ │ │ │ - obj["default"] = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Value": function(node, obj) { │ │ │ │ - obj.values.push(this.getChildValue(node)); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.NavToolbar │ │ │ │ + * Add our two mousedefaults controls. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be used │ │ │ │ + * to extend the control. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); │ │ │ │ + this.addControls([ │ │ │ │ + new OpenLayers.Control.Navigation(), │ │ │ │ + new OpenLayers.Control.ZoomBox() │ │ │ │ + ]); │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMTSCapabilities.v1_0_0" │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * calls the default draw, and then activates mouse defaults. │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments); │ │ │ │ + if (this.defaultControl === null) { │ │ │ │ + this.defaultControl = this.controls[0]; │ │ │ │ + } │ │ │ │ + return div; │ │ │ │ + }, │ │ │ │ │ │ │ │ - }); │ │ │ │ + CLASS_NAME: "OpenLayers.Control.NavToolbar" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/WMC/v1.js │ │ │ │ + OpenLayers/Control/Geolocate.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/WMC.js │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Geometry/Point.js │ │ │ │ + * @requires OpenLayers/Projection.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.WMC.v1 │ │ │ │ - * Superclass for WMC version 1 parsers. │ │ │ │ + * Class: OpenLayers.Control.Geolocate │ │ │ │ + * The Geolocate control wraps w3c geolocation API into control that can be │ │ │ │ + * bound to a map, and generate events on location update │ │ │ │ + * │ │ │ │ + * To use this control requires to load the proj4js library if the projection │ │ │ │ + * of the map is not EPSG:4326 or EPSG:900913. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.WMC.v1 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ +OpenLayers.Control.Geolocate = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * locationupdated - Triggered when browser return a new position. Listeners will │ │ │ │ + * receive an object with a 'position' property which is the browser.geolocation.position │ │ │ │ + * native object, as well as a 'point' property which is the location transformed in the │ │ │ │ + * current map projection. │ │ │ │ + * locationfailed - Triggered when geolocation has failed │ │ │ │ + * locationuncapable - Triggered when control is activated on a browser │ │ │ │ + * which doesn't support geolocation │ │ │ │ */ │ │ │ │ - namespaces: { │ │ │ │ - ol: "http://openlayers.org/context", │ │ │ │ - wmc: "http://www.opengis.net/context", │ │ │ │ - sld: "http://www.opengis.net/sld", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} Schema location for a particular minor version. │ │ │ │ + * Property: geolocation │ │ │ │ + * {Object} The geolocation engine, as a property to be possibly mocked. │ │ │ │ + * This is set lazily to avoid a memory leak in IE9. │ │ │ │ */ │ │ │ │ - schemaLocation: "", │ │ │ │ + geolocation: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getNamespacePrefix │ │ │ │ - * Get the namespace prefix for a given uri from the <namespaces> object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A namespace prefix or null if none found. │ │ │ │ + * Property: available │ │ │ │ + * {Boolean} The navigator.geolocation object is available. │ │ │ │ */ │ │ │ │ - getNamespacePrefix: function(uri) { │ │ │ │ - var prefix = null; │ │ │ │ - if (uri == null) { │ │ │ │ - prefix = this.namespaces[this.defaultPrefix]; │ │ │ │ - } else { │ │ │ │ - for (prefix in this.namespaces) { │ │ │ │ - if (this.namespaces[prefix] == uri) { │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return prefix; │ │ │ │ - }, │ │ │ │ + available: ('geolocation' in navigator), │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ + * APIProperty: bind │ │ │ │ + * {Boolean} If true, map center will be set on location update. │ │ │ │ */ │ │ │ │ - defaultPrefix: "wmc", │ │ │ │ + bind: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: rootPrefix │ │ │ │ - * {String} Prefix on the root node that maps to the context namespace URI. │ │ │ │ + * APIProperty: watch │ │ │ │ + * {Boolean} If true, position will be update regularly. │ │ │ │ */ │ │ │ │ - rootPrefix: null, │ │ │ │ + watch: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: defaultStyleName │ │ │ │ - * {String} Style name used if layer has no style param. Default is "". │ │ │ │ + * APIProperty: geolocationOptions │ │ │ │ + * {Object} Options to pass to the navigator's geolocation API. See │ │ │ │ + * <http://dev.w3.org/geo/api/spec-source.html>. No specific │ │ │ │ + * option is passed to the geolocation API by default. │ │ │ │ */ │ │ │ │ - defaultStyleName: "", │ │ │ │ + geolocationOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: defaultStyleTitle │ │ │ │ - * {String} Default style title. Default is "Default". │ │ │ │ + * Constructor: OpenLayers.Control.Geolocate │ │ │ │ + * Create a new control to deal with browser geolocation API │ │ │ │ + * │ │ │ │ */ │ │ │ │ - defaultStyleTitle: "Default", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.WMC.v1 │ │ │ │ - * Instances of this class are not created directly. Use the │ │ │ │ - * <OpenLayers.Format.WMC> constructor instead. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ + destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read │ │ │ │ - * Read capabilities data from a string, and return a list of layers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * Method: activate │ │ │ │ + * Activates the control. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array} List of named layers. │ │ │ │ + * {Boolean} The control was effectively activated. │ │ │ │ */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + activate: function() { │ │ │ │ + if (this.available && !this.geolocation) { │ │ │ │ + // set lazily to avoid IE9 memory leak │ │ │ │ + this.geolocation = navigator.geolocation; │ │ │ │ } │ │ │ │ - var root = data.documentElement; │ │ │ │ - this.rootPrefix = root.prefix; │ │ │ │ - var context = { │ │ │ │ - version: root.getAttribute("version") │ │ │ │ - }; │ │ │ │ - this.runChildNodes(context, root); │ │ │ │ - return context; │ │ │ │ + if (!this.geolocation) { │ │ │ │ + this.events.triggerEvent("locationuncapable"); │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ + if (this.watch) { │ │ │ │ + this.watchId = this.geolocation.watchPosition( │ │ │ │ + OpenLayers.Function.bind(this.geolocate, this), │ │ │ │ + OpenLayers.Function.bind(this.failure, this), │ │ │ │ + this.geolocationOptions │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + this.getCurrentLocation(); │ │ │ │ + } │ │ │ │ + return true; │ │ │ │ + } │ │ │ │ + return false; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: runChildNodes │ │ │ │ + * Method: deactivate │ │ │ │ + * Deactivates the control. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The control was effectively deactivated. │ │ │ │ */ │ │ │ │ - runChildNodes: function(obj, node) { │ │ │ │ - var children = node.childNodes; │ │ │ │ - var childNode, processor, prefix, local; │ │ │ │ - for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ - childNode = children[i]; │ │ │ │ - if (childNode.nodeType == 1) { │ │ │ │ - prefix = this.getNamespacePrefix(childNode.namespaceURI); │ │ │ │ - local = childNode.nodeName.split(":").pop(); │ │ │ │ - processor = this["read_" + prefix + "_" + local]; │ │ │ │ - if (processor) { │ │ │ │ - processor.apply(this, [obj, childNode]); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.active && this.watchId !== null) { │ │ │ │ + this.geolocation.clearWatch(this.watchId); │ │ │ │ } │ │ │ │ + return OpenLayers.Control.prototype.deactivate.apply( │ │ │ │ + this, arguments │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_General │ │ │ │ + * Method: geolocate │ │ │ │ + * Activates the control. │ │ │ │ + * │ │ │ │ */ │ │ │ │ - read_wmc_General: function(context, node) { │ │ │ │ - this.runChildNodes(context, node); │ │ │ │ + geolocate: function(position) { │ │ │ │ + var center = new OpenLayers.LonLat( │ │ │ │ + position.coords.longitude, │ │ │ │ + position.coords.latitude │ │ │ │ + ).transform( │ │ │ │ + new OpenLayers.Projection("EPSG:4326"), │ │ │ │ + this.map.getProjectionObject() │ │ │ │ + ); │ │ │ │ + if (this.bind) { │ │ │ │ + this.map.setCenter(center); │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("locationupdated", { │ │ │ │ + position: position, │ │ │ │ + point: new OpenLayers.Geometry.Point( │ │ │ │ + center.lon, center.lat │ │ │ │ + ) │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_BoundingBox │ │ │ │ + * APIMethod: getCurrentLocation │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Returns true if a event will be fired (successfull │ │ │ │ + * registration) │ │ │ │ */ │ │ │ │ - read_wmc_BoundingBox: function(context, node) { │ │ │ │ - context.projection = node.getAttribute("SRS"); │ │ │ │ - context.bounds = new OpenLayers.Bounds( │ │ │ │ - node.getAttribute("minx"), node.getAttribute("miny"), │ │ │ │ - node.getAttribute("maxx"), node.getAttribute("maxy") │ │ │ │ + getCurrentLocation: function() { │ │ │ │ + if (!this.active || this.watch) { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + this.geolocation.getCurrentPosition( │ │ │ │ + OpenLayers.Function.bind(this.geolocate, this), │ │ │ │ + OpenLayers.Function.bind(this.failure, this), │ │ │ │ + this.geolocationOptions │ │ │ │ ); │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_LayerList │ │ │ │ + * Method: failure │ │ │ │ + * method called on browser's geolocation failure │ │ │ │ + * │ │ │ │ */ │ │ │ │ - read_wmc_LayerList: function(context, node) { │ │ │ │ - // layersContext is an array containing info for each layer │ │ │ │ - context.layersContext = []; │ │ │ │ - this.runChildNodes(context, node); │ │ │ │ + failure: function(error) { │ │ │ │ + this.events.triggerEvent("locationfailed", { │ │ │ │ + error: error │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: read_wmc_Layer │ │ │ │ - */ │ │ │ │ - read_wmc_Layer: function(context, node) { │ │ │ │ - var layerContext = { │ │ │ │ - visibility: (node.getAttribute("hidden") != "1"), │ │ │ │ - queryable: (node.getAttribute("queryable") == "1"), │ │ │ │ - formats: [], │ │ │ │ - styles: [], │ │ │ │ - metadata: {} │ │ │ │ - }; │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Geolocate" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/DragFeature.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - this.runChildNodes(layerContext, node); │ │ │ │ - // set properties common to multiple objects on layer options/params │ │ │ │ - context.layersContext.push(layerContext); │ │ │ │ - }, │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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: read_wmc_Extension │ │ │ │ - */ │ │ │ │ - read_wmc_Extension: function(obj, node) { │ │ │ │ - this.runChildNodes(obj, node); │ │ │ │ - }, │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Handler/Drag.js │ │ │ │ + * @requires OpenLayers/Handler/Feature.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.DragFeature │ │ │ │ + * The DragFeature control moves a feature with a drag of the mouse. Create a │ │ │ │ + * new control with the <OpenLayers.Control.DragFeature> constructor. │ │ │ │ + * │ │ │ │ + * Inherits From: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_ol_units │ │ │ │ + * APIProperty: geometryTypes │ │ │ │ + * {Array(String)} To restrict dragging to a limited set of geometry types, │ │ │ │ + * send a list of strings corresponding to the geometry class names. │ │ │ │ */ │ │ │ │ - read_ol_units: function(layerContext, node) { │ │ │ │ - layerContext.units = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ + geometryTypes: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_ol_maxExtent │ │ │ │ + * APIProperty: onStart │ │ │ │ + * {Function} Define this function if you want to know when a drag starts. │ │ │ │ + * The function should expect to receive two arguments: the feature │ │ │ │ + * that is about to be dragged and the pixel location of the mouse. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The feature that is about to be │ │ │ │ + * dragged. │ │ │ │ + * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse. │ │ │ │ */ │ │ │ │ - read_ol_maxExtent: function(obj, node) { │ │ │ │ - var bounds = new OpenLayers.Bounds( │ │ │ │ - node.getAttribute("minx"), node.getAttribute("miny"), │ │ │ │ - node.getAttribute("maxx"), node.getAttribute("maxy") │ │ │ │ - ); │ │ │ │ - obj.maxExtent = bounds; │ │ │ │ - }, │ │ │ │ + onStart: function(feature, pixel) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_ol_transparent │ │ │ │ + * APIProperty: onDrag │ │ │ │ + * {Function} Define this function if you want to know about each move of a │ │ │ │ + * feature. The function should expect to receive two arguments: the │ │ │ │ + * feature that is being dragged and the pixel location of the mouse. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged. │ │ │ │ + * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse. │ │ │ │ */ │ │ │ │ - read_ol_transparent: function(layerContext, node) { │ │ │ │ - layerContext.transparent = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ + onDrag: function(feature, pixel) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_ol_numZoomLevels │ │ │ │ + * APIProperty: onComplete │ │ │ │ + * {Function} Define this function if you want to know when a feature is │ │ │ │ + * done dragging. The function should expect to receive two arguments: │ │ │ │ + * the feature that is being dragged and the pixel location of the │ │ │ │ + * mouse. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged. │ │ │ │ + * pixel - {<OpenLayers.Pixel>} The pixel location of the mouse. │ │ │ │ */ │ │ │ │ - read_ol_numZoomLevels: function(layerContext, node) { │ │ │ │ - layerContext.numZoomLevels = parseInt(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ + onComplete: function(feature, pixel) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_ol_opacity │ │ │ │ + * APIProperty: onEnter │ │ │ │ + * {Function} Define this function if you want to know when the mouse │ │ │ │ + * goes over a feature and thereby makes this feature a candidate │ │ │ │ + * for dragging. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The feature that is ready │ │ │ │ + * to be dragged. │ │ │ │ */ │ │ │ │ - read_ol_opacity: function(layerContext, node) { │ │ │ │ - layerContext.opacity = parseFloat(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ + onEnter: function(feature) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_ol_singleTile │ │ │ │ + * APIProperty: onLeave │ │ │ │ + * {Function} Define this function if you want to know when the mouse │ │ │ │ + * goes out of the feature that was dragged. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The feature that was dragged. │ │ │ │ */ │ │ │ │ - read_ol_singleTile: function(layerContext, node) { │ │ │ │ - layerContext.singleTile = (this.getChildValue(node) == "true"); │ │ │ │ - }, │ │ │ │ + onLeave: function(feature) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_ol_tileSize │ │ │ │ + * APIProperty: documentDrag │ │ │ │ + * {Boolean} If set to true, mouse dragging will continue even if the │ │ │ │ + * mouse cursor leaves the map viewport. Default is false. │ │ │ │ */ │ │ │ │ - read_ol_tileSize: function(layerContext, node) { │ │ │ │ - var obj = { │ │ │ │ - "width": node.getAttribute("width"), │ │ │ │ - "height": node.getAttribute("height") │ │ │ │ - }; │ │ │ │ - layerContext.tileSize = obj; │ │ │ │ - }, │ │ │ │ + documentDrag: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_ol_isBaseLayer │ │ │ │ + * Property: layer │ │ │ │ + * {<OpenLayers.Layer.Vector>} │ │ │ │ */ │ │ │ │ - read_ol_isBaseLayer: function(layerContext, node) { │ │ │ │ - layerContext.isBaseLayer = (this.getChildValue(node) == "true"); │ │ │ │ - }, │ │ │ │ + layer: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_ol_displayInLayerSwitcher │ │ │ │ + * Property: feature │ │ │ │ + * {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - read_ol_displayInLayerSwitcher: function(layerContext, node) { │ │ │ │ - layerContext.displayInLayerSwitcher = (this.getChildValue(node) == "true"); │ │ │ │ - }, │ │ │ │ + feature: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_Server │ │ │ │ + * Property: dragCallbacks │ │ │ │ + * {Object} The functions that are sent to the drag handler for callback. │ │ │ │ */ │ │ │ │ - read_wmc_Server: function(layerContext, node) { │ │ │ │ - layerContext.version = node.getAttribute("version"); │ │ │ │ - layerContext.url = this.getOnlineResource_href(node); │ │ │ │ - layerContext.metadata.servertitle = node.getAttribute("title"); │ │ │ │ - }, │ │ │ │ + dragCallbacks: {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_FormatList │ │ │ │ + * Property: featureCallbacks │ │ │ │ + * {Object} The functions that are sent to the feature handler for callback. │ │ │ │ */ │ │ │ │ - read_wmc_FormatList: function(layerContext, node) { │ │ │ │ - this.runChildNodes(layerContext, node); │ │ │ │ - }, │ │ │ │ + featureCallbacks: {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_Format │ │ │ │ + * Property: lastPixel │ │ │ │ + * {<OpenLayers.Pixel>} │ │ │ │ */ │ │ │ │ - read_wmc_Format: function(layerContext, node) { │ │ │ │ - var format = { │ │ │ │ - value: this.getChildValue(node) │ │ │ │ - }; │ │ │ │ - if (node.getAttribute("current") == "1") { │ │ │ │ - format.current = true; │ │ │ │ - } │ │ │ │ - layerContext.formats.push(format); │ │ │ │ - }, │ │ │ │ + lastPixel: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_StyleList │ │ │ │ + * Constructor: OpenLayers.Control.DragFeature │ │ │ │ + * Create a new control to drag features. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} The layer containing features to be │ │ │ │ + * dragged. │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * control. │ │ │ │ */ │ │ │ │ - read_wmc_StyleList: function(layerContext, node) { │ │ │ │ - this.runChildNodes(layerContext, node); │ │ │ │ + initialize: function(layer, options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + this.layer = layer; │ │ │ │ + this.handlers = { │ │ │ │ + drag: new OpenLayers.Handler.Drag( │ │ │ │ + this, OpenLayers.Util.extend({ │ │ │ │ + down: this.downFeature, │ │ │ │ + move: this.moveFeature, │ │ │ │ + up: this.upFeature, │ │ │ │ + out: this.cancel, │ │ │ │ + done: this.doneDragging │ │ │ │ + }, this.dragCallbacks), { │ │ │ │ + documentDrag: this.documentDrag │ │ │ │ + } │ │ │ │ + ), │ │ │ │ + feature: new OpenLayers.Handler.Feature( │ │ │ │ + this, this.layer, OpenLayers.Util.extend({ │ │ │ │ + // 'click' and 'clickout' callback are for the mobile │ │ │ │ + // support: no 'over' or 'out' in touch based browsers. │ │ │ │ + click: this.clickFeature, │ │ │ │ + clickout: this.clickoutFeature, │ │ │ │ + over: this.overFeature, │ │ │ │ + out: this.outFeature │ │ │ │ + }, this.featureCallbacks), { │ │ │ │ + geometryTypes: this.geometryTypes │ │ │ │ + } │ │ │ │ + ) │ │ │ │ + }; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_Style │ │ │ │ + * Method: clickFeature │ │ │ │ + * Called when the feature handler detects a click-in on a feature. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - read_wmc_Style: function(layerContext, node) { │ │ │ │ - var style = {}; │ │ │ │ - this.runChildNodes(style, node); │ │ │ │ - if (node.getAttribute("current") == "1") { │ │ │ │ - style.current = true; │ │ │ │ + clickFeature: function(feature) { │ │ │ │ + if (this.handlers.feature.touch && !this.over && this.overFeature(feature)) { │ │ │ │ + this.handlers.drag.dragstart(this.handlers.feature.evt); │ │ │ │ + // to let the events propagate to the feature handler (click callback) │ │ │ │ + this.handlers.drag.stopDown = false; │ │ │ │ } │ │ │ │ - layerContext.styles.push(style); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_SLD │ │ │ │ + * Method: clickoutFeature │ │ │ │ + * Called when the feature handler detects a click-out on a feature. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - read_wmc_SLD: function(style, node) { │ │ │ │ - this.runChildNodes(style, node); │ │ │ │ - // style either comes back with an href or a body property │ │ │ │ + clickoutFeature: function(feature) { │ │ │ │ + if (this.handlers.feature.touch && this.over) { │ │ │ │ + this.outFeature(feature); │ │ │ │ + this.handlers.drag.stopDown = true; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_sld_StyledLayerDescriptor │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Take care of things that are not handled in superclass │ │ │ │ */ │ │ │ │ - read_sld_StyledLayerDescriptor: function(sld, node) { │ │ │ │ - var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ - sld.body = xml; │ │ │ │ + destroy: function() { │ │ │ │ + this.layer = null; │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, []); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_sld_FeatureTypeStyle │ │ │ │ + * APIMethod: activate │ │ │ │ + * Activate the control and the feature handler. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Successfully activated the control and feature handler. │ │ │ │ */ │ │ │ │ - read_sld_FeatureTypeStyle: function(sld, node) { │ │ │ │ - var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ - sld.body = xml; │ │ │ │ + activate: function() { │ │ │ │ + return (this.handlers.feature.activate() && │ │ │ │ + OpenLayers.Control.prototype.activate.apply(this, arguments)); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_OnlineResource │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivate the control and all handlers. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Successfully deactivated the control. │ │ │ │ */ │ │ │ │ - read_wmc_OnlineResource: function(obj, node) { │ │ │ │ - obj.href = this.getAttributeNS( │ │ │ │ - node, this.namespaces.xlink, "href" │ │ │ │ + deactivate: function() { │ │ │ │ + // the return from the handlers is unimportant in this case │ │ │ │ + this.handlers.drag.deactivate(); │ │ │ │ + this.handlers.feature.deactivate(); │ │ │ │ + this.feature = null; │ │ │ │ + this.dragging = false; │ │ │ │ + this.lastPixel = null; │ │ │ │ + OpenLayers.Element.removeClass( │ │ │ │ + this.map.viewPortDiv, this.displayClass + "Over" │ │ │ │ ); │ │ │ │ + return OpenLayers.Control.prototype.deactivate.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_Name │ │ │ │ + * Method: overFeature │ │ │ │ + * Called when the feature handler detects a mouse-over on a feature. │ │ │ │ + * This activates the drag handler. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The selected feature. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Successfully activated the drag handler. │ │ │ │ */ │ │ │ │ - read_wmc_Name: function(obj, node) { │ │ │ │ - var name = this.getChildValue(node); │ │ │ │ - if (name) { │ │ │ │ - obj.name = name; │ │ │ │ + overFeature: function(feature) { │ │ │ │ + var activated = false; │ │ │ │ + if (!this.handlers.drag.dragging) { │ │ │ │ + this.feature = feature; │ │ │ │ + this.handlers.drag.activate(); │ │ │ │ + activated = true; │ │ │ │ + this.over = true; │ │ │ │ + OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + "Over"); │ │ │ │ + this.onEnter(feature); │ │ │ │ + } else { │ │ │ │ + if (this.feature.id == feature.id) { │ │ │ │ + this.over = true; │ │ │ │ + } else { │ │ │ │ + this.over = false; │ │ │ │ + } │ │ │ │ } │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_Title │ │ │ │ + * Method: downFeature │ │ │ │ + * Called when the drag handler detects a mouse-down. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * pixel - {<OpenLayers.Pixel>} Location of the mouse event. │ │ │ │ */ │ │ │ │ - read_wmc_Title: function(obj, node) { │ │ │ │ - var title = this.getChildValue(node); │ │ │ │ - if (title) { │ │ │ │ - obj.title = title; │ │ │ │ - } │ │ │ │ + downFeature: function(pixel) { │ │ │ │ + this.lastPixel = pixel; │ │ │ │ + this.onStart(this.feature, pixel); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_MetadataURL │ │ │ │ + * Method: moveFeature │ │ │ │ + * Called when the drag handler detects a mouse-move. Also calls the │ │ │ │ + * optional onDrag method. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * pixel - {<OpenLayers.Pixel>} Location of the mouse event. │ │ │ │ */ │ │ │ │ - read_wmc_MetadataURL: function(layerContext, node) { │ │ │ │ - layerContext.metadataURL = this.getOnlineResource_href(node); │ │ │ │ + moveFeature: function(pixel) { │ │ │ │ + var res = this.map.getResolution(); │ │ │ │ + this.feature.geometry.move(res * (pixel.x - this.lastPixel.x), │ │ │ │ + res * (this.lastPixel.y - pixel.y)); │ │ │ │ + this.layer.drawFeature(this.feature); │ │ │ │ + this.lastPixel = pixel; │ │ │ │ + this.onDrag(this.feature, pixel); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_KeywordList │ │ │ │ + * Method: upFeature │ │ │ │ + * Called when the drag handler detects a mouse-up. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * pixel - {<OpenLayers.Pixel>} Location of the mouse event. │ │ │ │ */ │ │ │ │ - read_wmc_KeywordList: function(context, node) { │ │ │ │ - context.keywords = []; │ │ │ │ - this.runChildNodes(context.keywords, node); │ │ │ │ + upFeature: function(pixel) { │ │ │ │ + if (!this.over) { │ │ │ │ + this.handlers.drag.deactivate(); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_Keyword │ │ │ │ + * Method: doneDragging │ │ │ │ + * Called when the drag handler is done dragging. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * pixel - {<OpenLayers.Pixel>} The last event pixel location. If this event │ │ │ │ + * came from a mouseout, this may not be in the map viewport. │ │ │ │ */ │ │ │ │ - read_wmc_Keyword: function(keywords, node) { │ │ │ │ - keywords.push(this.getChildValue(node)); │ │ │ │ + doneDragging: function(pixel) { │ │ │ │ + this.onComplete(this.feature, pixel); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_Abstract │ │ │ │ + * Method: outFeature │ │ │ │ + * Called when the feature handler detects a mouse-out on a feature. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The feature that the mouse left. │ │ │ │ */ │ │ │ │ - read_wmc_Abstract: function(obj, node) { │ │ │ │ - var abst = this.getChildValue(node); │ │ │ │ - if (abst) { │ │ │ │ - obj["abstract"] = abst; │ │ │ │ + outFeature: function(feature) { │ │ │ │ + if (!this.handlers.drag.dragging) { │ │ │ │ + this.over = false; │ │ │ │ + this.handlers.drag.deactivate(); │ │ │ │ + OpenLayers.Element.removeClass( │ │ │ │ + this.map.viewPortDiv, this.displayClass + "Over" │ │ │ │ + ); │ │ │ │ + this.onLeave(feature); │ │ │ │ + this.feature = null; │ │ │ │ + } else { │ │ │ │ + if (this.feature.id == feature.id) { │ │ │ │ + this.over = false; │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_LogoURL │ │ │ │ + * Method: cancel │ │ │ │ + * Called when the drag handler detects a mouse-out (from the map viewport). │ │ │ │ */ │ │ │ │ - read_wmc_LogoURL: function(context, node) { │ │ │ │ - context.logo = { │ │ │ │ - width: node.getAttribute("width"), │ │ │ │ - height: node.getAttribute("height"), │ │ │ │ - format: node.getAttribute("format"), │ │ │ │ - href: this.getOnlineResource_href(node) │ │ │ │ - }; │ │ │ │ + cancel: function() { │ │ │ │ + this.handlers.drag.deactivate(); │ │ │ │ + this.over = false; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_DescriptionURL │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the control and all handlers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} The control's map. │ │ │ │ */ │ │ │ │ - read_wmc_DescriptionURL: function(context, node) { │ │ │ │ - context.descriptionURL = this.getOnlineResource_href(node); │ │ │ │ + setMap: function(map) { │ │ │ │ + this.handlers.drag.setMap(map); │ │ │ │ + this.handlers.feature.setMap(map); │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: read_wmc_ContactInformation │ │ │ │ + CLASS_NAME: "OpenLayers.Control.DragFeature" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/PanPanel.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control/Panel.js │ │ │ │ + * @requires OpenLayers/Control/Pan.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.PanPanel │ │ │ │ + * The PanPanel is visible control for panning the map North, South, East or │ │ │ │ + * West in small steps. By default it is drawn in the top left corner of the │ │ │ │ + * map. │ │ │ │ + * │ │ │ │ + * Note: │ │ │ │ + * If you wish to use this class with the default images and you want │ │ │ │ + * it to look nice in ie6, you should add the following, conditionally │ │ │ │ + * added css stylesheet to your HTML file: │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * <!--[if lte IE 6]> │ │ │ │ + * <link rel="stylesheet" href="../theme/default/ie6-style.css" type="text/css" /> │ │ │ │ + * <![endif]--> │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control.Panel> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.PanPanel = OpenLayers.Class(OpenLayers.Control.Panel, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: slideFactor │ │ │ │ + * {Integer} Number of pixels by which we'll pan the map in any direction │ │ │ │ + * on clicking the arrow buttons, defaults to 50. If you want to pan │ │ │ │ + * by some ratio of the map dimensions, use <slideRatio> instead. │ │ │ │ */ │ │ │ │ - read_wmc_ContactInformation: function(obj, node) { │ │ │ │ - var contact = {}; │ │ │ │ - this.runChildNodes(contact, node); │ │ │ │ - obj.contactInformation = contact; │ │ │ │ - }, │ │ │ │ + slideFactor: 50, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: read_wmc_ContactPersonPrimary │ │ │ │ + /** │ │ │ │ + * APIProperty: slideRatio │ │ │ │ + * {Number} The fraction of map width/height by which we'll pan the map │ │ │ │ + * on clicking the arrow buttons. Default is null. If set, will │ │ │ │ + * override <slideFactor>. E.g. if slideRatio is .5, then Pan Up will │ │ │ │ + * pan up half the map height. │ │ │ │ */ │ │ │ │ - read_wmc_ContactPersonPrimary: function(contact, node) { │ │ │ │ - var personPrimary = {}; │ │ │ │ - this.runChildNodes(personPrimary, node); │ │ │ │ - contact.personPrimary = personPrimary; │ │ │ │ - }, │ │ │ │ + slideRatio: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_ContactPerson │ │ │ │ + * Constructor: OpenLayers.Control.PanPanel │ │ │ │ + * Add the four directional pan buttons. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be used │ │ │ │ + * to extend the control. │ │ │ │ */ │ │ │ │ - read_wmc_ContactPerson: function(primaryPerson, node) { │ │ │ │ - var person = this.getChildValue(node); │ │ │ │ - if (person) { │ │ │ │ - primaryPerson.person = person; │ │ │ │ - } │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); │ │ │ │ + var options = { │ │ │ │ + slideFactor: this.slideFactor, │ │ │ │ + slideRatio: this.slideRatio │ │ │ │ + }; │ │ │ │ + this.addControls([ │ │ │ │ + new OpenLayers.Control.Pan(OpenLayers.Control.Pan.NORTH, options), │ │ │ │ + new OpenLayers.Control.Pan(OpenLayers.Control.Pan.SOUTH, options), │ │ │ │ + new OpenLayers.Control.Pan(OpenLayers.Control.Pan.EAST, options), │ │ │ │ + new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST, options) │ │ │ │ + ]); │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Control.PanPanel" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/OverviewMap.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/BaseTypes.js │ │ │ │ + * @requires OpenLayers/Events/buttonclick.js │ │ │ │ + * @requires OpenLayers/Map.js │ │ │ │ + * @requires OpenLayers/Handler/Click.js │ │ │ │ + * @requires OpenLayers/Handler/Drag.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.OverviewMap │ │ │ │ + * The OverMap control creates a small overview map, useful to display the │ │ │ │ + * extent of a zoomed map and your main map and provide additional │ │ │ │ + * navigation options to the User. By default the overview map is drawn in │ │ │ │ + * the lower right corner of the main map. Create a new overview map with the │ │ │ │ + * <OpenLayers.Control.OverviewMap> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: read_wmc_ContactOrganization │ │ │ │ + * Property: element │ │ │ │ + * {DOMElement} The DOM element that contains the overview map │ │ │ │ */ │ │ │ │ - read_wmc_ContactOrganization: function(primaryPerson, node) { │ │ │ │ - var organization = this.getChildValue(node); │ │ │ │ - if (organization) { │ │ │ │ - primaryPerson.organization = organization; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + element: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_ContactPosition │ │ │ │ + * APIProperty: ovmap │ │ │ │ + * {<OpenLayers.Map>} A reference to the overview map itself. │ │ │ │ */ │ │ │ │ - read_wmc_ContactPosition: function(contact, node) { │ │ │ │ - var position = this.getChildValue(node); │ │ │ │ - if (position) { │ │ │ │ - contact.position = position; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + ovmap: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_ContactAddress │ │ │ │ + * APIProperty: size │ │ │ │ + * {<OpenLayers.Size>} The overvew map size in pixels. Note that this is │ │ │ │ + * the size of the map itself - the element that contains the map (default │ │ │ │ + * class name olControlOverviewMapElement) may have padding or other style │ │ │ │ + * attributes added via CSS. │ │ │ │ */ │ │ │ │ - read_wmc_ContactAddress: function(contact, node) { │ │ │ │ - var contactAddress = {}; │ │ │ │ - this.runChildNodes(contactAddress, node); │ │ │ │ - contact.contactAddress = contactAddress; │ │ │ │ + size: { │ │ │ │ + w: 180, │ │ │ │ + h: 90 │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_AddressType │ │ │ │ + * APIProperty: layers │ │ │ │ + * {Array(<OpenLayers.Layer>)} Ordered list of layers in the overview map. │ │ │ │ + * If none are sent at construction, the base layer for the main map is used. │ │ │ │ */ │ │ │ │ - read_wmc_AddressType: function(contactAddress, node) { │ │ │ │ - var type = this.getChildValue(node); │ │ │ │ - if (type) { │ │ │ │ - contactAddress.type = type; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + layers: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_Address │ │ │ │ + * APIProperty: minRectSize │ │ │ │ + * {Integer} The minimum width or height (in pixels) of the extent │ │ │ │ + * rectangle on the overview map. When the extent rectangle reaches │ │ │ │ + * this size, it will be replaced depending on the value of the │ │ │ │ + * <minRectDisplayClass> property. Default is 15 pixels. │ │ │ │ */ │ │ │ │ - read_wmc_Address: function(contactAddress, node) { │ │ │ │ - var address = this.getChildValue(node); │ │ │ │ - if (address) { │ │ │ │ - contactAddress.address = address; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + minRectSize: 15, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_City │ │ │ │ + * APIProperty: minRectDisplayClass │ │ │ │ + * {String} Replacement style class name for the extent rectangle when │ │ │ │ + * <minRectSize> is reached. This string will be suffixed on to the │ │ │ │ + * displayClass. Default is "RectReplacement". │ │ │ │ + * │ │ │ │ + * Example CSS declaration: │ │ │ │ + * (code) │ │ │ │ + * .olControlOverviewMapRectReplacement { │ │ │ │ + * overflow: hidden; │ │ │ │ + * cursor: move; │ │ │ │ + * background-image: url("img/overview_replacement.gif"); │ │ │ │ + * background-repeat: no-repeat; │ │ │ │ + * background-position: center; │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ */ │ │ │ │ - read_wmc_City: function(contactAddress, node) { │ │ │ │ - var city = this.getChildValue(node); │ │ │ │ - if (city) { │ │ │ │ - contactAddress.city = city; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + minRectDisplayClass: "RectReplacement", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_StateOrProvince │ │ │ │ + * APIProperty: minRatio │ │ │ │ + * {Float} The ratio of the overview map resolution to the main map │ │ │ │ + * resolution at which to zoom farther out on the overview map. │ │ │ │ */ │ │ │ │ - read_wmc_StateOrProvince: function(contactAddress, node) { │ │ │ │ - var stateOrProvince = this.getChildValue(node); │ │ │ │ - if (stateOrProvince) { │ │ │ │ - contactAddress.stateOrProvince = stateOrProvince; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + minRatio: 8, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_PostCode │ │ │ │ + * APIProperty: maxRatio │ │ │ │ + * {Float} The ratio of the overview map resolution to the main map │ │ │ │ + * resolution at which to zoom farther in on the overview map. │ │ │ │ */ │ │ │ │ - read_wmc_PostCode: function(contactAddress, node) { │ │ │ │ - var postcode = this.getChildValue(node); │ │ │ │ - if (postcode) { │ │ │ │ - contactAddress.postcode = postcode; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + maxRatio: 32, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_Country │ │ │ │ + * APIProperty: mapOptions │ │ │ │ + * {Object} An object containing any non-default properties to be sent to │ │ │ │ + * the overview map's map constructor. These should include any │ │ │ │ + * non-default options that the main map was constructed with. │ │ │ │ */ │ │ │ │ - read_wmc_Country: function(contactAddress, node) { │ │ │ │ - var country = this.getChildValue(node); │ │ │ │ - if (country) { │ │ │ │ - contactAddress.country = country; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + mapOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_ContactVoiceTelephone │ │ │ │ + * APIProperty: autoPan │ │ │ │ + * {Boolean} Always pan the overview map, so the extent marker remains in │ │ │ │ + * the center. Default is false. If true, when you drag the extent │ │ │ │ + * marker, the overview map will update itself so the marker returns │ │ │ │ + * to the center. │ │ │ │ */ │ │ │ │ - read_wmc_ContactVoiceTelephone: function(contact, node) { │ │ │ │ - var phone = this.getChildValue(node); │ │ │ │ - if (phone) { │ │ │ │ - contact.phone = phone; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + autoPan: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_ContactFacsimileTelephone │ │ │ │ + * Property: handlers │ │ │ │ + * {Object} │ │ │ │ */ │ │ │ │ - read_wmc_ContactFacsimileTelephone: function(contact, node) { │ │ │ │ - var fax = this.getChildValue(node); │ │ │ │ - if (fax) { │ │ │ │ - contact.fax = fax; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + handlers: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_ContactElectronicMailAddress │ │ │ │ + * Property: resolutionFactor │ │ │ │ + * {Object} │ │ │ │ */ │ │ │ │ - read_wmc_ContactElectronicMailAddress: function(contact, node) { │ │ │ │ - var email = this.getChildValue(node); │ │ │ │ - if (email) { │ │ │ │ - contact.email = email; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + resolutionFactor: 1, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_DataURL │ │ │ │ + * APIProperty: maximized │ │ │ │ + * {Boolean} Start as maximized (visible). Defaults to false. │ │ │ │ */ │ │ │ │ - read_wmc_DataURL: function(layerContext, node) { │ │ │ │ - layerContext.dataURL = this.getOnlineResource_href(node); │ │ │ │ - }, │ │ │ │ + maximized: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_LegendURL │ │ │ │ + * APIProperty: maximizeTitle │ │ │ │ + * {String} This property is used for showing a tooltip over the │ │ │ │ + * maximize div. Defaults to "" (no title). │ │ │ │ */ │ │ │ │ - read_wmc_LegendURL: function(style, node) { │ │ │ │ - var legend = { │ │ │ │ - width: node.getAttribute('width'), │ │ │ │ - height: node.getAttribute('height'), │ │ │ │ - format: node.getAttribute('format'), │ │ │ │ - href: this.getOnlineResource_href(node) │ │ │ │ - }; │ │ │ │ - style.legend = legend; │ │ │ │ - }, │ │ │ │ + maximizeTitle: "", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read_wmc_DimensionList │ │ │ │ + * APIProperty: minimizeTitle │ │ │ │ + * {String} This property is used for showing a tooltip over the │ │ │ │ + * minimize div. Defaults to "" (no title). │ │ │ │ */ │ │ │ │ - read_wmc_DimensionList: function(layerContext, node) { │ │ │ │ - layerContext.dimensions = {}; │ │ │ │ - this.runChildNodes(layerContext.dimensions, node); │ │ │ │ - }, │ │ │ │ + minimizeTitle: "", │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: read_wmc_Dimension │ │ │ │ + * Constructor: OpenLayers.Control.OverviewMap │ │ │ │ + * Create a new overview map │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Properties of this object will be set on the overview │ │ │ │ + * map object. Note, to set options on the map object contained in this │ │ │ │ + * control, set <mapOptions> as one of the options properties. │ │ │ │ */ │ │ │ │ - read_wmc_Dimension: function(dimensions, node) { │ │ │ │ - var name = node.getAttribute("name").toLowerCase(); │ │ │ │ - │ │ │ │ - var dim = { │ │ │ │ - name: name, │ │ │ │ - units: node.getAttribute("units") || "", │ │ │ │ - unitSymbol: node.getAttribute("unitSymbol") || "", │ │ │ │ - userValue: node.getAttribute("userValue") || "", │ │ │ │ - nearestValue: node.getAttribute("nearestValue") === "1", │ │ │ │ - multipleValues: node.getAttribute("multipleValues") === "1", │ │ │ │ - current: node.getAttribute("current") === "1", │ │ │ │ - "default": node.getAttribute("default") || "" │ │ │ │ - }; │ │ │ │ - var values = this.getChildValue(node); │ │ │ │ - dim.values = values.split(","); │ │ │ │ - │ │ │ │ - dimensions[dim.name] = dim; │ │ │ │ + initialize: function(options) { │ │ │ │ + this.layers = []; │ │ │ │ + this.handlers = {}; │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * context - {Object} An object representing the map context. │ │ │ │ - * options - {Object} Optional object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A WMC document string. │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Deconstruct the control │ │ │ │ */ │ │ │ │ - write: function(context, options) { │ │ │ │ - var root = this.createElementDefaultNS("ViewContext"); │ │ │ │ - this.setAttributes(root, { │ │ │ │ - version: this.VERSION, │ │ │ │ - id: (options && typeof options.id == "string") ? │ │ │ │ - options.id : OpenLayers.Util.createUniqueID("OpenLayers_Context_") │ │ │ │ - }); │ │ │ │ + destroy: function() { │ │ │ │ + if (!this.mapDiv) { // we've already been destroyed │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (this.handlers.click) { │ │ │ │ + this.handlers.click.destroy(); │ │ │ │ + } │ │ │ │ + if (this.handlers.drag) { │ │ │ │ + this.handlers.drag.destroy(); │ │ │ │ + } │ │ │ │ │ │ │ │ - // add schemaLocation attribute │ │ │ │ - this.setAttributeNS( │ │ │ │ - root, this.namespaces.xsi, │ │ │ │ - "xsi:schemaLocation", this.schemaLocation │ │ │ │ - ); │ │ │ │ + this.ovmap && this.ovmap.viewPortDiv.removeChild(this.extentRectangle); │ │ │ │ + this.extentRectangle = null; │ │ │ │ │ │ │ │ - // required General element │ │ │ │ - root.appendChild(this.write_wmc_General(context)); │ │ │ │ + if (this.rectEvents) { │ │ │ │ + this.rectEvents.destroy(); │ │ │ │ + this.rectEvents = null; │ │ │ │ + } │ │ │ │ │ │ │ │ - // required LayerList element │ │ │ │ - root.appendChild(this.write_wmc_LayerList(context)); │ │ │ │ + if (this.ovmap) { │ │ │ │ + this.ovmap.destroy(); │ │ │ │ + this.ovmap = null; │ │ │ │ + } │ │ │ │ │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [root]); │ │ │ │ - }, │ │ │ │ + this.element.removeChild(this.mapDiv); │ │ │ │ + this.mapDiv = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: createElementDefaultNS │ │ │ │ - * Shorthand for createElementNS with namespace from <defaultPrefix>. │ │ │ │ - * Can optionally be used to set attributes and a text child value. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} The qualified node name. │ │ │ │ - * childValue - {String} Optional value for text child node. │ │ │ │ - * attributes - {Object} Optional object representing attributes. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Element} An element node. │ │ │ │ - */ │ │ │ │ - createElementDefaultNS: function(name, childValue, attributes) { │ │ │ │ - var node = this.createElementNS( │ │ │ │ - this.namespaces[this.defaultPrefix], │ │ │ │ - name │ │ │ │ - ); │ │ │ │ - if (childValue) { │ │ │ │ - node.appendChild(this.createTextNode(childValue)); │ │ │ │ + this.div.removeChild(this.element); │ │ │ │ + this.element = null; │ │ │ │ + │ │ │ │ + if (this.maximizeDiv) { │ │ │ │ + this.div.removeChild(this.maximizeDiv); │ │ │ │ + this.maximizeDiv = null; │ │ │ │ } │ │ │ │ - if (attributes) { │ │ │ │ - this.setAttributes(node, attributes); │ │ │ │ + │ │ │ │ + if (this.minimizeDiv) { │ │ │ │ + this.div.removeChild(this.minimizeDiv); │ │ │ │ + this.minimizeDiv = null; │ │ │ │ } │ │ │ │ - return node; │ │ │ │ + │ │ │ │ + this.map.events.un({ │ │ │ │ + buttonclick: this.onButtonClick, │ │ │ │ + moveend: this.update, │ │ │ │ + changebaselayer: this.baseLayerDraw, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setAttributes │ │ │ │ - * Set multiple attributes given key value pairs from an object. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {Element} An element node. │ │ │ │ - * obj - {Object} An object whose properties represent attribute names and │ │ │ │ - * values represent attribute values. │ │ │ │ + * Method: draw │ │ │ │ + * Render the control in the browser. │ │ │ │ */ │ │ │ │ - setAttributes: function(node, obj) { │ │ │ │ - var value; │ │ │ │ - for (var name in obj) { │ │ │ │ - value = obj[name].toString(); │ │ │ │ - if (value.match(/[A-Z]/)) { │ │ │ │ - // safari lowercases attributes with setAttribute │ │ │ │ - this.setAttributeNS(node, null, name, value); │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ + if (this.layers.length === 0) { │ │ │ │ + if (this.map.baseLayer) { │ │ │ │ + var layer = this.map.baseLayer.clone(); │ │ │ │ + this.layers = [layer]; │ │ │ │ } else { │ │ │ │ - node.setAttribute(name, value); │ │ │ │ + this.map.events.register("changebaselayer", this, this.baseLayerDraw); │ │ │ │ + return this.div; │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: write_wmc_General │ │ │ │ - * Create a General node given an context object. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * context - {Object} Context object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Element} A WMC General element node. │ │ │ │ - */ │ │ │ │ - write_wmc_General: function(context) { │ │ │ │ - var node = this.createElementDefaultNS("General"); │ │ │ │ │ │ │ │ - // optional Window element │ │ │ │ - if (context.size) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "Window", null, { │ │ │ │ - width: context.size.w, │ │ │ │ - height: context.size.h │ │ │ │ - } │ │ │ │ - )); │ │ │ │ - } │ │ │ │ + // create overview map DOM elements │ │ │ │ + this.element = document.createElement('div'); │ │ │ │ + this.element.className = this.displayClass + 'Element'; │ │ │ │ + this.element.style.display = 'none'; │ │ │ │ │ │ │ │ - // required BoundingBox element │ │ │ │ - var bounds = context.bounds; │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "BoundingBox", null, { │ │ │ │ - minx: bounds.left.toPrecision(18), │ │ │ │ - miny: bounds.bottom.toPrecision(18), │ │ │ │ - maxx: bounds.right.toPrecision(18), │ │ │ │ - maxy: bounds.top.toPrecision(18), │ │ │ │ - SRS: context.projection │ │ │ │ - } │ │ │ │ - )); │ │ │ │ + this.mapDiv = document.createElement('div'); │ │ │ │ + this.mapDiv.style.width = this.size.w + 'px'; │ │ │ │ + this.mapDiv.style.height = this.size.h + 'px'; │ │ │ │ + this.mapDiv.style.position = 'relative'; │ │ │ │ + this.mapDiv.style.overflow = 'hidden'; │ │ │ │ + this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap'); │ │ │ │ │ │ │ │ - // required Title element │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "Title", context.title │ │ │ │ - )); │ │ │ │ + this.extentRectangle = document.createElement('div'); │ │ │ │ + this.extentRectangle.style.position = 'absolute'; │ │ │ │ + this.extentRectangle.style.zIndex = 1000; //HACK │ │ │ │ + this.extentRectangle.className = this.displayClass + 'ExtentRectangle'; │ │ │ │ │ │ │ │ - // optional KeywordList element │ │ │ │ - if (context.keywords) { │ │ │ │ - node.appendChild(this.write_wmc_KeywordList(context.keywords)); │ │ │ │ - } │ │ │ │ + this.element.appendChild(this.mapDiv); │ │ │ │ │ │ │ │ - // optional Abstract element │ │ │ │ - if (context["abstract"]) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "Abstract", context["abstract"] │ │ │ │ - )); │ │ │ │ - } │ │ │ │ + this.div.appendChild(this.element); │ │ │ │ │ │ │ │ - // Optional LogoURL element │ │ │ │ - if (context.logo) { │ │ │ │ - node.appendChild(this.write_wmc_URLType("LogoURL", context.logo.href, context.logo)); │ │ │ │ - } │ │ │ │ + // Optionally add min/max buttons if the control will go in the │ │ │ │ + // map viewport. │ │ │ │ + if (!this.outsideViewport) { │ │ │ │ + this.div.className += " " + this.displayClass + 'Container'; │ │ │ │ + // maximize button div │ │ │ │ + var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png'); │ │ │ │ + this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ + this.displayClass + 'MaximizeButton', │ │ │ │ + null, │ │ │ │ + null, │ │ │ │ + img, │ │ │ │ + 'absolute'); │ │ │ │ + this.maximizeDiv.style.display = 'none'; │ │ │ │ + this.maximizeDiv.className = this.displayClass + 'MaximizeButton olButton'; │ │ │ │ + if (this.maximizeTitle) { │ │ │ │ + this.maximizeDiv.title = this.maximizeTitle; │ │ │ │ + } │ │ │ │ + this.div.appendChild(this.maximizeDiv); │ │ │ │ │ │ │ │ - // Optional DescriptionURL element │ │ │ │ - if (context.descriptionURL) { │ │ │ │ - node.appendChild(this.write_wmc_URLType("DescriptionURL", context.descriptionURL)); │ │ │ │ + // minimize button div │ │ │ │ + var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png'); │ │ │ │ + this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ + 'OpenLayers_Control_minimizeDiv', │ │ │ │ + null, │ │ │ │ + null, │ │ │ │ + img, │ │ │ │ + 'absolute'); │ │ │ │ + this.minimizeDiv.style.display = 'none'; │ │ │ │ + this.minimizeDiv.className = this.displayClass + 'MinimizeButton olButton'; │ │ │ │ + if (this.minimizeTitle) { │ │ │ │ + this.minimizeDiv.title = this.minimizeTitle; │ │ │ │ + } │ │ │ │ + this.div.appendChild(this.minimizeDiv); │ │ │ │ + this.minimizeControl(); │ │ │ │ + } else { │ │ │ │ + // show the overview map │ │ │ │ + this.element.style.display = ''; │ │ │ │ } │ │ │ │ - │ │ │ │ - // Optional ContactInformation element │ │ │ │ - if (context.contactInformation) { │ │ │ │ - node.appendChild(this.write_wmc_ContactInformation(context.contactInformation)); │ │ │ │ + if (this.map.getExtent()) { │ │ │ │ + this.update(); │ │ │ │ } │ │ │ │ │ │ │ │ - // OpenLayers specific map properties │ │ │ │ - node.appendChild(this.write_ol_MapExtension(context)); │ │ │ │ + this.map.events.on({ │ │ │ │ + buttonclick: this.onButtonClick, │ │ │ │ + moveend: this.update, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ │ │ │ │ - return node; │ │ │ │ + if (this.maximized) { │ │ │ │ + this.maximizeControl(); │ │ │ │ + } │ │ │ │ + return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write_wmc_KeywordList │ │ │ │ + * Method: baseLayerDraw │ │ │ │ + * Draw the base layer - called if unable to complete in the initial draw │ │ │ │ */ │ │ │ │ - write_wmc_KeywordList: function(keywords) { │ │ │ │ - var node = this.createElementDefaultNS("KeywordList"); │ │ │ │ + baseLayerDraw: function() { │ │ │ │ + this.draw(); │ │ │ │ + this.map.events.unregister("changebaselayer", this, this.baseLayerDraw); │ │ │ │ + }, │ │ │ │ │ │ │ │ - for (var i = 0, len = keywords.length; i < len; i++) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "Keyword", keywords[i] │ │ │ │ - )); │ │ │ │ + /** │ │ │ │ + * Method: rectDrag │ │ │ │ + * Handle extent rectangle drag │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * px - {<OpenLayers.Pixel>} The pixel location of the drag. │ │ │ │ + */ │ │ │ │ + rectDrag: function(px) { │ │ │ │ + var deltaX = this.handlers.drag.last.x - px.x; │ │ │ │ + var deltaY = this.handlers.drag.last.y - px.y; │ │ │ │ + if (deltaX != 0 || deltaY != 0) { │ │ │ │ + var rectTop = this.rectPxBounds.top; │ │ │ │ + var rectLeft = this.rectPxBounds.left; │ │ │ │ + var rectHeight = Math.abs(this.rectPxBounds.getHeight()); │ │ │ │ + var rectWidth = this.rectPxBounds.getWidth(); │ │ │ │ + // don't allow dragging off of parent element │ │ │ │ + var newTop = Math.max(0, (rectTop - deltaY)); │ │ │ │ + newTop = Math.min(newTop, │ │ │ │ + this.ovmap.size.h - this.hComp - rectHeight); │ │ │ │ + var newLeft = Math.max(0, (rectLeft - deltaX)); │ │ │ │ + newLeft = Math.min(newLeft, │ │ │ │ + this.ovmap.size.w - this.wComp - rectWidth); │ │ │ │ + this.setRectPxBounds(new OpenLayers.Bounds(newLeft, │ │ │ │ + newTop + rectHeight, │ │ │ │ + newLeft + rectWidth, │ │ │ │ + newTop)); │ │ │ │ } │ │ │ │ - return node; │ │ │ │ }, │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: write_wmc_ContactInformation │ │ │ │ + * Method: mapDivClick │ │ │ │ + * Handle browser events │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} evt │ │ │ │ */ │ │ │ │ - write_wmc_ContactInformation: function(contact) { │ │ │ │ - var node = this.createElementDefaultNS("ContactInformation"); │ │ │ │ - │ │ │ │ - if (contact.personPrimary) { │ │ │ │ - node.appendChild(this.write_wmc_ContactPersonPrimary(contact.personPrimary)); │ │ │ │ - } │ │ │ │ - if (contact.position) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "ContactPosition", contact.position │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - if (contact.contactAddress) { │ │ │ │ - node.appendChild(this.write_wmc_ContactAddress(contact.contactAddress)); │ │ │ │ - } │ │ │ │ - if (contact.phone) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "ContactVoiceTelephone", contact.phone │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - if (contact.fax) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "ContactFacsimileTelephone", contact.fax │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - if (contact.email) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "ContactElectronicMailAddress", contact.email │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ + mapDivClick: function(evt) { │ │ │ │ + var pxCenter = this.rectPxBounds.getCenterPixel(); │ │ │ │ + var deltaX = evt.xy.x - pxCenter.x; │ │ │ │ + var deltaY = evt.xy.y - pxCenter.y; │ │ │ │ + var top = this.rectPxBounds.top; │ │ │ │ + var left = this.rectPxBounds.left; │ │ │ │ + var height = Math.abs(this.rectPxBounds.getHeight()); │ │ │ │ + var width = this.rectPxBounds.getWidth(); │ │ │ │ + var newTop = Math.max(0, (top + deltaY)); │ │ │ │ + newTop = Math.min(newTop, this.ovmap.size.h - height); │ │ │ │ + var newLeft = Math.max(0, (left + deltaX)); │ │ │ │ + newLeft = Math.min(newLeft, this.ovmap.size.w - width); │ │ │ │ + this.setRectPxBounds(new OpenLayers.Bounds(newLeft, │ │ │ │ + newTop + height, │ │ │ │ + newLeft + width, │ │ │ │ + newTop)); │ │ │ │ + this.updateMapToRect(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write_wmc_ContactPersonPrimary │ │ │ │ + * Method: onButtonClick │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - write_wmc_ContactPersonPrimary: function(personPrimary) { │ │ │ │ - var node = this.createElementDefaultNS("ContactPersonPrimary"); │ │ │ │ - if (personPrimary.person) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "ContactPerson", personPrimary.person │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - if (personPrimary.organization) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "ContactOrganization", personPrimary.organization │ │ │ │ - )); │ │ │ │ + onButtonClick: function(evt) { │ │ │ │ + if (evt.buttonElement === this.minimizeDiv) { │ │ │ │ + this.minimizeControl(); │ │ │ │ + } else if (evt.buttonElement === this.maximizeDiv) { │ │ │ │ + this.maximizeControl(); │ │ │ │ } │ │ │ │ - return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write_wmc_ContactAddress │ │ │ │ + * Method: maximizeControl │ │ │ │ + * Unhide the control. Called when the control is in the map viewport. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * e - {<OpenLayers.Event>} │ │ │ │ */ │ │ │ │ - write_wmc_ContactAddress: function(contactAddress) { │ │ │ │ - var node = this.createElementDefaultNS("ContactAddress"); │ │ │ │ - if (contactAddress.type) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "AddressType", contactAddress.type │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - if (contactAddress.address) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "Address", contactAddress.address │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - if (contactAddress.city) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "City", contactAddress.city │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - if (contactAddress.stateOrProvince) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "StateOrProvince", contactAddress.stateOrProvince │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - if (contactAddress.postcode) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "PostCode", contactAddress.postcode │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - if (contactAddress.country) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "Country", contactAddress.country │ │ │ │ - )); │ │ │ │ + maximizeControl: function(e) { │ │ │ │ + this.element.style.display = ''; │ │ │ │ + this.showToggle(false); │ │ │ │ + if (e != null) { │ │ │ │ + OpenLayers.Event.stop(e); │ │ │ │ } │ │ │ │ - return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write_ol_MapExtension │ │ │ │ + * Method: minimizeControl │ │ │ │ + * Hide all the contents of the control, shrink the size, │ │ │ │ + * add the maximize icon │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * e - {<OpenLayers.Event>} │ │ │ │ */ │ │ │ │ - write_ol_MapExtension: function(context) { │ │ │ │ - var node = this.createElementDefaultNS("Extension"); │ │ │ │ - │ │ │ │ - var bounds = context.maxExtent; │ │ │ │ - if (bounds) { │ │ │ │ - var maxExtent = this.createElementNS( │ │ │ │ - this.namespaces.ol, "ol:maxExtent" │ │ │ │ - ); │ │ │ │ - this.setAttributes(maxExtent, { │ │ │ │ - minx: bounds.left.toPrecision(18), │ │ │ │ - miny: bounds.bottom.toPrecision(18), │ │ │ │ - maxx: bounds.right.toPrecision(18), │ │ │ │ - maxy: bounds.top.toPrecision(18) │ │ │ │ - }); │ │ │ │ - node.appendChild(maxExtent); │ │ │ │ + minimizeControl: function(e) { │ │ │ │ + this.element.style.display = 'none'; │ │ │ │ + this.showToggle(true); │ │ │ │ + if (e != null) { │ │ │ │ + OpenLayers.Event.stop(e); │ │ │ │ } │ │ │ │ - │ │ │ │ - return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write_wmc_LayerList │ │ │ │ - * Create a LayerList node given an context object. │ │ │ │ + * Method: showToggle │ │ │ │ + * Hide/Show the toggle depending on whether the control is minimized │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * context - {Object} Context object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Element} A WMC LayerList element node. │ │ │ │ + * minimize - {Boolean} │ │ │ │ */ │ │ │ │ - write_wmc_LayerList: function(context) { │ │ │ │ - var list = this.createElementDefaultNS("LayerList"); │ │ │ │ - │ │ │ │ - for (var i = 0, len = context.layersContext.length; i < len; ++i) { │ │ │ │ - list.appendChild(this.write_wmc_Layer(context.layersContext[i])); │ │ │ │ + showToggle: function(minimize) { │ │ │ │ + if (this.maximizeDiv) { │ │ │ │ + this.maximizeDiv.style.display = minimize ? '' : 'none'; │ │ │ │ + } │ │ │ │ + if (this.minimizeDiv) { │ │ │ │ + this.minimizeDiv.style.display = minimize ? 'none' : ''; │ │ │ │ } │ │ │ │ - │ │ │ │ - return list; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write_wmc_Layer │ │ │ │ - * Create a Layer node given a layer context object. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * context - {Object} A layer context object.} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Element} A WMC Layer element node. │ │ │ │ + * Method: update │ │ │ │ + * Update the overview map after layers move. │ │ │ │ */ │ │ │ │ - write_wmc_Layer: function(context) { │ │ │ │ - var node = this.createElementDefaultNS( │ │ │ │ - "Layer", null, { │ │ │ │ - queryable: context.queryable ? "1" : "0", │ │ │ │ - hidden: context.visibility ? "0" : "1" │ │ │ │ - } │ │ │ │ - ); │ │ │ │ + update: function() { │ │ │ │ + if (this.ovmap == null) { │ │ │ │ + this.createMap(); │ │ │ │ + } │ │ │ │ │ │ │ │ - // required Server element │ │ │ │ - node.appendChild(this.write_wmc_Server(context)); │ │ │ │ + if (this.autoPan || !this.isSuitableOverview()) { │ │ │ │ + this.updateOverview(); │ │ │ │ + } │ │ │ │ │ │ │ │ - // required Name element │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "Name", context.name │ │ │ │ - )); │ │ │ │ + // update extent rectangle │ │ │ │ + this.updateRectToMap(); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // required Title element │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "Title", context.title │ │ │ │ - )); │ │ │ │ + /** │ │ │ │ + * Method: isSuitableOverview │ │ │ │ + * Determines if the overview map is suitable given the extent and │ │ │ │ + * resolution of the main map. │ │ │ │ + */ │ │ │ │ + isSuitableOverview: function() { │ │ │ │ + var mapExtent = this.map.getExtent(); │ │ │ │ + var maxExtent = this.map.getMaxExtent(); │ │ │ │ + var testExtent = new OpenLayers.Bounds( │ │ │ │ + Math.max(mapExtent.left, maxExtent.left), │ │ │ │ + Math.max(mapExtent.bottom, maxExtent.bottom), │ │ │ │ + Math.min(mapExtent.right, maxExtent.right), │ │ │ │ + Math.min(mapExtent.top, maxExtent.top)); │ │ │ │ │ │ │ │ - // optional Abstract element │ │ │ │ - if (context["abstract"]) { │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "Abstract", context["abstract"] │ │ │ │ - )); │ │ │ │ + if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ + testExtent = testExtent.transform( │ │ │ │ + this.map.getProjectionObject(), │ │ │ │ + this.ovmap.getProjectionObject()); │ │ │ │ } │ │ │ │ │ │ │ │ - // optional DataURL element │ │ │ │ - if (context.dataURL) { │ │ │ │ - node.appendChild(this.write_wmc_URLType("DataURL", context.dataURL)); │ │ │ │ - } │ │ │ │ + var resRatio = this.ovmap.getResolution() / this.map.getResolution(); │ │ │ │ + return ((resRatio > this.minRatio) && │ │ │ │ + (resRatio <= this.maxRatio) && │ │ │ │ + (this.ovmap.getExtent().containsBounds(testExtent))); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // optional MetadataURL element │ │ │ │ - if (context.metadataURL) { │ │ │ │ - node.appendChild(this.write_wmc_URLType("MetadataURL", context.metadataURL)); │ │ │ │ + /** │ │ │ │ + * Method updateOverview │ │ │ │ + * Called by <update> if <isSuitableOverview> returns true │ │ │ │ + */ │ │ │ │ + updateOverview: function() { │ │ │ │ + var mapRes = this.map.getResolution(); │ │ │ │ + var targetRes = this.ovmap.getResolution(); │ │ │ │ + var resRatio = targetRes / mapRes; │ │ │ │ + if (resRatio > this.maxRatio) { │ │ │ │ + // zoom in overview map │ │ │ │ + targetRes = this.minRatio * mapRes; │ │ │ │ + } else if (resRatio <= this.minRatio) { │ │ │ │ + // zoom out overview map │ │ │ │ + targetRes = this.maxRatio * mapRes; │ │ │ │ } │ │ │ │ - │ │ │ │ - return node; │ │ │ │ + var center; │ │ │ │ + if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ + center = this.map.center.clone(); │ │ │ │ + center.transform(this.map.getProjectionObject(), │ │ │ │ + this.ovmap.getProjectionObject()); │ │ │ │ + } else { │ │ │ │ + center = this.map.center; │ │ │ │ + } │ │ │ │ + this.ovmap.setCenter(center, this.ovmap.getZoomForResolution( │ │ │ │ + targetRes * this.resolutionFactor)); │ │ │ │ + this.updateRectToMap(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write_wmc_LayerExtension │ │ │ │ - * Add OpenLayers specific layer parameters to an Extension element. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * context - {Object} A layer context object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Element} A WMC Extension element (for a layer). │ │ │ │ + * Method: createMap │ │ │ │ + * Construct the map that this control contains │ │ │ │ */ │ │ │ │ - write_wmc_LayerExtension: function(context) { │ │ │ │ - var node = this.createElementDefaultNS("Extension"); │ │ │ │ + createMap: function() { │ │ │ │ + // create the overview map │ │ │ │ + var options = OpenLayers.Util.extend({ │ │ │ │ + controls: [], │ │ │ │ + maxResolution: 'auto', │ │ │ │ + fallThrough: false │ │ │ │ + }, this.mapOptions); │ │ │ │ + this.ovmap = new OpenLayers.Map(this.mapDiv, options); │ │ │ │ + this.ovmap.viewPortDiv.appendChild(this.extentRectangle); │ │ │ │ │ │ │ │ - var bounds = context.maxExtent; │ │ │ │ - var maxExtent = this.createElementNS( │ │ │ │ - this.namespaces.ol, "ol:maxExtent" │ │ │ │ + // prevent ovmap from being destroyed when the page unloads, because │ │ │ │ + // the OverviewMap control has to do this (and does it). │ │ │ │ + OpenLayers.Event.stopObserving(window, 'unload', this.ovmap.unloadDestroy); │ │ │ │ + │ │ │ │ + this.ovmap.addLayers(this.layers); │ │ │ │ + this.ovmap.zoomToMaxExtent(); │ │ │ │ + // check extent rectangle border width │ │ │ │ + this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle, │ │ │ │ + 'border-left-width')) + │ │ │ │ + parseInt(OpenLayers.Element.getStyle(this.extentRectangle, │ │ │ │ + 'border-right-width')); │ │ │ │ + this.wComp = (this.wComp) ? this.wComp : 2; │ │ │ │ + this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle, │ │ │ │ + 'border-top-width')) + │ │ │ │ + parseInt(OpenLayers.Element.getStyle(this.extentRectangle, │ │ │ │ + 'border-bottom-width')); │ │ │ │ + this.hComp = (this.hComp) ? this.hComp : 2; │ │ │ │ + │ │ │ │ + this.handlers.drag = new OpenLayers.Handler.Drag( │ │ │ │ + this, { │ │ │ │ + move: this.rectDrag, │ │ │ │ + done: this.updateMapToRect │ │ │ │ + }, { │ │ │ │ + map: this.ovmap │ │ │ │ + } │ │ │ │ ); │ │ │ │ - this.setAttributes(maxExtent, { │ │ │ │ - minx: bounds.left.toPrecision(18), │ │ │ │ - miny: bounds.bottom.toPrecision(18), │ │ │ │ - maxx: bounds.right.toPrecision(18), │ │ │ │ - maxy: bounds.top.toPrecision(18) │ │ │ │ + this.handlers.click = new OpenLayers.Handler.Click( │ │ │ │ + this, { │ │ │ │ + "click": this.mapDivClick │ │ │ │ + }, { │ │ │ │ + "single": true, │ │ │ │ + "double": false, │ │ │ │ + "stopSingle": true, │ │ │ │ + "stopDouble": true, │ │ │ │ + "pixelTolerance": 1, │ │ │ │ + map: this.ovmap │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + this.handlers.click.activate(); │ │ │ │ + │ │ │ │ + this.rectEvents = new OpenLayers.Events(this, this.extentRectangle, │ │ │ │ + null, true); │ │ │ │ + this.rectEvents.register("mouseover", this, function(e) { │ │ │ │ + if (!this.handlers.drag.active && !this.map.dragging) { │ │ │ │ + this.handlers.drag.activate(); │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + this.rectEvents.register("mouseout", this, function(e) { │ │ │ │ + if (!this.handlers.drag.dragging) { │ │ │ │ + this.handlers.drag.deactivate(); │ │ │ │ + } │ │ │ │ }); │ │ │ │ - node.appendChild(maxExtent); │ │ │ │ │ │ │ │ - if (context.tileSize && !context.singleTile) { │ │ │ │ - var size = this.createElementNS( │ │ │ │ - this.namespaces.ol, "ol:tileSize" │ │ │ │ - ); │ │ │ │ - this.setAttributes(size, context.tileSize); │ │ │ │ - node.appendChild(size); │ │ │ │ + if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ + var sourceUnits = this.map.getProjectionObject().getUnits() || │ │ │ │ + this.map.units || this.map.baseLayer.units; │ │ │ │ + var targetUnits = this.ovmap.getProjectionObject().getUnits() || │ │ │ │ + this.ovmap.units || this.ovmap.baseLayer.units; │ │ │ │ + this.resolutionFactor = sourceUnits && targetUnits ? │ │ │ │ + OpenLayers.INCHES_PER_UNIT[sourceUnits] / │ │ │ │ + OpenLayers.INCHES_PER_UNIT[targetUnits] : 1; │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - var properties = [ │ │ │ │ - "transparent", "numZoomLevels", "units", "isBaseLayer", │ │ │ │ - "opacity", "displayInLayerSwitcher", "singleTile" │ │ │ │ - ]; │ │ │ │ - var child; │ │ │ │ - for (var i = 0, len = properties.length; i < len; ++i) { │ │ │ │ - child = this.createOLPropertyNode(context, properties[i]); │ │ │ │ - if (child) { │ │ │ │ - node.appendChild(child); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: updateRectToMap │ │ │ │ + * Updates the extent rectangle position and size to match the map extent │ │ │ │ + */ │ │ │ │ + updateRectToMap: function() { │ │ │ │ + // If the projections differ we need to reproject │ │ │ │ + var bounds; │ │ │ │ + if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ + bounds = this.map.getExtent().transform( │ │ │ │ + this.map.getProjectionObject(), │ │ │ │ + this.ovmap.getProjectionObject()); │ │ │ │ + } else { │ │ │ │ + bounds = this.map.getExtent(); │ │ │ │ + } │ │ │ │ + var pxBounds = this.getRectBoundsFromMapBounds(bounds); │ │ │ │ + if (pxBounds) { │ │ │ │ + this.setRectPxBounds(pxBounds); │ │ │ │ } │ │ │ │ - │ │ │ │ - return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createOLPropertyNode │ │ │ │ - * Create a node representing an OpenLayers property. If the property is │ │ │ │ - * null or undefined, null will be returned. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {Object} An object. │ │ │ │ - * prop - {String} A property. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Element} A property node. │ │ │ │ + * Method: updateMapToRect │ │ │ │ + * Updates the map extent to match the extent rectangle position and size │ │ │ │ */ │ │ │ │ - createOLPropertyNode: function(obj, prop) { │ │ │ │ - var node = null; │ │ │ │ - if (obj[prop] != null) { │ │ │ │ - node = this.createElementNS(this.namespaces.ol, "ol:" + prop); │ │ │ │ - node.appendChild(this.createTextNode(obj[prop].toString())); │ │ │ │ + updateMapToRect: function() { │ │ │ │ + var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds); │ │ │ │ + if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ + lonLatBounds = lonLatBounds.transform( │ │ │ │ + this.ovmap.getProjectionObject(), │ │ │ │ + this.map.getProjectionObject()); │ │ │ │ } │ │ │ │ - return node; │ │ │ │ + this.map.panTo(lonLatBounds.getCenterLonLat()); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write_wmc_Server │ │ │ │ - * Create a Server node given a layer context object. │ │ │ │ + * Method: setRectPxBounds │ │ │ │ + * Set extent rectangle pixel bounds. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * context - {Object} Layer context object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Element} A WMC Server element node. │ │ │ │ + * pxBounds - {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - write_wmc_Server: function(context) { │ │ │ │ - var server = context.server; │ │ │ │ - var node = this.createElementDefaultNS("Server"); │ │ │ │ - var attributes = { │ │ │ │ - service: "OGC:WMS", │ │ │ │ - version: server.version │ │ │ │ - }; │ │ │ │ - if (server.title) { │ │ │ │ - attributes.title = server.title; │ │ │ │ + setRectPxBounds: function(pxBounds) { │ │ │ │ + var top = Math.max(pxBounds.top, 0); │ │ │ │ + var left = Math.max(pxBounds.left, 0); │ │ │ │ + var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()), │ │ │ │ + this.ovmap.size.h - this.hComp); │ │ │ │ + var right = Math.min(pxBounds.left + pxBounds.getWidth(), │ │ │ │ + this.ovmap.size.w - this.wComp); │ │ │ │ + var width = Math.max(right - left, 0); │ │ │ │ + var height = Math.max(bottom - top, 0); │ │ │ │ + if (width < this.minRectSize || height < this.minRectSize) { │ │ │ │ + this.extentRectangle.className = this.displayClass + │ │ │ │ + this.minRectDisplayClass; │ │ │ │ + var rLeft = left + (width / 2) - (this.minRectSize / 2); │ │ │ │ + var rTop = top + (height / 2) - (this.minRectSize / 2); │ │ │ │ + this.extentRectangle.style.top = Math.round(rTop) + 'px'; │ │ │ │ + this.extentRectangle.style.left = Math.round(rLeft) + 'px'; │ │ │ │ + this.extentRectangle.style.height = this.minRectSize + 'px'; │ │ │ │ + this.extentRectangle.style.width = this.minRectSize + 'px'; │ │ │ │ + } else { │ │ │ │ + this.extentRectangle.className = this.displayClass + │ │ │ │ + 'ExtentRectangle'; │ │ │ │ + this.extentRectangle.style.top = Math.round(top) + 'px'; │ │ │ │ + this.extentRectangle.style.left = Math.round(left) + 'px'; │ │ │ │ + this.extentRectangle.style.height = Math.round(height) + 'px'; │ │ │ │ + this.extentRectangle.style.width = Math.round(width) + 'px'; │ │ │ │ } │ │ │ │ - this.setAttributes(node, attributes); │ │ │ │ - │ │ │ │ - // required OnlineResource element │ │ │ │ - node.appendChild(this.write_wmc_OnlineResource(server.url)); │ │ │ │ - │ │ │ │ - return node; │ │ │ │ + this.rectPxBounds = new OpenLayers.Bounds( │ │ │ │ + Math.round(left), Math.round(bottom), │ │ │ │ + Math.round(right), Math.round(top) │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write_wmc_URLType │ │ │ │ - * Create a LogoURL/DescriptionURL/MetadataURL/DataURL/LegendURL node given a object and elementName. │ │ │ │ + * Method: getRectBoundsFromMapBounds │ │ │ │ + * Get the rect bounds from the map bounds. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * elName - {String} Name of element (LogoURL/DescriptionURL/MetadataURL/LegendURL) │ │ │ │ - * url - {String} URL string value │ │ │ │ - * attr - {Object} Optional attributes (width, height, format) │ │ │ │ + * lonLatBounds - {<OpenLayers.Bounds>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Element} A WMC element node. │ │ │ │ - */ │ │ │ │ - write_wmc_URLType: function(elName, url, attr) { │ │ │ │ - var node = this.createElementDefaultNS(elName); │ │ │ │ - node.appendChild(this.write_wmc_OnlineResource(url)); │ │ │ │ - if (attr) { │ │ │ │ - var optionalAttributes = ["width", "height", "format"]; │ │ │ │ - for (var i = 0; i < optionalAttributes.length; i++) { │ │ │ │ - if (optionalAttributes[i] in attr) { │ │ │ │ - node.setAttribute(optionalAttributes[i], attr[optionalAttributes[i]]); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: write_wmc_DimensionList │ │ │ │ + * {<OpenLayers.Bounds>}A bounds which is the passed-in map lon/lat extent │ │ │ │ + * translated into pixel bounds for the overview map │ │ │ │ */ │ │ │ │ - write_wmc_DimensionList: function(context) { │ │ │ │ - var node = this.createElementDefaultNS("DimensionList"); │ │ │ │ - var required_attributes = { │ │ │ │ - name: true, │ │ │ │ - units: true, │ │ │ │ - unitSymbol: true, │ │ │ │ - userValue: true │ │ │ │ - }; │ │ │ │ - for (var dim in context.dimensions) { │ │ │ │ - var attributes = {}; │ │ │ │ - var dimension = context.dimensions[dim]; │ │ │ │ - for (var name in dimension) { │ │ │ │ - if (typeof dimension[name] == "boolean") { │ │ │ │ - attributes[name] = Number(dimension[name]); │ │ │ │ - } else { │ │ │ │ - attributes[name] = dimension[name]; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var values = ""; │ │ │ │ - if (attributes.values) { │ │ │ │ - values = attributes.values.join(","); │ │ │ │ - delete attributes.values; │ │ │ │ - } │ │ │ │ - │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "Dimension", values, attributes │ │ │ │ - )); │ │ │ │ + getRectBoundsFromMapBounds: function(lonLatBounds) { │ │ │ │ + var leftBottomPx = this.getOverviewPxFromLonLat({ │ │ │ │ + lon: lonLatBounds.left, │ │ │ │ + lat: lonLatBounds.bottom │ │ │ │ + }); │ │ │ │ + var rightTopPx = this.getOverviewPxFromLonLat({ │ │ │ │ + lon: lonLatBounds.right, │ │ │ │ + lat: lonLatBounds.top │ │ │ │ + }); │ │ │ │ + var bounds = null; │ │ │ │ + if (leftBottomPx && rightTopPx) { │ │ │ │ + bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y, │ │ │ │ + rightTopPx.x, rightTopPx.y); │ │ │ │ } │ │ │ │ - return node; │ │ │ │ + return bounds; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write_wmc_FormatList │ │ │ │ - * Create a FormatList node given a layer context. │ │ │ │ + * Method: getMapBoundsFromRectBounds │ │ │ │ + * Get the map bounds from the rect bounds. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * context - {Object} Layer context object. │ │ │ │ + * pxBounds - {<OpenLayers.Bounds>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Element} A WMC FormatList element node. │ │ │ │ + * {<OpenLayers.Bounds>} Bounds which is the passed-in overview rect bounds │ │ │ │ + * translated into lon/lat bounds for the overview map │ │ │ │ */ │ │ │ │ - write_wmc_FormatList: function(context) { │ │ │ │ - var node = this.createElementDefaultNS("FormatList"); │ │ │ │ - for (var i = 0, len = context.formats.length; i < len; i++) { │ │ │ │ - var format = context.formats[i]; │ │ │ │ - node.appendChild(this.createElementDefaultNS( │ │ │ │ - "Format", │ │ │ │ - format.value, │ │ │ │ - (format.current && format.current == true) ? { │ │ │ │ - current: "1" │ │ │ │ - } : null │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - │ │ │ │ - return node; │ │ │ │ + getMapBoundsFromRectBounds: function(pxBounds) { │ │ │ │ + var leftBottomLonLat = this.getLonLatFromOverviewPx({ │ │ │ │ + x: pxBounds.left, │ │ │ │ + y: pxBounds.bottom │ │ │ │ + }); │ │ │ │ + var rightTopLonLat = this.getLonLatFromOverviewPx({ │ │ │ │ + x: pxBounds.right, │ │ │ │ + y: pxBounds.top │ │ │ │ + }); │ │ │ │ + return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat, │ │ │ │ + rightTopLonLat.lon, rightTopLonLat.lat); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write_wmc_StyleList │ │ │ │ - * Create a StyleList node given a layer context. │ │ │ │ + * Method: getLonLatFromOverviewPx │ │ │ │ + * Get a map location from a pixel location │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * layer - {Object} Layer context object. │ │ │ │ + * overviewMapPx - {<OpenLayers.Pixel>|Object} OpenLayers.Pixel or │ │ │ │ + * an object with a │ │ │ │ + * 'x' and 'y' properties. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Element} A WMC StyleList element node. │ │ │ │ + * {Object} Location which is the passed-in overview map │ │ │ │ + * OpenLayers.Pixel, translated into lon/lat by the overview │ │ │ │ + * map. An object with a 'lon' and 'lat' properties. │ │ │ │ */ │ │ │ │ - write_wmc_StyleList: function(layer) { │ │ │ │ - var node = this.createElementDefaultNS("StyleList"); │ │ │ │ - │ │ │ │ - var styles = layer.styles; │ │ │ │ - if (styles && OpenLayers.Util.isArray(styles)) { │ │ │ │ - var sld; │ │ │ │ - for (var i = 0, len = styles.length; i < len; i++) { │ │ │ │ - var s = styles[i]; │ │ │ │ - // three style types to consider │ │ │ │ - // [1] linked SLD │ │ │ │ - // [2] inline SLD │ │ │ │ - // [3] named style │ │ │ │ - // running child nodes always gets name, optionally gets href or body │ │ │ │ - var style = this.createElementDefaultNS( │ │ │ │ - "Style", │ │ │ │ - null, │ │ │ │ - (s.current && s.current == true) ? { │ │ │ │ - current: "1" │ │ │ │ - } : null │ │ │ │ - ); │ │ │ │ - if (s.href) { // [1] │ │ │ │ - sld = this.createElementDefaultNS("SLD"); │ │ │ │ - // Name is optional. │ │ │ │ - if (s.name) { │ │ │ │ - sld.appendChild(this.createElementDefaultNS("Name", s.name)); │ │ │ │ - } │ │ │ │ - // Title is optional. │ │ │ │ - if (s.title) { │ │ │ │ - sld.appendChild(this.createElementDefaultNS("Title", s.title)); │ │ │ │ - } │ │ │ │ - // LegendURL is optional │ │ │ │ - if (s.legend) { │ │ │ │ - sld.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend)); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var link = this.write_wmc_OnlineResource(s.href); │ │ │ │ - sld.appendChild(link); │ │ │ │ - style.appendChild(sld); │ │ │ │ - } else if (s.body) { // [2] │ │ │ │ - sld = this.createElementDefaultNS("SLD"); │ │ │ │ - // Name is optional. │ │ │ │ - if (s.name) { │ │ │ │ - sld.appendChild(this.createElementDefaultNS("Name", s.name)); │ │ │ │ - } │ │ │ │ - // Title is optional. │ │ │ │ - if (s.title) { │ │ │ │ - sld.appendChild(this.createElementDefaultNS("Title", s.title)); │ │ │ │ - } │ │ │ │ - // LegendURL is optional │ │ │ │ - if (s.legend) { │ │ │ │ - sld.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend)); │ │ │ │ - } │ │ │ │ + getLonLatFromOverviewPx: function(overviewMapPx) { │ │ │ │ + var size = this.ovmap.size; │ │ │ │ + var res = this.ovmap.getResolution(); │ │ │ │ + var center = this.ovmap.getExtent().getCenterLonLat(); │ │ │ │ │ │ │ │ - // read in body as xml doc - assume proper namespace declarations │ │ │ │ - var doc = OpenLayers.Format.XML.prototype.read.apply(this, [s.body]); │ │ │ │ - // append to StyledLayerDescriptor node │ │ │ │ - var imported = doc.documentElement; │ │ │ │ - if (sld.ownerDocument && sld.ownerDocument.importNode) { │ │ │ │ - imported = sld.ownerDocument.importNode(imported, true); │ │ │ │ - } │ │ │ │ - sld.appendChild(imported); │ │ │ │ - style.appendChild(sld); │ │ │ │ - } else { // [3] │ │ │ │ - // both Name and Title are required. │ │ │ │ - style.appendChild(this.createElementDefaultNS("Name", s.name)); │ │ │ │ - style.appendChild(this.createElementDefaultNS("Title", s.title)); │ │ │ │ - // Abstract is optional │ │ │ │ - if (s['abstract']) { // abstract is a js keyword │ │ │ │ - style.appendChild(this.createElementDefaultNS( │ │ │ │ - "Abstract", s['abstract'] │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - // LegendURL is optional │ │ │ │ - if (s.legend) { │ │ │ │ - style.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend)); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - node.appendChild(style); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + var deltaX = overviewMapPx.x - (size.w / 2); │ │ │ │ + var deltaY = overviewMapPx.y - (size.h / 2); │ │ │ │ │ │ │ │ - return node; │ │ │ │ + return { │ │ │ │ + lon: center.lon + deltaX * res, │ │ │ │ + lat: center.lat - deltaY * res │ │ │ │ + }; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write_wmc_OnlineResource │ │ │ │ - * Create an OnlineResource node given a URL. │ │ │ │ + * Method: getOverviewPxFromLonLat │ │ │ │ + * Get a pixel location from a map location │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * href - {String} URL for the resource. │ │ │ │ + * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an │ │ │ │ + * object with a 'lon' and 'lat' properties. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Element} A WMC OnlineResource element node. │ │ │ │ - */ │ │ │ │ - write_wmc_OnlineResource: function(href) { │ │ │ │ - var node = this.createElementDefaultNS("OnlineResource"); │ │ │ │ - this.setAttributeNS(node, this.namespaces.xlink, "xlink:type", "simple"); │ │ │ │ - this.setAttributeNS(node, this.namespaces.xlink, "xlink:href", href); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getOnlineResource_href │ │ │ │ + * {Object} Location which is the passed-in OpenLayers.LonLat, │ │ │ │ + * translated into overview map pixels │ │ │ │ */ │ │ │ │ - getOnlineResource_href: function(node) { │ │ │ │ - var object = {}; │ │ │ │ - var links = node.getElementsByTagName("OnlineResource"); │ │ │ │ - if (links.length > 0) { │ │ │ │ - this.read_wmc_OnlineResource(object, links[0]); │ │ │ │ + getOverviewPxFromLonLat: function(lonlat) { │ │ │ │ + var res = this.ovmap.getResolution(); │ │ │ │ + var extent = this.ovmap.getExtent(); │ │ │ │ + if (extent) { │ │ │ │ + return { │ │ │ │ + x: Math.round(1 / res * (lonlat.lon - extent.left)), │ │ │ │ + y: Math.round(1 / res * (extent.top - lonlat.lat)) │ │ │ │ + }; │ │ │ │ } │ │ │ │ - return object.href; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMC.v1" │ │ │ │ - │ │ │ │ + CLASS_NAME: 'OpenLayers.Control.OverviewMap' │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/WMC/v1_1_0.js │ │ │ │ + OpenLayers/Control/GetFeature.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/WMC/v1.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Handler/Click.js │ │ │ │ + * @requires OpenLayers/Handler/Box.js │ │ │ │ + * @requires OpenLayers/Handler/Hover.js │ │ │ │ + * @requires OpenLayers/Filter/Spatial.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.WMC.v1_1_0 │ │ │ │ - * Read and write WMC version 1.1.0. │ │ │ │ + * Class: OpenLayers.Control.GetFeature │ │ │ │ + * Gets vector features for locations underneath the mouse cursor. Can be │ │ │ │ + * configured to act on click, hover or dragged boxes. Uses an │ │ │ │ + * <OpenLayers.Protocol> that supports spatial filters to retrieve │ │ │ │ + * features from a server and fires events that notify applications of the │ │ │ │ + * selected features. │ │ │ │ * │ │ │ │ - * Differences between 1.1.0 and 1.0.0: │ │ │ │ - * - 1.1.0 Layers have optional sld:MinScaleDenominator and │ │ │ │ - * sld:MaxScaleDenominator │ │ │ │ - * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WMC.v1> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.WMC.v1_1_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WMC.v1, { │ │ │ │ +OpenLayers.Control.GetFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: VERSION │ │ │ │ - * {String} 1.1.0 │ │ │ │ - */ │ │ │ │ - VERSION: "1.1.0", │ │ │ │ + /** │ │ │ │ + * APIProperty: protocol │ │ │ │ + * {<OpenLayers.Protocol>} Required. The protocol used for fetching │ │ │ │ + * features. │ │ │ │ + */ │ │ │ │ + protocol: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} http://www.opengis.net/context │ │ │ │ - * http://schemas.opengis.net/context/1.1.0/context.xsd │ │ │ │ - */ │ │ │ │ - schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd", │ │ │ │ + /** │ │ │ │ + * APIProperty: multipleKey │ │ │ │ + * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets │ │ │ │ + * the <multiple> property to true. Default is null. │ │ │ │ + */ │ │ │ │ + multipleKey: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WMC.v1_1_0 │ │ │ │ - * Instances of this class are not created directly. Use the │ │ │ │ - * <OpenLayers.Format.WMC> constructor instead. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.WMC.v1.prototype.initialize.apply( │ │ │ │ - this, [options] │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: toggleKey │ │ │ │ + * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets │ │ │ │ + * the <toggle> property to true. Default is null. │ │ │ │ + */ │ │ │ │ + toggleKey: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: read_sld_MinScaleDenominator │ │ │ │ - * Read a sld:MinScaleDenominator node. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layerContext - {Object} An object representing a layer. │ │ │ │ - * node - {Element} An element node. │ │ │ │ - */ │ │ │ │ - read_sld_MinScaleDenominator: function(layerContext, node) { │ │ │ │ - var minScaleDenominator = parseFloat(this.getChildValue(node)); │ │ │ │ - if (minScaleDenominator > 0) { │ │ │ │ - layerContext.maxScale = minScaleDenominator; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: modifiers │ │ │ │ + * {Object} The event modifiers to use, according to the current event │ │ │ │ + * being handled by this control's handlers │ │ │ │ + */ │ │ │ │ + modifiers: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: read_sld_MaxScaleDenominator │ │ │ │ - * Read a sld:MaxScaleDenominator node. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layerContext - {Object} An object representing a layer. │ │ │ │ - * node - {Element} An element node. │ │ │ │ - */ │ │ │ │ - read_sld_MaxScaleDenominator: function(layerContext, node) { │ │ │ │ - layerContext.minScale = parseFloat(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: multiple │ │ │ │ + * {Boolean} Allow selection of multiple geometries. Default is false. │ │ │ │ + */ │ │ │ │ + multiple: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: read_wmc_SRS │ │ │ │ - */ │ │ │ │ - read_wmc_SRS: function(layerContext, node) { │ │ │ │ - if (!("srs" in layerContext)) { │ │ │ │ - layerContext.srs = {}; │ │ │ │ - } │ │ │ │ - layerContext.srs[this.getChildValue(node)] = true; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: click │ │ │ │ + * {Boolean} Use a click handler for selecting/unselecting features. If │ │ │ │ + * both <click> and <box> are set to true, the click handler takes │ │ │ │ + * precedence over the box handler if a box with zero extent was │ │ │ │ + * selected. Default is true. │ │ │ │ + */ │ │ │ │ + click: true, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: write_wmc_Layer │ │ │ │ - * Create a Layer node given a layer context object. This method adds │ │ │ │ - * elements specific to version 1.1.0. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * context - {Object} A layer context object.} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Element} A WMC Layer element node. │ │ │ │ - */ │ │ │ │ - write_wmc_Layer: function(context) { │ │ │ │ - var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply( │ │ │ │ - this, [context] │ │ │ │ - ); │ │ │ │ + /** │ │ │ │ + * APIProperty: single │ │ │ │ + * {Boolean} Tells whether select by click should select a single │ │ │ │ + * feature. If set to false, all matching features are selected. │ │ │ │ + * If set to true, only the best matching feature is selected. │ │ │ │ + * This option has an effect only of the <click> option is set │ │ │ │ + * to true. Default is true. │ │ │ │ + */ │ │ │ │ + single: true, │ │ │ │ │ │ │ │ - // min/max scale denominator elements go before the 4th element in v1 │ │ │ │ - if (context.maxScale) { │ │ │ │ - var minSD = this.createElementNS( │ │ │ │ - this.namespaces.sld, "sld:MinScaleDenominator" │ │ │ │ - ); │ │ │ │ - minSD.appendChild(this.createTextNode(context.maxScale.toPrecision(16))); │ │ │ │ - node.appendChild(minSD); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: clickout │ │ │ │ + * {Boolean} Unselect features when clicking outside any feature. │ │ │ │ + * Applies only if <click> is true. Default is true. │ │ │ │ + */ │ │ │ │ + clickout: true, │ │ │ │ │ │ │ │ - if (context.minScale) { │ │ │ │ - var maxSD = this.createElementNS( │ │ │ │ - this.namespaces.sld, "sld:MaxScaleDenominator" │ │ │ │ - ); │ │ │ │ - maxSD.appendChild(this.createTextNode(context.minScale.toPrecision(16))); │ │ │ │ - node.appendChild(maxSD); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: toggle │ │ │ │ + * {Boolean} Unselect a selected feature on click. Applies only if │ │ │ │ + * <click> is true. Default is false. │ │ │ │ + */ │ │ │ │ + toggle: false, │ │ │ │ │ │ │ │ - // optional SRS element(s) │ │ │ │ - if (context.srs) { │ │ │ │ - for (var name in context.srs) { │ │ │ │ - node.appendChild(this.createElementDefaultNS("SRS", name)); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: clickTolerance │ │ │ │ + * {Integer} Tolerance for the filter query in pixels. This has the │ │ │ │ + * same effect as the tolerance parameter on WMS GetFeatureInfo │ │ │ │ + * requests. Will be ignored for box selections. Applies only if │ │ │ │ + * <click> or <hover> is true. Default is 5. Note that this not │ │ │ │ + * only affects requests on click, but also on hover. │ │ │ │ + */ │ │ │ │ + clickTolerance: 5, │ │ │ │ │ │ │ │ - // optional FormatList element │ │ │ │ - node.appendChild(this.write_wmc_FormatList(context)); │ │ │ │ + /** │ │ │ │ + * APIProperty: hover │ │ │ │ + * {Boolean} Send feature requests on mouse moves. Default is false. │ │ │ │ + */ │ │ │ │ + hover: false, │ │ │ │ │ │ │ │ - // optional StyleList element │ │ │ │ - node.appendChild(this.write_wmc_StyleList(context)); │ │ │ │ + /** │ │ │ │ + * APIProperty: box │ │ │ │ + * {Boolean} Allow feature selection by drawing a box. If set to │ │ │ │ + * true set <click> to false to disable the click handler and │ │ │ │ + * rely on the box handler only, even for "zero extent" boxes. │ │ │ │ + * See the description of the <click> option for additional │ │ │ │ + * information. Default is false. │ │ │ │ + */ │ │ │ │ + box: false, │ │ │ │ │ │ │ │ - // optional DimensionList element │ │ │ │ - if (context.dimensions) { │ │ │ │ - node.appendChild(this.write_wmc_DimensionList(context)); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: maxFeatures │ │ │ │ + * {Integer} Maximum number of features to return from a query in single mode │ │ │ │ + * if supported by the <protocol>. This set of features is then used to │ │ │ │ + * determine the best match client-side. Default is 10. │ │ │ │ + */ │ │ │ │ + maxFeatures: 10, │ │ │ │ │ │ │ │ - // OpenLayers specific properties go in an Extension element │ │ │ │ - node.appendChild(this.write_wmc_LayerExtension(context)); │ │ │ │ + /** │ │ │ │ + * Property: features │ │ │ │ + * {Object} Hash of {<OpenLayers.Feature.Vector>}, keyed by fid, holding │ │ │ │ + * the currently selected features │ │ │ │ + */ │ │ │ │ + features: null, │ │ │ │ │ │ │ │ - return node; │ │ │ │ + /** │ │ │ │ + * Proeprty: hoverFeature │ │ │ │ + * {<OpenLayers.Feature.Vector>} The feature currently selected by the │ │ │ │ + * hover handler │ │ │ │ + */ │ │ │ │ + hoverFeature: null, │ │ │ │ │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: handlerOptions │ │ │ │ + * {Object} Additional options for the handlers used by this control. This │ │ │ │ + * is a hash with the keys "click", "box" and "hover". │ │ │ │ + */ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMC.v1_1_0" │ │ │ │ + /** │ │ │ │ + * Property: handlers │ │ │ │ + * {Object} Object with references to multiple <OpenLayers.Handler> │ │ │ │ + * instances. │ │ │ │ + */ │ │ │ │ + handlers: null, │ │ │ │ │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WMC/v1_0_0.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * Property: hoverResponse │ │ │ │ + * {<OpenLayers.Protocol.Response>} The response object associated with │ │ │ │ + * the currently running hover request (if any). │ │ │ │ + */ │ │ │ │ + hoverResponse: 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: filterType │ │ │ │ + * {<String>} The type of filter to use when sending off a request. │ │ │ │ + * Possible values: │ │ │ │ + * OpenLayers.Filter.Spatial.<BBOX|INTERSECTS|WITHIN|CONTAINS> │ │ │ │ + * Defaults to: OpenLayers.Filter.Spatial.BBOX │ │ │ │ + */ │ │ │ │ + filterType: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WMC/v1.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * beforefeatureselected - Triggered when <click> is true before a │ │ │ │ + * feature is selected. The event object has a feature property with │ │ │ │ + * the feature about to select │ │ │ │ + * featureselected - Triggered when <click> is true and a feature is │ │ │ │ + * selected. The event object has a feature property with the │ │ │ │ + * selected feature │ │ │ │ + * beforefeaturesselected - Triggered when <click> is true before a │ │ │ │ + * set of features is selected. The event object is an array of │ │ │ │ + * feature properties with the features about to be selected. │ │ │ │ + * Return false after receiving this event to discontinue processing │ │ │ │ + * of all featureselected events and the featuresselected event. │ │ │ │ + * featuresselected - Triggered when <click> is true and a set of │ │ │ │ + * features is selected. The event object is an array of feature │ │ │ │ + * properties of the selected features │ │ │ │ + * featureunselected - Triggered when <click> is true and a feature is │ │ │ │ + * unselected. The event object has a feature property with the │ │ │ │ + * unselected feature │ │ │ │ + * clickout - Triggered when when <click> is true and no feature was │ │ │ │ + * selected. │ │ │ │ + * hoverfeature - Triggered when <hover> is true and the mouse has │ │ │ │ + * stopped over a feature │ │ │ │ + * outfeature - Triggered when <hover> is true and the mouse moves │ │ │ │ + * moved away from a hover-selected feature │ │ │ │ + */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WMC.v1_0_0 │ │ │ │ - * Read and write WMC version 1.0.0. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WMC.v1> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WMC.v1_0_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WMC.v1, { │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.GetFeature │ │ │ │ + * Create a new control for fetching remote features. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} A configuration object which at least has to contain │ │ │ │ + * a <protocol> property (if not, it has to be set before a request is │ │ │ │ + * made) │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + options.handlerOptions = options.handlerOptions || {}; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: VERSION │ │ │ │ - * {String} 1.0.0 │ │ │ │ - */ │ │ │ │ - VERSION: "1.0.0", │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} http://www.opengis.net/context │ │ │ │ - * http://schemas.opengis.net/context/1.0.0/context.xsd │ │ │ │ - */ │ │ │ │ - schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.0.0/context.xsd", │ │ │ │ + this.features = {}; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WMC.v1_0_0 │ │ │ │ - * Instances of this class are not created directly. Use the │ │ │ │ - * <OpenLayers.Format.WMC> constructor instead. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.WMC.v1.prototype.initialize.apply( │ │ │ │ - this, [options] │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ + this.handlers = {}; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: read_wmc_SRS │ │ │ │ - */ │ │ │ │ - read_wmc_SRS: function(layerContext, node) { │ │ │ │ - var srs = this.getChildValue(node); │ │ │ │ - if (typeof layerContext.projections != "object") { │ │ │ │ - layerContext.projections = {}; │ │ │ │ - } │ │ │ │ - var values = srs.split(/ +/); │ │ │ │ - for (var i = 0, len = values.length; i < len; i++) { │ │ │ │ - layerContext.projections[values[i]] = true; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + if (this.click) { │ │ │ │ + this.handlers.click = new OpenLayers.Handler.Click(this, { │ │ │ │ + click: this.selectClick │ │ │ │ + }, this.handlerOptions.click || {}); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: write_wmc_Layer │ │ │ │ - * Create a Layer node given a layer context object. This method adds │ │ │ │ - * elements specific to version 1.0.0. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * context - {Object} A layer context object.} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Element} A WMC Layer element node. │ │ │ │ - */ │ │ │ │ - write_wmc_Layer: function(context) { │ │ │ │ - var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply( │ │ │ │ - this, [context] │ │ │ │ + if (this.box) { │ │ │ │ + this.handlers.box = new OpenLayers.Handler.Box( │ │ │ │ + this, { │ │ │ │ + done: this.selectBox │ │ │ │ + }, │ │ │ │ + OpenLayers.Util.extend(this.handlerOptions.box, { │ │ │ │ + boxDivClassName: "olHandlerBoxSelectFeature" │ │ │ │ + }) │ │ │ │ ); │ │ │ │ + } │ │ │ │ │ │ │ │ - // optional SRS element(s) │ │ │ │ - if (context.srs) { │ │ │ │ - var projections = []; │ │ │ │ - for (var name in context.srs) { │ │ │ │ - projections.push(name); │ │ │ │ - } │ │ │ │ - node.appendChild(this.createElementDefaultNS("SRS", projections.join(" "))); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // optional FormatList element │ │ │ │ - node.appendChild(this.write_wmc_FormatList(context)); │ │ │ │ - │ │ │ │ - // optional StyleList element │ │ │ │ - node.appendChild(this.write_wmc_StyleList(context)); │ │ │ │ + if (this.hover) { │ │ │ │ + this.handlers.hover = new OpenLayers.Handler.Hover( │ │ │ │ + this, { │ │ │ │ + 'move': this.cancelHover, │ │ │ │ + 'pause': this.selectHover │ │ │ │ + }, │ │ │ │ + OpenLayers.Util.extend(this.handlerOptions.hover, { │ │ │ │ + 'delay': 250, │ │ │ │ + 'pixelTolerance': 2 │ │ │ │ + }) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // optional DimensionList element │ │ │ │ - if (context.dimensions) { │ │ │ │ - node.appendChild(this.write_wmc_DimensionList(context)); │ │ │ │ + /** │ │ │ │ + * Method: activate │ │ │ │ + * Activates the control. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The control was effectively activated. │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + if (!this.active) { │ │ │ │ + for (var i in this.handlers) { │ │ │ │ + this.handlers[i].activate(); │ │ │ │ } │ │ │ │ + } │ │ │ │ + return OpenLayers.Control.prototype.activate.apply( │ │ │ │ + this, arguments │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // OpenLayers specific properties go in an Extension element │ │ │ │ - node.appendChild(this.write_wmc_LayerExtension(context)); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMC.v1_0_0" │ │ │ │ - │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WCSCapabilities/v1.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WCSCapabilities.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WCSCapabilities.v1 │ │ │ │ - * Abstract class not to be instantiated directly. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WCSCapabilities.v1 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.XML, { │ │ │ │ - │ │ │ │ - regExes: { │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ - splitSpace: (/\s+/) │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ - */ │ │ │ │ - defaultPrefix: "wcs", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read capabilities data from a string, and return a list of coverages. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} List of named coverages. │ │ │ │ - */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - } │ │ │ │ - var raw = data; │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ - data = data.documentElement; │ │ │ │ + /** │ │ │ │ + * Method: deactivate │ │ │ │ + * Deactivates the control. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The control was effectively deactivated. │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + for (var i in this.handlers) { │ │ │ │ + this.handlers[i].deactivate(); │ │ │ │ } │ │ │ │ - var capabilities = {}; │ │ │ │ - this.readNode(data, capabilities); │ │ │ │ - return capabilities; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1" │ │ │ │ - │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WCSCapabilities/v1_1_0.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WCSCapabilities/v1.js │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WCSCapabilities/v1_1_0 │ │ │ │ - * Read WCS Capabilities version 1.1.0. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WCSCapabilities.v1> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WCSCapabilities.v1_1_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WCSCapabilities.v1, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ - */ │ │ │ │ - namespaces: { │ │ │ │ - wcs: "http://www.opengis.net/wcs/1.1", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance", │ │ │ │ - ows: "http://www.opengis.net/ows/1.1" │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: errorProperty │ │ │ │ - * {String} Which property of the returned object to check for in order to │ │ │ │ - * determine whether or not parsing has failed. In the case that the │ │ │ │ - * errorProperty is undefined on the returned object, the document will be │ │ │ │ - * run through an OGCExceptionReport parser. │ │ │ │ - */ │ │ │ │ - errorProperty: "operationsMetadata", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WCSCapabilities.v1_1_0 │ │ │ │ - * Create a new parser for WCS capabilities version 1.1.0. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wcs": OpenLayers.Util.applyDefaults({ │ │ │ │ - // In 1.0.0, this was WCS_Capabilties, in 1.1.0, it's Capabilities │ │ │ │ - "Capabilities": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "Contents": function(node, request) { │ │ │ │ - request.contentMetadata = []; │ │ │ │ - this.readChildNodes(node, request.contentMetadata); │ │ │ │ - }, │ │ │ │ - "CoverageSummary": function(node, contentMetadata) { │ │ │ │ - var coverageSummary = {}; │ │ │ │ - // Read the summary: │ │ │ │ - this.readChildNodes(node, coverageSummary); │ │ │ │ - │ │ │ │ - // Add it to the contentMetadata array: │ │ │ │ - contentMetadata.push(coverageSummary); │ │ │ │ - }, │ │ │ │ - "Identifier": function(node, coverageSummary) { │ │ │ │ - coverageSummary.identifier = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Title": function(node, coverageSummary) { │ │ │ │ - coverageSummary.title = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "Abstract": function(node, coverageSummary) { │ │ │ │ - coverageSummary["abstract"] = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "SupportedCRS": function(node, coverageSummary) { │ │ │ │ - var crs = this.getChildValue(node); │ │ │ │ - if (crs) { │ │ │ │ - if (!coverageSummary.supportedCRS) { │ │ │ │ - coverageSummary.supportedCRS = []; │ │ │ │ - } │ │ │ │ - coverageSummary.supportedCRS.push(crs); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "SupportedFormat": function(node, coverageSummary) { │ │ │ │ - var format = this.getChildValue(node); │ │ │ │ - if (format) { │ │ │ │ - if (!coverageSummary.supportedFormat) { │ │ │ │ - coverageSummary.supportedFormat = []; │ │ │ │ - } │ │ │ │ - coverageSummary.supportedFormat.push(format); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.WCSCapabilities.v1.prototype.readers["wcs"]), │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1_1_0" │ │ │ │ - │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WCSCapabilities/v1_0_0.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WCSCapabilities/v1.js │ │ │ │ - * @requires OpenLayers/Format/GML/v3.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WCSCapabilities/v1_0_0 │ │ │ │ - * Read WCS Capabilities version 1.0.0. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WCSCapabilities.v1> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WCSCapabilities.v1_0_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WCSCapabilities.v1, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WCSCapabilities.v1_0_0 │ │ │ │ - * Create a new parser for WCS capabilities version 1.0.0. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ - */ │ │ │ │ - namespaces: { │ │ │ │ - wcs: "http://www.opengis.net/wcs", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance", │ │ │ │ - ows: "http://www.opengis.net/ows" │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: errorProperty │ │ │ │ - * {String} Which property of the returned object to check for in order to │ │ │ │ - * determine whether or not parsing has failed. In the case that the │ │ │ │ - * errorProperty is undefined on the returned object, the document will be │ │ │ │ - * run through an OGCExceptionReport parser. │ │ │ │ - */ │ │ │ │ - errorProperty: "service", │ │ │ │ + } │ │ │ │ + return OpenLayers.Control.prototype.deactivate.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: { │ │ │ │ - "wcs": { │ │ │ │ - "WCS_Capabilities": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "Service": function(node, obj) { │ │ │ │ - obj.service = {}; │ │ │ │ - this.readChildNodes(node, obj.service); │ │ │ │ - }, │ │ │ │ - "name": function(node, service) { │ │ │ │ - service.name = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "label": function(node, service) { │ │ │ │ - service.label = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "keywords": function(node, service) { │ │ │ │ - service.keywords = []; │ │ │ │ - this.readChildNodes(node, service.keywords); │ │ │ │ - }, │ │ │ │ - "keyword": function(node, keywords) { │ │ │ │ - // Append the keyword to the keywords list │ │ │ │ - keywords.push(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "responsibleParty": function(node, service) { │ │ │ │ - service.responsibleParty = {}; │ │ │ │ - this.readChildNodes(node, service.responsibleParty); │ │ │ │ - }, │ │ │ │ - "individualName": function(node, responsibleParty) { │ │ │ │ - responsibleParty.individualName = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "organisationName": function(node, responsibleParty) { │ │ │ │ - responsibleParty.organisationName = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "positionName": function(node, responsibleParty) { │ │ │ │ - responsibleParty.positionName = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "contactInfo": function(node, responsibleParty) { │ │ │ │ - responsibleParty.contactInfo = {}; │ │ │ │ - this.readChildNodes(node, responsibleParty.contactInfo); │ │ │ │ - }, │ │ │ │ - "phone": function(node, contactInfo) { │ │ │ │ - contactInfo.phone = {}; │ │ │ │ - this.readChildNodes(node, contactInfo.phone); │ │ │ │ - }, │ │ │ │ - "voice": function(node, phone) { │ │ │ │ - phone.voice = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "facsimile": function(node, phone) { │ │ │ │ - phone.facsimile = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "address": function(node, contactInfo) { │ │ │ │ - contactInfo.address = {}; │ │ │ │ - this.readChildNodes(node, contactInfo.address); │ │ │ │ - }, │ │ │ │ - "deliveryPoint": function(node, address) { │ │ │ │ - address.deliveryPoint = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "city": function(node, address) { │ │ │ │ - address.city = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "postalCode": function(node, address) { │ │ │ │ - address.postalCode = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "country": function(node, address) { │ │ │ │ - address.country = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "electronicMailAddress": function(node, address) { │ │ │ │ - address.electronicMailAddress = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "fees": function(node, service) { │ │ │ │ - service.fees = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "accessConstraints": function(node, service) { │ │ │ │ - service.accessConstraints = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "ContentMetadata": function(node, obj) { │ │ │ │ - obj.contentMetadata = []; │ │ │ │ - this.readChildNodes(node, obj.contentMetadata); │ │ │ │ - }, │ │ │ │ - "CoverageOfferingBrief": function(node, contentMetadata) { │ │ │ │ - var coverageOfferingBrief = {}; │ │ │ │ - this.readChildNodes(node, coverageOfferingBrief); │ │ │ │ - contentMetadata.push(coverageOfferingBrief); │ │ │ │ - }, │ │ │ │ - "name": function(node, coverageOfferingBrief) { │ │ │ │ - coverageOfferingBrief.name = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "label": function(node, coverageOfferingBrief) { │ │ │ │ - coverageOfferingBrief.label = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "lonLatEnvelope": function(node, coverageOfferingBrief) { │ │ │ │ - var nodeList = this.getElementsByTagNameNS(node, "http://www.opengis.net/gml", "pos"); │ │ │ │ + /** │ │ │ │ + * Method: selectClick │ │ │ │ + * Called on click │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ + */ │ │ │ │ + selectClick: function(evt) { │ │ │ │ + var bounds = this.pixelToBounds(evt.xy); │ │ │ │ │ │ │ │ - // We expect two nodes here, to create the corners of a bounding box │ │ │ │ - if (nodeList.length == 2) { │ │ │ │ - var min = {}; │ │ │ │ - var max = {}; │ │ │ │ + this.setModifiers(evt); │ │ │ │ + this.request(bounds, { │ │ │ │ + single: this.single │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ │ │ │ │ - OpenLayers.Format.GML.v3.prototype.readers["gml"].pos.apply(this, [nodeList[0], min]); │ │ │ │ - OpenLayers.Format.GML.v3.prototype.readers["gml"].pos.apply(this, [nodeList[1], max]); │ │ │ │ + /** │ │ │ │ + * Method: selectBox │ │ │ │ + * Callback from the handlers.box set up when <box> selection is on │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * position - {<OpenLayers.Bounds>|Object} An OpenLayers.Bounds or │ │ │ │ + * an object with a 'left', 'bottom', 'right' and 'top' properties. │ │ │ │ + */ │ │ │ │ + selectBox: function(position) { │ │ │ │ + var bounds; │ │ │ │ + if (position instanceof OpenLayers.Bounds) { │ │ │ │ + var minXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.left, │ │ │ │ + y: position.bottom │ │ │ │ + }); │ │ │ │ + var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.right, │ │ │ │ + y: position.top │ │ │ │ + }); │ │ │ │ + bounds = new OpenLayers.Bounds( │ │ │ │ + minXY.lon, minXY.lat, maxXY.lon, maxXY.lat │ │ │ │ + ); │ │ │ │ │ │ │ │ - coverageOfferingBrief.lonLatEnvelope = {}; │ │ │ │ - coverageOfferingBrief.lonLatEnvelope.srsName = node.getAttribute("srsName"); │ │ │ │ - coverageOfferingBrief.lonLatEnvelope.min = min.points[0]; │ │ │ │ - coverageOfferingBrief.lonLatEnvelope.max = max.points[0]; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + } else { │ │ │ │ + if (this.click) { │ │ │ │ + // box without extent - let the click handler take care of it │ │ │ │ + return; │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1_0_0" │ │ │ │ - │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WFSCapabilities/v1.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WFSCapabilities.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WFSCapabilities.v1 │ │ │ │ - * Abstract class not to be instantiated directly. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WFSCapabilities.v1 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.XML, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ - */ │ │ │ │ - namespaces: { │ │ │ │ - wfs: "http://www.opengis.net/wfs", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance", │ │ │ │ - ows: "http://www.opengis.net/ows" │ │ │ │ - }, │ │ │ │ + bounds = this.pixelToBounds(position); │ │ │ │ + } │ │ │ │ + this.setModifiers(this.handlers.box.dragHandler.evt); │ │ │ │ + this.request(bounds); │ │ │ │ + }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Method: selectHover │ │ │ │ + * Callback from the handlers.hover set up when <hover> selection is on │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} event object with an xy property │ │ │ │ + */ │ │ │ │ + selectHover: function(evt) { │ │ │ │ + var bounds = this.pixelToBounds(evt.xy); │ │ │ │ + this.request(bounds, { │ │ │ │ + single: true, │ │ │ │ + hover: true │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: errorProperty │ │ │ │ - * {String} Which property of the returned object to check for in order to │ │ │ │ - * determine whether or not parsing has failed. In the case that the │ │ │ │ - * errorProperty is undefined on the returned object, the document will be │ │ │ │ - * run through an OGCExceptionReport parser. │ │ │ │ - */ │ │ │ │ - errorProperty: "featureTypeList", │ │ │ │ + /** │ │ │ │ + * Method: cancelHover │ │ │ │ + * Callback from the handlers.hover set up when <hover> selection is on │ │ │ │ + */ │ │ │ │ + cancelHover: function() { │ │ │ │ + if (this.hoverResponse) { │ │ │ │ + this.protocol.abort(this.hoverResponse); │ │ │ │ + this.hoverResponse = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ - */ │ │ │ │ - defaultPrefix: "wfs", │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WFSCapabilities.v1_1 │ │ │ │ - * Create an instance of one of the subclasses. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Method: request │ │ │ │ + * Sends a GetFeature request to the WFS │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} bounds for the request's BBOX filter │ │ │ │ + * options - {Object} additional options for this method. │ │ │ │ + * │ │ │ │ + * Supported options include: │ │ │ │ + * single - {Boolean} A single feature should be returned. │ │ │ │ + * Note that this will be ignored if the protocol does not │ │ │ │ + * return the geometries of the features. │ │ │ │ + * hover - {Boolean} Do the request for the hover handler. │ │ │ │ + */ │ │ │ │ + request: function(bounds, options) { │ │ │ │ + options = options || {}; │ │ │ │ + var filter = new OpenLayers.Filter.Spatial({ │ │ │ │ + type: this.filterType, │ │ │ │ + value: bounds │ │ │ │ + }); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read capabilities data from a string, and return a list of layers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} List of named layers. │ │ │ │ - */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - } │ │ │ │ - var raw = data; │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ - data = data.documentElement; │ │ │ │ - } │ │ │ │ - var capabilities = {}; │ │ │ │ - this.readNode(data, capabilities); │ │ │ │ - return capabilities; │ │ │ │ - }, │ │ │ │ + // Set the cursor to "wait" to tell the user we're working. │ │ │ │ + OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wfs": { │ │ │ │ - "WFS_Capabilities": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "FeatureTypeList": function(node, request) { │ │ │ │ - request.featureTypeList = { │ │ │ │ - featureTypes: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, request.featureTypeList); │ │ │ │ - }, │ │ │ │ - "FeatureType": function(node, featureTypeList) { │ │ │ │ - var featureType = {}; │ │ │ │ - this.readChildNodes(node, featureType); │ │ │ │ - featureTypeList.featureTypes.push(featureType); │ │ │ │ - }, │ │ │ │ - "Name": function(node, obj) { │ │ │ │ - var name = this.getChildValue(node); │ │ │ │ - if (name) { │ │ │ │ - var parts = name.split(":"); │ │ │ │ - obj.name = parts.pop(); │ │ │ │ - if (parts.length > 0) { │ │ │ │ - obj.featureNS = this.lookupNamespaceURI(node, parts[0]); │ │ │ │ + var response = this.protocol.read({ │ │ │ │ + maxFeatures: options.single == true ? this.maxFeatures : undefined, │ │ │ │ + filter: filter, │ │ │ │ + callback: function(result) { │ │ │ │ + if (result.success()) { │ │ │ │ + if (result.features.length) { │ │ │ │ + if (options.single == true) { │ │ │ │ + this.selectBestFeature(result.features, │ │ │ │ + bounds.getCenterLonLat(), options); │ │ │ │ + } else { │ │ │ │ + this.select(result.features); │ │ │ │ + } │ │ │ │ + } else if (options.hover) { │ │ │ │ + this.hoverSelect(); │ │ │ │ + } else { │ │ │ │ + this.events.triggerEvent("clickout"); │ │ │ │ + if (this.clickout) { │ │ │ │ + this.unselectAll(); │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - "Title": function(node, obj) { │ │ │ │ - var title = this.getChildValue(node); │ │ │ │ - if (title) { │ │ │ │ - obj.title = title; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Abstract": function(node, obj) { │ │ │ │ - var abst = this.getChildValue(node); │ │ │ │ - if (abst) { │ │ │ │ - obj["abstract"] = abst; │ │ │ │ + } │ │ │ │ + // Reset the cursor. │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ + }, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + if (options.hover == true) { │ │ │ │ + this.hoverResponse = response; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: selectBestFeature │ │ │ │ + * Selects the feature from an array of features that is the best match │ │ │ │ + * for the click position. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + * clickPosition - {<OpenLayers.LonLat>} │ │ │ │ + * options - {Object} additional options for this method │ │ │ │ + * │ │ │ │ + * Supported options include: │ │ │ │ + * hover - {Boolean} Do the selection for the hover handler. │ │ │ │ + */ │ │ │ │ + selectBestFeature: function(features, clickPosition, options) { │ │ │ │ + options = options || {}; │ │ │ │ + if (features.length) { │ │ │ │ + var point = new OpenLayers.Geometry.Point(clickPosition.lon, │ │ │ │ + clickPosition.lat); │ │ │ │ + var feature, resultFeature, dist; │ │ │ │ + var minDist = Number.MAX_VALUE; │ │ │ │ + for (var i = 0; i < features.length; ++i) { │ │ │ │ + feature = features[i]; │ │ │ │ + if (feature.geometry) { │ │ │ │ + dist = point.distanceTo(feature.geometry, { │ │ │ │ + edge: false │ │ │ │ + }); │ │ │ │ + if (dist < minDist) { │ │ │ │ + minDist = dist; │ │ │ │ + resultFeature = feature; │ │ │ │ + if (minDist == 0) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1" │ │ │ │ - │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WFSCapabilities/v1_1_0.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WFSCapabilities/v1.js │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1.js │ │ │ │ - */ │ │ │ │ + if (options.hover == true) { │ │ │ │ + this.hoverSelect(resultFeature); │ │ │ │ + } else { │ │ │ │ + this.select(resultFeature || features); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WFSCapabilities/v1_1_0 │ │ │ │ - * Read WFS Capabilities version 1.1.0. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WFSCapabilities> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WFSCapabilities.v1_1_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WFSCapabilities.v1, { │ │ │ │ + /** │ │ │ │ + * Method: setModifiers │ │ │ │ + * Sets the multiple and toggle modifiers according to the current event │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ + */ │ │ │ │ + setModifiers: function(evt) { │ │ │ │ + this.modifiers = { │ │ │ │ + multiple: this.multiple || (this.multipleKey && evt[this.multipleKey]), │ │ │ │ + toggle: this.toggle || (this.toggleKey && evt[this.toggleKey]) │ │ │ │ + }; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: regExes │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ - */ │ │ │ │ - regExes: { │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: select │ │ │ │ + * Add feature to the hash of selected features and trigger the │ │ │ │ + * featureselected and featuresselected events. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {<OpenLayers.Feature.Vector>} or an array of features │ │ │ │ + */ │ │ │ │ + select: function(features) { │ │ │ │ + if (!this.modifiers.multiple && !this.modifiers.toggle) { │ │ │ │ + this.unselectAll(); │ │ │ │ + } │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ + features = [features]; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WFSCapabilities.v1_1_0 │ │ │ │ - * Create a new parser for WFS capabilities version 1.1.0. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + var cont = this.events.triggerEvent("beforefeaturesselected", { │ │ │ │ + features: features │ │ │ │ + }); │ │ │ │ + if (cont !== false) { │ │ │ │ + var selectedFeatures = []; │ │ │ │ + var feature; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + feature = features[i]; │ │ │ │ + if (this.features[feature.fid || feature.id]) { │ │ │ │ + if (this.modifiers.toggle) { │ │ │ │ + this.unselect(this.features[feature.fid || feature.id]); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + cont = this.events.triggerEvent("beforefeatureselected", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + if (cont !== false) { │ │ │ │ + this.features[feature.fid || feature.id] = feature; │ │ │ │ + selectedFeatures.push(feature); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wfs": OpenLayers.Util.applyDefaults({ │ │ │ │ - "DefaultSRS": function(node, obj) { │ │ │ │ - var defaultSRS = this.getChildValue(node); │ │ │ │ - if (defaultSRS) { │ │ │ │ - obj.srs = defaultSRS; │ │ │ │ + this.events.triggerEvent("featureselected", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ } │ │ │ │ } │ │ │ │ - }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers["wfs"]), │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1.prototype.readers.ows │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_1_0" │ │ │ │ - │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Format/WFSCapabilities/v1_0_0.js │ │ │ │ - ====================================================================== */ │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("featuresselected", { │ │ │ │ + features: selectedFeatures │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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: hoverSelect │ │ │ │ + * Sets/unsets the <hoverFeature> │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} the feature to hover-select. │ │ │ │ + * If none is provided, the current <hoverFeature> will be nulled and │ │ │ │ + * the outfeature event will be triggered. │ │ │ │ + */ │ │ │ │ + hoverSelect: function(feature) { │ │ │ │ + var fid = feature ? feature.fid || feature.id : null; │ │ │ │ + var hfid = this.hoverFeature ? │ │ │ │ + this.hoverFeature.fid || this.hoverFeature.id : null; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Format/WFSCapabilities/v1.js │ │ │ │ - */ │ │ │ │ + if (hfid && hfid != fid) { │ │ │ │ + this.events.triggerEvent("outfeature", { │ │ │ │ + feature: this.hoverFeature │ │ │ │ + }); │ │ │ │ + this.hoverFeature = null; │ │ │ │ + } │ │ │ │ + if (fid && fid != hfid) { │ │ │ │ + this.events.triggerEvent("hoverfeature", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + this.hoverFeature = feature; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Format.WFSCapabilities/v1_0_0 │ │ │ │ - * Read WFS Capabilities version 1.0.0. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.WFSCapabilities.v1> │ │ │ │ - */ │ │ │ │ -OpenLayers.Format.WFSCapabilities.v1_0_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.WFSCapabilities.v1, { │ │ │ │ + /** │ │ │ │ + * Method: unselect │ │ │ │ + * Remove feature from the hash of selected features and trigger the │ │ │ │ + * featureunselected event. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + */ │ │ │ │ + unselect: function(feature) { │ │ │ │ + delete this.features[feature.fid || feature.id]; │ │ │ │ + this.events.triggerEvent("featureunselected", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.WFSCapabilities.v1_0_0 │ │ │ │ - * Create a new parser for WFS capabilities version 1.0.0. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Method: unselectAll │ │ │ │ + * Unselect all selected features. │ │ │ │ + */ │ │ │ │ + unselectAll: function() { │ │ │ │ + // we'll want an option to supress notification here │ │ │ │ + for (var fid in this.features) { │ │ │ │ + this.unselect(this.features[fid]); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "wfs": OpenLayers.Util.applyDefaults({ │ │ │ │ - "Service": function(node, capabilities) { │ │ │ │ - capabilities.service = {}; │ │ │ │ - this.readChildNodes(node, capabilities.service); │ │ │ │ - }, │ │ │ │ - "Fees": function(node, service) { │ │ │ │ - var fees = this.getChildValue(node); │ │ │ │ - if (fees && fees.toLowerCase() != "none") { │ │ │ │ - service.fees = fees; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "AccessConstraints": function(node, service) { │ │ │ │ - var constraints = this.getChildValue(node); │ │ │ │ - if (constraints && constraints.toLowerCase() != "none") { │ │ │ │ - service.accessConstraints = constraints; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "OnlineResource": function(node, service) { │ │ │ │ - var onlineResource = this.getChildValue(node); │ │ │ │ - if (onlineResource && onlineResource.toLowerCase() != "none") { │ │ │ │ - service.onlineResource = onlineResource; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Keywords": function(node, service) { │ │ │ │ - var keywords = this.getChildValue(node); │ │ │ │ - if (keywords && keywords.toLowerCase() != "none") { │ │ │ │ - service.keywords = keywords.split(', '); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Capability": function(node, capabilities) { │ │ │ │ - capabilities.capability = {}; │ │ │ │ - this.readChildNodes(node, capabilities.capability); │ │ │ │ - }, │ │ │ │ - "Request": function(node, obj) { │ │ │ │ - obj.request = {}; │ │ │ │ - this.readChildNodes(node, obj.request); │ │ │ │ - }, │ │ │ │ - "GetFeature": function(node, request) { │ │ │ │ - request.getfeature = { │ │ │ │ - href: {}, // DCPType │ │ │ │ - formats: [] // ResultFormat │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, request.getfeature); │ │ │ │ - }, │ │ │ │ - "ResultFormat": function(node, obj) { │ │ │ │ - var children = node.childNodes; │ │ │ │ - var childNode; │ │ │ │ - for (var i = 0; i < children.length; i++) { │ │ │ │ - childNode = children[i]; │ │ │ │ - if (childNode.nodeType == 1) { │ │ │ │ - obj.formats.push(childNode.nodeName); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "DCPType": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "HTTP": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj.href); │ │ │ │ - }, │ │ │ │ - "Get": function(node, obj) { │ │ │ │ - obj.get = node.getAttribute("onlineResource"); │ │ │ │ - }, │ │ │ │ - "Post": function(node, obj) { │ │ │ │ - obj.post = node.getAttribute("onlineResource"); │ │ │ │ - }, │ │ │ │ - "SRS": function(node, obj) { │ │ │ │ - var srs = this.getChildValue(node); │ │ │ │ - if (srs) { │ │ │ │ - obj.srs = srs; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.WFSCapabilities.v1.prototype.readers["wfs"]) │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the control. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + for (var i in this.handlers) { │ │ │ │ + this.handlers[i].setMap(map); │ │ │ │ + } │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_0_0" │ │ │ │ + /** │ │ │ │ + * Method: pixelToBounds │ │ │ │ + * Takes a pixel as argument and creates bounds after adding the │ │ │ │ + * <clickTolerance>. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * pixel - {<OpenLayers.Pixel>} │ │ │ │ + */ │ │ │ │ + pixelToBounds: function(pixel) { │ │ │ │ + var llPx = pixel.add(-this.clickTolerance / 2, this.clickTolerance / 2); │ │ │ │ + var urPx = pixel.add(this.clickTolerance / 2, -this.clickTolerance / 2); │ │ │ │ + var ll = this.map.getLonLatFromPixel(llPx); │ │ │ │ + var ur = this.map.getLonLatFromPixel(urPx); │ │ │ │ + return new OpenLayers.Bounds(ll.lon, ll.lat, ur.lon, ur.lat); │ │ │ │ + }, │ │ │ │ │ │ │ │ - }); │ │ │ │ + CLASS_NAME: "OpenLayers.Control.GetFeature" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/SOSCapabilities/v1_0_0.js │ │ │ │ + OpenLayers/Control/Attribution.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/SOSCapabilities.js │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1_1_0.js │ │ │ │ - * @requires OpenLayers/Format/GML/v3.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.SOSCapabilities.v1_0_0 │ │ │ │ - * Read SOS Capabilities version 1.0.0. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Control.Attribution │ │ │ │ + * The attribution control adds attribution from layers to the map display. │ │ │ │ + * It uses 'attribution' property of each layer. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.SOSCapabilities> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.SOSCapabilities.v1_0_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.SOSCapabilities, { │ │ │ │ +OpenLayers.Control.Attribution = │ │ │ │ + OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + * APIProperty: separator │ │ │ │ + * {String} String used to separate layers. │ │ │ │ */ │ │ │ │ - namespaces: { │ │ │ │ - ows: "http://www.opengis.net/ows/1.1", │ │ │ │ - sos: "http://www.opengis.net/sos/1.0", │ │ │ │ - gml: "http://www.opengis.net/gml", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink" │ │ │ │ - }, │ │ │ │ + separator: ", ", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: regExes │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ + * APIProperty: template │ │ │ │ + * {String} Template for the attribution. This has to include the substring │ │ │ │ + * "${layers}", which will be replaced by the layer specific │ │ │ │ + * attributions, separated by <separator>. The default is "${layers}". │ │ │ │ */ │ │ │ │ - regExes: { │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ - }, │ │ │ │ + template: "${layers}", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.SOSCapabilities.v1_0_0 │ │ │ │ - * Create a new parser for SOS capabilities version 1.0.0. │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Control.Attribution │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * options - {Object} Options for control. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ - this.options = options; │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * Destroy control. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.map.events.un({ │ │ │ │ + "removelayer": this.updateAttribution, │ │ │ │ + "addlayer": this.updateAttribution, │ │ │ │ + "changelayer": this.updateAttribution, │ │ │ │ + "changebaselayer": this.updateAttribution, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read capabilities data from a string, and return info about the SOS. │ │ │ │ + * Method: draw │ │ │ │ + * Initialize control. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Information about the SOS service. │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A reference to the DIV DOMElement containing the control │ │ │ │ */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - } │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ - data = data.documentElement; │ │ │ │ - } │ │ │ │ - var capabilities = {}; │ │ │ │ - this.readNode(data, capabilities); │ │ │ │ - return capabilities; │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ + │ │ │ │ + this.map.events.on({ │ │ │ │ + 'changebaselayer': this.updateAttribution, │ │ │ │ + 'changelayer': this.updateAttribution, │ │ │ │ + 'addlayer': this.updateAttribution, │ │ │ │ + 'removelayer': this.updateAttribution, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.updateAttribution(); │ │ │ │ + │ │ │ │ + return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: updateAttribution │ │ │ │ + * Update attribution string. │ │ │ │ */ │ │ │ │ - readers: { │ │ │ │ - "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ - "name": function(node, obj) { │ │ │ │ - obj.name = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "TimePeriod": function(node, obj) { │ │ │ │ - obj.timePeriod = {}; │ │ │ │ - this.readChildNodes(node, obj.timePeriod); │ │ │ │ - }, │ │ │ │ - "beginPosition": function(node, timePeriod) { │ │ │ │ - timePeriod.beginPosition = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "endPosition": function(node, timePeriod) { │ │ │ │ - timePeriod.endPosition = this.getChildValue(node); │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.GML.v3.prototype.readers["gml"]), │ │ │ │ - "sos": { │ │ │ │ - "Capabilities": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "Contents": function(node, obj) { │ │ │ │ - obj.contents = {}; │ │ │ │ - this.readChildNodes(node, obj.contents); │ │ │ │ - }, │ │ │ │ - "ObservationOfferingList": function(node, contents) { │ │ │ │ - contents.offeringList = {}; │ │ │ │ - this.readChildNodes(node, contents.offeringList); │ │ │ │ - }, │ │ │ │ - "ObservationOffering": function(node, offeringList) { │ │ │ │ - var id = this.getAttributeNS(node, this.namespaces.gml, "id"); │ │ │ │ - offeringList[id] = { │ │ │ │ - procedures: [], │ │ │ │ - observedProperties: [], │ │ │ │ - featureOfInterestIds: [], │ │ │ │ - responseFormats: [], │ │ │ │ - resultModels: [], │ │ │ │ - responseModes: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, offeringList[id]); │ │ │ │ - }, │ │ │ │ - "time": function(node, offering) { │ │ │ │ - offering.time = {}; │ │ │ │ - this.readChildNodes(node, offering.time); │ │ │ │ - }, │ │ │ │ - "procedure": function(node, offering) { │ │ │ │ - offering.procedures.push(this.getAttributeNS(node, │ │ │ │ - this.namespaces.xlink, "href")); │ │ │ │ - }, │ │ │ │ - "observedProperty": function(node, offering) { │ │ │ │ - offering.observedProperties.push(this.getAttributeNS(node, │ │ │ │ - this.namespaces.xlink, "href")); │ │ │ │ - }, │ │ │ │ - "featureOfInterest": function(node, offering) { │ │ │ │ - offering.featureOfInterestIds.push(this.getAttributeNS(node, │ │ │ │ - this.namespaces.xlink, "href")); │ │ │ │ - }, │ │ │ │ - "responseFormat": function(node, offering) { │ │ │ │ - offering.responseFormats.push(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "resultModel": function(node, offering) { │ │ │ │ - offering.resultModels.push(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "responseMode": function(node, offering) { │ │ │ │ - offering.responseModes.push(this.getChildValue(node)); │ │ │ │ + updateAttribution: function() { │ │ │ │ + var attributions = []; │ │ │ │ + if (this.map && this.map.layers) { │ │ │ │ + for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + if (layer.attribution && layer.getVisibility()) { │ │ │ │ + // add attribution only if attribution text is unique │ │ │ │ + if (OpenLayers.Util.indexOf( │ │ │ │ + attributions, layer.attribution) === -1) { │ │ │ │ + attributions.push(layer.attribution); │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"] │ │ │ │ + this.div.innerHTML = OpenLayers.String.format(this.template, { │ │ │ │ + layers: attributions.join(this.separator) │ │ │ │ + }); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.SOSCapabilities.v1_0_0" │ │ │ │ - │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Attribution" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/OWSContext/v0_3_1.js │ │ │ │ + OpenLayers/Control/SLDSelect.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XML.js │ │ │ │ - * @requires OpenLayers/Format/KML.js │ │ │ │ - * @requires OpenLayers/Format/GML.js │ │ │ │ - * @requires OpenLayers/Format/GML/v2.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Layer/WMS.js │ │ │ │ + * @requires OpenLayers/Handler/RegularPolygon.js │ │ │ │ + * @requires OpenLayers/Handler/Polygon.js │ │ │ │ + * @requires OpenLayers/Handler/Path.js │ │ │ │ + * @requires OpenLayers/Handler/Click.js │ │ │ │ + * @requires OpenLayers/Filter/Spatial.js │ │ │ │ * @requires OpenLayers/Format/SLD/v1_0_0.js │ │ │ │ - * @requires OpenLayers/Format/OWSContext.js │ │ │ │ - * @requires OpenLayers/Format/OWSCommon/v1_0_0.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.OWSContext.v0_3_1 │ │ │ │ - * Read and write OWSContext version 0.3.1. │ │ │ │ + * Class: OpenLayers.Control.SLDSelect │ │ │ │ + * Perform selections on WMS layers using Styled Layer Descriptor (SLD) │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.OWSContext.v0_3_1 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ +OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * selected - Triggered when a selection occurs. Listeners receive an │ │ │ │ + * event with *filters* and *layer* properties. Filters will be an │ │ │ │ + * array of OpenLayers.Filter objects created in order to perform │ │ │ │ + * the particular selection. │ │ │ │ */ │ │ │ │ - namespaces: { │ │ │ │ - owc: "http://www.opengis.net/ows-context", │ │ │ │ - gml: "http://www.opengis.net/gml", │ │ │ │ - kml: "http://www.opengis.net/kml/2.2", │ │ │ │ - ogc: "http://www.opengis.net/ogc", │ │ │ │ - ows: "http://www.opengis.net/ows", │ │ │ │ - sld: "http://www.opengis.net/sld", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: VERSION │ │ │ │ - * {String} 0.3.1 │ │ │ │ + * APIProperty: clearOnDeactivate │ │ │ │ + * {Boolean} Should the selection be cleared when the control is │ │ │ │ + * deactivated. Default value is false. │ │ │ │ */ │ │ │ │ - VERSION: "0.3.1", │ │ │ │ + clearOnDeactivate: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} Schema location │ │ │ │ + * APIProperty: layers │ │ │ │ + * {Array(<OpenLayers.Layer.WMS>)} The WMS layers this control will work │ │ │ │ + * on. │ │ │ │ */ │ │ │ │ - schemaLocation: "http://www.opengis.net/ows-context http://www.ogcnetwork.net/schemas/owc/0.3.1/owsContext.xsd", │ │ │ │ + layers: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ - * {String} Default namespace prefix to use. │ │ │ │ + * Property: callbacks │ │ │ │ + * {Object} The functions that are sent to the handler for callback │ │ │ │ */ │ │ │ │ - defaultPrefix: "owc", │ │ │ │ + callbacks: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: extractAttributes │ │ │ │ - * {Boolean} Extract attributes from GML. Default is true. │ │ │ │ + * APIProperty: selectionSymbolizer │ │ │ │ + * {Object} Determines the styling of the selected objects. Default is │ │ │ │ + * a selection in red. │ │ │ │ */ │ │ │ │ - extractAttributes: true, │ │ │ │ + selectionSymbolizer: { │ │ │ │ + 'Polygon': { │ │ │ │ + fillColor: '#FF0000', │ │ │ │ + stroke: false │ │ │ │ + }, │ │ │ │ + 'Line': { │ │ │ │ + strokeColor: '#FF0000', │ │ │ │ + strokeWidth: 2 │ │ │ │ + }, │ │ │ │ + 'Point': { │ │ │ │ + graphicName: 'square', │ │ │ │ + fillColor: '#FF0000', │ │ │ │ + pointRadius: 5 │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: layerOptions │ │ │ │ + * {Object} The options to apply to the selection layer, by default the │ │ │ │ + * selection layer will be kept out of the layer switcher. │ │ │ │ */ │ │ │ │ - xy: true, │ │ │ │ + layerOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: regExes │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ + * APIProperty: handlerOptions │ │ │ │ + * {Object} Used to set non-default properties on the control's handler │ │ │ │ */ │ │ │ │ - regExes: { │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: featureNS │ │ │ │ - * {String} The namespace uri to use for writing InlineGeometry │ │ │ │ + * APIProperty: sketchStyle │ │ │ │ + * {<OpenLayers.Style>|Object} Style or symbolizer to use for the sketch │ │ │ │ + * handler. The recommended way of styling the sketch layer, however, is │ │ │ │ + * to configure an <OpenLayers.StyleMap> in the layerOptions of the │ │ │ │ + * <handlerOptions>: │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * new OpenLayers.Control.SLDSelect(OpenLayers.Handler.Path, { │ │ │ │ + * handlerOptions: { │ │ │ │ + * layerOptions: { │ │ │ │ + * styleMap: new OpenLayers.StyleMap({ │ │ │ │ + * "default": {strokeColor: "yellow"} │ │ │ │ + * }) │ │ │ │ + * } │ │ │ │ + * } │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ */ │ │ │ │ - featureNS: "http://mapserver.gis.umn.edu/mapserver", │ │ │ │ + sketchStyle: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: featureType │ │ │ │ - * {String} The name to use as the feature type when writing out │ │ │ │ - * InlineGeometry │ │ │ │ + * APIProperty: wfsCache │ │ │ │ + * {Object} Cache to use for storing parsed results from │ │ │ │ + * <OpenLayers.Format.WFSDescribeFeatureType.read>. If not provided, │ │ │ │ + * these will be cached on the prototype. │ │ │ │ */ │ │ │ │ - featureType: 'vector', │ │ │ │ + wfsCache: {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: geometryName │ │ │ │ - * {String} The name to use for the geometry attribute when writing out │ │ │ │ - * InlineGeometry │ │ │ │ + * APIProperty: layerCache │ │ │ │ + * {Object} Cache to use for storing references to the selection layers. │ │ │ │ + * Normally each source layer will have exactly 1 selection layer of │ │ │ │ + * type OpenLayers.Layer.WMS. If not provided, layers will │ │ │ │ + * be cached on the prototype. Note that if <clearOnDeactivate> is │ │ │ │ + * true, the layer will no longer be cached after deactivating the │ │ │ │ + * control. │ │ │ │ */ │ │ │ │ - geometryName: 'geometry', │ │ │ │ + layerCache: {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: nestingLayerLookup │ │ │ │ - * {Object} Hashtable lookup for nesting layer nodes. Used while writing │ │ │ │ - * the OWS context document. It is necessary to keep track of the │ │ │ │ - * nestingPaths for which nesting layer nodes have already been │ │ │ │ - * created, so (nesting) layer nodes are added to those nodes. │ │ │ │ - * │ │ │ │ - * For example: │ │ │ │ + * Constructor: OpenLayers.Control.SLDSelect │ │ │ │ + * Create a new control for selecting features in WMS layers using │ │ │ │ + * Styled Layer Descriptor (SLD). │ │ │ │ * │ │ │ │ - * If there are three layers with nestingPaths: │ │ │ │ - * layer1.metadata.nestingPath = "a/b/" │ │ │ │ - * layer2.metadata.nestingPath = "a/b/" │ │ │ │ - * layer2.metadata.nestingPath = "a/c" │ │ │ │ + * Parameters: │ │ │ │ + * handler - {<OpenLayers.Class>} A sketch handler class. This determines │ │ │ │ + * the type of selection, e.g. box (<OpenLayers.Handler.Box>), point │ │ │ │ + * (<OpenLayers.Handler.Point>), path (<OpenLayers.Handler.Path>) or │ │ │ │ + * polygon (<OpenLayers.Handler.Polygon>) selection. To use circle │ │ │ │ + * type selection, use <OpenLayers.Handler.RegularPolygon> and pass │ │ │ │ + * the number of desired sides (e.g. 40) as "sides" property to the │ │ │ │ + * <handlerOptions>. │ │ │ │ + * options - {Object} An object containing all configuration properties for │ │ │ │ + * the control. │ │ │ │ * │ │ │ │ - * then a nesting layer node "a" should be created once and added │ │ │ │ - * to the resource list, a nesting layer node "b" should be created │ │ │ │ - * once and added under "a", and a nesting layer node "c" should be │ │ │ │ - * created and added under "a". The lookup paths for these nodes │ │ │ │ - * will be "a", "a/b", and "a/c" respectively. │ │ │ │ + * Valid options: │ │ │ │ + * layers - Array({<OpenLayers.Layer.WMS>}) The layers to perform the │ │ │ │ + * selection on. │ │ │ │ */ │ │ │ │ - nestingLayerLookup: null, │ │ │ │ + initialize: function(handler, options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + │ │ │ │ + this.callbacks = OpenLayers.Util.extend({ │ │ │ │ + done: this.select, │ │ │ │ + click: this.select │ │ │ │ + }, this.callbacks); │ │ │ │ + this.handlerOptions = this.handlerOptions || {}; │ │ │ │ + this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, { │ │ │ │ + displayInLayerSwitcher: false, │ │ │ │ + tileOptions: { │ │ │ │ + maxGetUrlLength: 2048 │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + if (this.sketchStyle) { │ │ │ │ + this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults( │ │ │ │ + this.handlerOptions.layerOptions, { │ │ │ │ + styleMap: new OpenLayers.StyleMap({ │ │ │ │ + "default": this.sketchStyle │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + this.handler = new handler(this, this.callbacks, this.handlerOptions); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.OWSContext.v0_3_1 │ │ │ │ - * Instances of this class are not created directly. Use the │ │ │ │ - * <OpenLayers.Format.OWSContext> constructor instead. │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Take care of things that are not handled in superclass. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + for (var key in this.layerCache) { │ │ │ │ + delete this.layerCache[key]; │ │ │ │ + } │ │ │ │ + for (var key in this.wfsCache) { │ │ │ │ + delete this.wfsCache[key]; │ │ │ │ + } │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: coupleLayerVisiblity │ │ │ │ + * Couple the selection layer and the source layer with respect to │ │ │ │ + * layer visibility. So if the source layer is turned off, the │ │ │ │ + * selection layer is also turned off. │ │ │ │ + * │ │ │ │ + * Context: │ │ │ │ + * - {<OpenLayers.Layer>} │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * evt - {Object} │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]); │ │ │ │ - OpenLayers.Format.GML.v2.prototype.setGeometryTypes.call(this); │ │ │ │ + coupleLayerVisiblity: function(evt) { │ │ │ │ + this.setVisibility(evt.object.getVisibility()); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setNestingPath │ │ │ │ - * Set the nestingPath property of the layer depending on the position │ │ │ │ - * of the layer in hierarchy of layers. │ │ │ │ + * Method: createSelectionLayer │ │ │ │ + * Creates a "clone" from the source layer in which the selection can │ │ │ │ + * be drawn. This ensures both the source layer and the selection are │ │ │ │ + * visible and not only the selection. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * l - {Object} An object that may have a layersContext array property. │ │ │ │ - * │ │ │ │ + * source - {<OpenLayers.Layer.WMS>} The source layer on which the selection │ │ │ │ + * is performed. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.WMS>} A WMS layer with maxGetUrlLength configured to 2048 │ │ │ │ + * since SLD selections can easily get quite long. │ │ │ │ */ │ │ │ │ - setNestingPath: function(l) { │ │ │ │ - if (l.layersContext) { │ │ │ │ - for (var i = 0, len = l.layersContext.length; i < len; i++) { │ │ │ │ - var layerContext = l.layersContext[i]; │ │ │ │ - var nPath = []; │ │ │ │ - var nTitle = l.title || ""; │ │ │ │ - if (l.metadata && l.metadata.nestingPath) { │ │ │ │ - nPath = l.metadata.nestingPath.slice(); │ │ │ │ - } │ │ │ │ - if (nTitle != "") { │ │ │ │ - nPath.push(nTitle); │ │ │ │ - } │ │ │ │ - layerContext.metadata.nestingPath = nPath; │ │ │ │ - if (layerContext.layersContext) { │ │ │ │ - this.setNestingPath(layerContext); │ │ │ │ - } │ │ │ │ + createSelectionLayer: function(source) { │ │ │ │ + // check if we already have a selection layer for the source layer │ │ │ │ + var selectionLayer; │ │ │ │ + if (!this.layerCache[source.id]) { │ │ │ │ + selectionLayer = new OpenLayers.Layer.WMS(source.name, │ │ │ │ + source.url, source.params, │ │ │ │ + OpenLayers.Util.applyDefaults( │ │ │ │ + this.layerOptions, │ │ │ │ + source.getOptions()) │ │ │ │ + ); │ │ │ │ + this.layerCache[source.id] = selectionLayer; │ │ │ │ + // make sure the layers are coupled wrt visibility, but only │ │ │ │ + // if they are not displayed in the layer switcher, because in │ │ │ │ + // that case the user cannot control visibility. │ │ │ │ + if (this.layerOptions.displayInLayerSwitcher === false) { │ │ │ │ + source.events.on({ │ │ │ │ + "visibilitychanged": this.coupleLayerVisiblity, │ │ │ │ + scope: selectionLayer │ │ │ │ + }); │ │ │ │ } │ │ │ │ + this.map.addLayer(selectionLayer); │ │ │ │ + } else { │ │ │ │ + selectionLayer = this.layerCache[source.id]; │ │ │ │ } │ │ │ │ + return selectionLayer; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: decomposeNestingPath │ │ │ │ - * Takes a nestingPath like "a/b/c" and decomposes it into subpaths: │ │ │ │ - * "a", "a/b", "a/b/c" │ │ │ │ + * Method: createSLD │ │ │ │ + * Create the SLD document for the layer using the supplied filters. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * nPath - {Array} the nesting path │ │ │ │ + * layer - {<OpenLayers.Layer.WMS>} │ │ │ │ + * filters - Array({<OpenLayers.Filter>}) The filters to be applied. │ │ │ │ + * geometryAttributes - Array({Object}) The geometry attributes of the │ │ │ │ + * layer. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * Array({String}) Array with subpaths, or empty array if there is nothing │ │ │ │ - * to decompose │ │ │ │ + * {String} The SLD document generated as a string. │ │ │ │ */ │ │ │ │ - decomposeNestingPath: function(nPath) { │ │ │ │ - var a = []; │ │ │ │ - if (OpenLayers.Util.isArray(nPath)) { │ │ │ │ - var path = nPath.slice(); │ │ │ │ - while (path.length > 0) { │ │ │ │ - a.push(path.slice()); │ │ │ │ - path.pop(); │ │ │ │ + createSLD: function(layer, filters, geometryAttributes) { │ │ │ │ + var sld = { │ │ │ │ + version: "1.0.0", │ │ │ │ + namedLayers: {} │ │ │ │ + }; │ │ │ │ + var layerNames = [layer.params.LAYERS].join(",").split(","); │ │ │ │ + for (var i = 0, len = layerNames.length; i < len; i++) { │ │ │ │ + var name = layerNames[i]; │ │ │ │ + sld.namedLayers[name] = { │ │ │ │ + name: name, │ │ │ │ + userStyles: [] │ │ │ │ + }; │ │ │ │ + var symbolizer = this.selectionSymbolizer; │ │ │ │ + var geometryAttribute = geometryAttributes[i]; │ │ │ │ + if (geometryAttribute.type.indexOf('Polygon') >= 0) { │ │ │ │ + symbolizer = { │ │ │ │ + Polygon: this.selectionSymbolizer['Polygon'] │ │ │ │ + }; │ │ │ │ + } else if (geometryAttribute.type.indexOf('LineString') >= 0) { │ │ │ │ + symbolizer = { │ │ │ │ + Line: this.selectionSymbolizer['Line'] │ │ │ │ + }; │ │ │ │ + } else if (geometryAttribute.type.indexOf('Point') >= 0) { │ │ │ │ + symbolizer = { │ │ │ │ + Point: this.selectionSymbolizer['Point'] │ │ │ │ + }; │ │ │ │ } │ │ │ │ - a.reverse(); │ │ │ │ + var filter = filters[i]; │ │ │ │ + sld.namedLayers[name].userStyles.push({ │ │ │ │ + name: 'default', │ │ │ │ + rules: [ │ │ │ │ + new OpenLayers.Rule({ │ │ │ │ + symbolizer: symbolizer, │ │ │ │ + filter: filter, │ │ │ │ + maxScaleDenominator: layer.options.minScale │ │ │ │ + }) │ │ │ │ + ] │ │ │ │ + }); │ │ │ │ } │ │ │ │ - return a; │ │ │ │ + return new OpenLayers.Format.SLD({ │ │ │ │ + srsName: this.map.getProjection() │ │ │ │ + }).write(sld); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Read OWS context data from a string or DOMElement, and return a list │ │ │ │ - * of layers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {String} or {DOMElement} data to read/parse. │ │ │ │ + * Method: parseDescribeLayer │ │ │ │ + * Parse the SLD WMS DescribeLayer response and issue the corresponding │ │ │ │ + * WFS DescribeFeatureType request │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Object} The context object with a flat layer list as a property named │ │ │ │ - * layersContext. │ │ │ │ + * request - {XMLHttpRequest} The request object. │ │ │ │ */ │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + parseDescribeLayer: function(request) { │ │ │ │ + var format = new OpenLayers.Format.WMSDescribeLayer(); │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText; │ │ │ │ } │ │ │ │ - if (data && data.nodeType == 9) { │ │ │ │ - data = data.documentElement; │ │ │ │ + var describeLayer = format.read(doc); │ │ │ │ + var typeNames = []; │ │ │ │ + var url = null; │ │ │ │ + for (var i = 0, len = describeLayer.length; i < len; i++) { │ │ │ │ + // perform a WFS DescribeFeatureType request │ │ │ │ + if (describeLayer[i].owsType == "WFS") { │ │ │ │ + typeNames.push(describeLayer[i].typeName); │ │ │ │ + url = describeLayer[i].owsURL; │ │ │ │ + } │ │ │ │ } │ │ │ │ - var context = {}; │ │ │ │ - this.readNode(data, context); │ │ │ │ - // since an OWSContext can be nested we need to go through this │ │ │ │ - // structure recursively │ │ │ │ - this.setNestingPath({ │ │ │ │ - layersContext: context.layersContext │ │ │ │ - }); │ │ │ │ - // after nesting path has been set, create a flat list of layers │ │ │ │ - var layers = []; │ │ │ │ - this.processLayer(layers, context); │ │ │ │ - delete context.layersContext; │ │ │ │ - context.layersContext = layers; │ │ │ │ - return context; │ │ │ │ + var options = { │ │ │ │ + url: url, │ │ │ │ + params: { │ │ │ │ + SERVICE: "WFS", │ │ │ │ + TYPENAME: typeNames.toString(), │ │ │ │ + REQUEST: "DescribeFeatureType", │ │ │ │ + VERSION: "1.0.0" │ │ │ │ + }, │ │ │ │ + callback: function(request) { │ │ │ │ + var format = new OpenLayers.Format.WFSDescribeFeatureType(); │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText; │ │ │ │ + } │ │ │ │ + var describeFeatureType = format.read(doc); │ │ │ │ + this.control.wfsCache[this.layer.id] = describeFeatureType; │ │ │ │ + this.control._queue && this.control.applySelection(); │ │ │ │ + }, │ │ │ │ + scope: this │ │ │ │ + }; │ │ │ │ + OpenLayers.Request.GET(options); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: processLayer │ │ │ │ - * Recursive function to get back a flat list of layers from the hierarchic │ │ │ │ - * layer structure. │ │ │ │ + * Method: getGeometryAttributes │ │ │ │ + * Look up the geometry attributes from the WFS DescribeFeatureType response │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * layerArray - {Array({Object})} Array of layerContext objects │ │ │ │ - * layer - {Object} layerContext object │ │ │ │ + * layer - {<OpenLayers.Layer.WMS>} The layer for which to look up the │ │ │ │ + * geometry attributes. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * Array({Object}) Array of geometry attributes │ │ │ │ */ │ │ │ │ - processLayer: function(layerArray, layer) { │ │ │ │ - if (layer.layersContext) { │ │ │ │ - for (var i = 0, len = layer.layersContext.length; i < len; i++) { │ │ │ │ - var l = layer.layersContext[i]; │ │ │ │ - layerArray.push(l); │ │ │ │ - if (l.layersContext) { │ │ │ │ - this.processLayer(layerArray, l); │ │ │ │ + getGeometryAttributes: function(layer) { │ │ │ │ + var result = []; │ │ │ │ + var cache = this.wfsCache[layer.id]; │ │ │ │ + for (var i = 0, len = cache.featureTypes.length; i < len; i++) { │ │ │ │ + var typeName = cache.featureTypes[i]; │ │ │ │ + var properties = typeName.properties; │ │ │ │ + for (var j = 0, lenj = properties.length; j < lenj; j++) { │ │ │ │ + var property = properties[j]; │ │ │ │ + var type = property.type; │ │ │ │ + if ((type.indexOf('LineString') >= 0) || │ │ │ │ + (type.indexOf('GeometryAssociationType') >= 0) || │ │ │ │ + (type.indexOf('GeometryPropertyType') >= 0) || │ │ │ │ + (type.indexOf('Point') >= 0) || │ │ │ │ + (type.indexOf('Polygon') >= 0)) { │ │ │ │ + result.push(property); │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ + return result; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * context - {Object} An object representing the map context. │ │ │ │ - * options - {Object} Optional object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} An OWS Context document string. │ │ │ │ + * APIMethod: activate │ │ │ │ + * Activate the control. Activating the control will perform a SLD WMS │ │ │ │ + * DescribeLayer request followed by a WFS DescribeFeatureType request │ │ │ │ + * so that the proper symbolizers can be chosen based on the geometry │ │ │ │ + * type. │ │ │ │ */ │ │ │ │ - write: function(context, options) { │ │ │ │ - var name = "OWSContext"; │ │ │ │ - this.nestingLayerLookup = {}; //start with empty lookup │ │ │ │ - options = options || {}; │ │ │ │ - OpenLayers.Util.applyDefaults(options, context); │ │ │ │ - var root = this.writeNode(name, options); │ │ │ │ - this.nestingLayerLookup = null; //clear lookup │ │ │ │ - this.setAttributeNS( │ │ │ │ - root, this.namespaces["xsi"], │ │ │ │ - "xsi:schemaLocation", this.schemaLocation │ │ │ │ - ); │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [root]); │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Control.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ + var layer = this.layers[i]; │ │ │ │ + if (layer && !this.wfsCache[layer.id]) { │ │ │ │ + var options = { │ │ │ │ + url: layer.url, │ │ │ │ + params: { │ │ │ │ + SERVICE: "WMS", │ │ │ │ + VERSION: layer.params.VERSION, │ │ │ │ + LAYERS: layer.params.LAYERS, │ │ │ │ + REQUEST: "DescribeLayer" │ │ │ │ + }, │ │ │ │ + callback: this.parseDescribeLayer, │ │ │ │ + scope: { │ │ │ │ + layer: layer, │ │ │ │ + control: this │ │ │ │ + } │ │ │ │ + }; │ │ │ │ + OpenLayers.Request.GET(options); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: deactivate │ │ │ │ + * Deactivate the control. If clearOnDeactivate is true, remove the │ │ │ │ + * selection layer(s). │ │ │ │ */ │ │ │ │ - readers: { │ │ │ │ - "kml": { │ │ │ │ - "Document": function(node, obj) { │ │ │ │ - obj.features = new OpenLayers.Format.KML({ │ │ │ │ - kmlns: this.namespaces.kml, │ │ │ │ - extractStyles: true │ │ │ │ - }).read(node); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "owc": { │ │ │ │ - "OWSContext": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "General": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "ResourceList": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "Layer": function(node, obj) { │ │ │ │ - var layerContext = { │ │ │ │ - metadata: {}, │ │ │ │ - visibility: (node.getAttribute("hidden") != "1"), │ │ │ │ - queryable: (node.getAttribute("queryable") == "1"), │ │ │ │ - opacity: ((node.getAttribute("opacity") != null) ? │ │ │ │ - parseFloat(node.getAttribute("opacity")) : null), │ │ │ │ - name: node.getAttribute("name"), │ │ │ │ - /* A category layer is a dummy layer meant for creating │ │ │ │ - hierarchies. It is not a physical layer in the │ │ │ │ - OpenLayers sense. The assumption we make here is that │ │ │ │ - category layers do not have a name attribute */ │ │ │ │ - categoryLayer: (node.getAttribute("name") == null), │ │ │ │ - formats: [], │ │ │ │ - styles: [] │ │ │ │ - }; │ │ │ │ - if (!obj.layersContext) { │ │ │ │ - obj.layersContext = []; │ │ │ │ - } │ │ │ │ - obj.layersContext.push(layerContext); │ │ │ │ - this.readChildNodes(node, layerContext); │ │ │ │ - }, │ │ │ │ - "InlineGeometry": function(node, obj) { │ │ │ │ - obj.features = []; │ │ │ │ - var elements = this.getElementsByTagNameNS(node, │ │ │ │ - this.namespaces.gml, "featureMember"); │ │ │ │ - var el; │ │ │ │ - if (elements.length >= 1) { │ │ │ │ - el = elements[0]; │ │ │ │ - } │ │ │ │ - if (el && el.firstChild) { │ │ │ │ - var featurenode = (el.firstChild.nextSibling) ? │ │ │ │ - el.firstChild.nextSibling : el.firstChild; │ │ │ │ - this.setNamespace("feature", featurenode.namespaceURI); │ │ │ │ - this.featureType = featurenode.localName || │ │ │ │ - featurenode.nodeName.split(":").pop(); │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "Server": function(node, obj) { │ │ │ │ - // when having multiple Server types, we prefer WMS │ │ │ │ - if ((!obj.service && !obj.version) || │ │ │ │ - (obj.service != │ │ │ │ - OpenLayers.Format.Context.serviceTypes.WMS)) { │ │ │ │ - obj.service = node.getAttribute("service"); │ │ │ │ - obj.version = node.getAttribute("version"); │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Control.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ + var layer = this.layers[i]; │ │ │ │ + if (layer && this.clearOnDeactivate === true) { │ │ │ │ + var layerCache = this.layerCache; │ │ │ │ + var selectionLayer = layerCache[layer.id]; │ │ │ │ + if (selectionLayer) { │ │ │ │ + layer.events.un({ │ │ │ │ + "visibilitychanged": this.coupleLayerVisiblity, │ │ │ │ + scope: selectionLayer │ │ │ │ + }); │ │ │ │ + selectionLayer.destroy(); │ │ │ │ + delete layerCache[layer.id]; │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - "Name": function(node, obj) { │ │ │ │ - obj.name = this.getChildValue(node); │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "Title": function(node, obj) { │ │ │ │ - obj.title = this.getChildValue(node); │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - }, │ │ │ │ - "StyleList": function(node, obj) { │ │ │ │ - this.readChildNodes(node, obj.styles); │ │ │ │ - }, │ │ │ │ - "Style": function(node, obj) { │ │ │ │ - var style = {}; │ │ │ │ - obj.push(style); │ │ │ │ - this.readChildNodes(node, style); │ │ │ │ - }, │ │ │ │ - "LegendURL": function(node, obj) { │ │ │ │ - var legend = {}; │ │ │ │ - obj.legend = legend; │ │ │ │ - this.readChildNodes(node, legend); │ │ │ │ - }, │ │ │ │ - "OnlineResource": function(node, obj) { │ │ │ │ - obj.url = this.getAttributeNS(node, this.namespaces.xlink, │ │ │ │ - "href"); │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ } │ │ │ │ - }, │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers.ows, │ │ │ │ - "gml": OpenLayers.Format.GML.v2.prototype.readers.gml, │ │ │ │ - "sld": OpenLayers.Format.SLD.v1_0_0.prototype.readers.sld, │ │ │ │ - "feature": OpenLayers.Format.GML.v2.prototype.readers.feature │ │ │ │ + } │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: setLayers │ │ │ │ + * Set the layers on which the selection should be performed. Call the │ │ │ │ + * setLayers method if the layer(s) to be used change and the same │ │ │ │ + * control should be used on a new set of layers. │ │ │ │ + * If the control is already active, it will be active after the new │ │ │ │ + * set of layers is set. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layers - {Array(<OpenLayers.Layer.WMS>)} The new set of layers on which │ │ │ │ + * the selection should be performed. │ │ │ │ */ │ │ │ │ - writers: { │ │ │ │ - "owc": { │ │ │ │ - "OWSContext": function(options) { │ │ │ │ - var node = this.createElementNSPlus("OWSContext", { │ │ │ │ - attributes: { │ │ │ │ - version: this.VERSION, │ │ │ │ - id: options.id || OpenLayers.Util.createUniqueID("OpenLayers_OWSContext_") │ │ │ │ - } │ │ │ │ + setLayers: function(layers) { │ │ │ │ + if (this.active) { │ │ │ │ + this.deactivate(); │ │ │ │ + this.layers = layers; │ │ │ │ + this.activate(); │ │ │ │ + } else { │ │ │ │ + this.layers = layers; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Function: createFilter │ │ │ │ + * Create the filter to be used in the SLD. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometryAttribute - {Object} Used to get the name of the geometry │ │ │ │ + * attribute which is needed for constructing the spatial filter. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} The geometry to use. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Filter.Spatial>} The spatial filter created. │ │ │ │ + */ │ │ │ │ + createFilter: function(geometryAttribute, geometry) { │ │ │ │ + var filter = null; │ │ │ │ + if (this.handler instanceof OpenLayers.Handler.RegularPolygon) { │ │ │ │ + // box │ │ │ │ + if (this.handler.irregular === true) { │ │ │ │ + filter = new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ + property: geometryAttribute.name, │ │ │ │ + value: geometry.getBounds() │ │ │ │ }); │ │ │ │ - this.writeNode("General", options, node); │ │ │ │ - this.writeNode("ResourceList", options, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "General": function(options) { │ │ │ │ - var node = this.createElementNSPlus("General"); │ │ │ │ - this.writeNode("ows:BoundingBox", options, node); │ │ │ │ - this.writeNode("ows:Title", options.title || 'OpenLayers OWSContext', node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "ResourceList": function(options) { │ │ │ │ - var node = this.createElementNSPlus("ResourceList"); │ │ │ │ - for (var i = 0, len = options.layers.length; i < len; i++) { │ │ │ │ - var layer = options.layers[i]; │ │ │ │ - var decomposedPath = this.decomposeNestingPath(layer.metadata.nestingPath); │ │ │ │ - this.writeNode("_Layer", { │ │ │ │ - layer: layer, │ │ │ │ - subPaths: decomposedPath │ │ │ │ - }, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Server": function(options) { │ │ │ │ - var node = this.createElementNSPlus("Server", { │ │ │ │ - attributes: { │ │ │ │ - version: options.version, │ │ │ │ - service: options.service │ │ │ │ - } │ │ │ │ + } else { │ │ │ │ + filter = new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ + property: geometryAttribute.name, │ │ │ │ + value: geometry │ │ │ │ }); │ │ │ │ - this.writeNode("OnlineResource", options, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "OnlineResource": function(options) { │ │ │ │ - var node = this.createElementNSPlus("OnlineResource", { │ │ │ │ - attributes: { │ │ │ │ - "xlink:href": options.url │ │ │ │ - } │ │ │ │ + } │ │ │ │ + } else if (this.handler instanceof OpenLayers.Handler.Polygon) { │ │ │ │ + filter = new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ + property: geometryAttribute.name, │ │ │ │ + value: geometry │ │ │ │ + }); │ │ │ │ + } else if (this.handler instanceof OpenLayers.Handler.Path) { │ │ │ │ + // if source layer is point based, use DWITHIN instead │ │ │ │ + if (geometryAttribute.type.indexOf('Point') >= 0) { │ │ │ │ + filter = new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.DWITHIN, │ │ │ │ + property: geometryAttribute.name, │ │ │ │ + distance: this.map.getExtent().getWidth() * 0.01, │ │ │ │ + distanceUnits: this.map.getUnits(), │ │ │ │ + value: geometry │ │ │ │ }); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "InlineGeometry": function(layer) { │ │ │ │ - var node = this.createElementNSPlus("InlineGeometry"), │ │ │ │ - dataExtent = layer.getDataExtent(); │ │ │ │ - if (dataExtent !== null) { │ │ │ │ - this.writeNode("gml:boundedBy", dataExtent, node); │ │ │ │ - } │ │ │ │ - for (var i = 0, len = layer.features.length; i < len; i++) { │ │ │ │ - this.writeNode("gml:featureMember", layer.features[i], node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "StyleList": function(styles) { │ │ │ │ - var node = this.createElementNSPlus("StyleList"); │ │ │ │ - for (var i = 0, len = styles.length; i < len; i++) { │ │ │ │ - this.writeNode("Style", styles[i], node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Style": function(style) { │ │ │ │ - var node = this.createElementNSPlus("Style"); │ │ │ │ - this.writeNode("Name", style, node); │ │ │ │ - this.writeNode("Title", style, node); │ │ │ │ - if (style.legend) { │ │ │ │ - this.writeNode("LegendURL", style, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Name": function(obj) { │ │ │ │ - var node = this.createElementNSPlus("Name", { │ │ │ │ - value: obj.name │ │ │ │ + } else { │ │ │ │ + filter = new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ + property: geometryAttribute.name, │ │ │ │ + value: geometry │ │ │ │ }); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Title": function(obj) { │ │ │ │ - var node = this.createElementNSPlus("Title", { │ │ │ │ - value: obj.title │ │ │ │ + } │ │ │ │ + } else if (this.handler instanceof OpenLayers.Handler.Click) { │ │ │ │ + if (geometryAttribute.type.indexOf('Polygon') >= 0) { │ │ │ │ + filter = new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ + property: geometryAttribute.name, │ │ │ │ + value: geometry │ │ │ │ }); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "LegendURL": function(style) { │ │ │ │ - var node = this.createElementNSPlus("LegendURL"); │ │ │ │ - this.writeNode("OnlineResource", style.legend, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "_WMS": function(layer) { │ │ │ │ - var node = this.createElementNSPlus("Layer", { │ │ │ │ - attributes: { │ │ │ │ - name: layer.params.LAYERS, │ │ │ │ - queryable: layer.queryable ? "1" : "0", │ │ │ │ - hidden: layer.visibility ? "0" : "1", │ │ │ │ - opacity: layer.hasOwnProperty("opacity") ? layer.opacity : null │ │ │ │ - } │ │ │ │ + } else { │ │ │ │ + filter = new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.DWITHIN, │ │ │ │ + property: geometryAttribute.name, │ │ │ │ + distance: this.map.getExtent().getWidth() * 0.01, │ │ │ │ + distanceUnits: this.map.getUnits(), │ │ │ │ + value: geometry │ │ │ │ }); │ │ │ │ - this.writeNode("ows:Title", layer.name, node); │ │ │ │ - this.writeNode("ows:OutputFormat", layer.params.FORMAT, node); │ │ │ │ - this.writeNode("Server", { │ │ │ │ - service: OpenLayers.Format.Context.serviceTypes.WMS, │ │ │ │ - version: layer.params.VERSION, │ │ │ │ - url: layer.url │ │ │ │ - }, node); │ │ │ │ - if (layer.metadata.styles && layer.metadata.styles.length > 0) { │ │ │ │ - this.writeNode("StyleList", layer.metadata.styles, node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "_Layer": function(options) { │ │ │ │ - var layer, subPaths, node, title; │ │ │ │ - layer = options.layer; │ │ │ │ - subPaths = options.subPaths; │ │ │ │ - node = null; │ │ │ │ - title = null; │ │ │ │ - // subPaths is an array of an array │ │ │ │ - // recursively calling _Layer writer eats up subPaths, until a │ │ │ │ - // real writer is called and nodes are returned. │ │ │ │ - if (subPaths.length > 0) { │ │ │ │ - var path = subPaths[0].join("/"); │ │ │ │ - var index = path.lastIndexOf("/"); │ │ │ │ - node = this.nestingLayerLookup[path]; │ │ │ │ - title = (index > 0) ? path.substring(index + 1, path.length) : path; │ │ │ │ - if (!node) { │ │ │ │ - // category layer │ │ │ │ - node = this.createElementNSPlus("Layer"); │ │ │ │ - this.writeNode("ows:Title", title, node); │ │ │ │ - this.nestingLayerLookup[path] = node; │ │ │ │ - } │ │ │ │ - options.subPaths.shift(); //remove a path after each call │ │ │ │ - this.writeNode("_Layer", options, node); │ │ │ │ - return node; │ │ │ │ - } else { │ │ │ │ - // write out the actual layer │ │ │ │ - if (layer instanceof OpenLayers.Layer.WMS) { │ │ │ │ - node = this.writeNode("_WMS", layer); │ │ │ │ - } else if (layer instanceof OpenLayers.Layer.Vector) { │ │ │ │ - if (layer.protocol instanceof OpenLayers.Protocol.WFS.v1) { │ │ │ │ - node = this.writeNode("_WFS", layer); │ │ │ │ - } else if (layer.protocol instanceof OpenLayers.Protocol.HTTP) { │ │ │ │ - if (layer.protocol.format instanceof OpenLayers.Format.GML) { │ │ │ │ - layer.protocol.format.version = "2.1.2"; │ │ │ │ - node = this.writeNode("_GML", layer); │ │ │ │ - } else if (layer.protocol.format instanceof OpenLayers.Format.KML) { │ │ │ │ - layer.protocol.format.version = "2.2"; │ │ │ │ - node = this.writeNode("_KML", layer); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - // write out as inline GML since we have no idea │ │ │ │ - // about the original Format │ │ │ │ - this.setNamespace("feature", this.featureNS); │ │ │ │ - node = this.writeNode("_InlineGeometry", layer); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return filter; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: select │ │ │ │ + * When the handler is done, use SLD_BODY on the selection layer to │ │ │ │ + * display the selection in the map. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {Object} or {<OpenLayers.Geometry>} │ │ │ │ + */ │ │ │ │ + select: function(geometry) { │ │ │ │ + this._queue = function() { │ │ │ │ + for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ + var layer = this.layers[i]; │ │ │ │ + var geometryAttributes = this.getGeometryAttributes(layer); │ │ │ │ + var filters = []; │ │ │ │ + for (var j = 0, lenj = geometryAttributes.length; j < lenj; j++) { │ │ │ │ + var geometryAttribute = geometryAttributes[j]; │ │ │ │ + if (geometryAttribute !== null) { │ │ │ │ + // from the click handler we will not get an actual │ │ │ │ + // geometry so transform │ │ │ │ + if (!(geometry instanceof OpenLayers.Geometry)) { │ │ │ │ + var point = this.map.getLonLatFromPixel( │ │ │ │ + geometry.xy); │ │ │ │ + geometry = new OpenLayers.Geometry.Point( │ │ │ │ + point.lon, point.lat); │ │ │ │ + } │ │ │ │ + var filter = this.createFilter(geometryAttribute, │ │ │ │ + geometry); │ │ │ │ + if (filter !== null) { │ │ │ │ + filters.push(filter); │ │ │ │ } │ │ │ │ } │ │ │ │ - if (layer.options.maxScale) { │ │ │ │ - this.writeNode("sld:MinScaleDenominator", │ │ │ │ - layer.options.maxScale, node); │ │ │ │ - } │ │ │ │ - if (layer.options.minScale) { │ │ │ │ - this.writeNode("sld:MaxScaleDenominator", │ │ │ │ - layer.options.minScale, node); │ │ │ │ - } │ │ │ │ - this.nestingLayerLookup[layer.name] = node; │ │ │ │ - return node; │ │ │ │ } │ │ │ │ - }, │ │ │ │ - "_WFS": function(layer) { │ │ │ │ - var node = this.createElementNSPlus("Layer", { │ │ │ │ - attributes: { │ │ │ │ - name: layer.protocol.featurePrefix + ":" + layer.protocol.featureType, │ │ │ │ - hidden: layer.visibility ? "0" : "1" │ │ │ │ - } │ │ │ │ + │ │ │ │ + var selectionLayer = this.createSelectionLayer(layer); │ │ │ │ + │ │ │ │ + this.events.triggerEvent("selected", { │ │ │ │ + layer: layer, │ │ │ │ + filters: filters │ │ │ │ }); │ │ │ │ - this.writeNode("ows:Title", layer.name, node); │ │ │ │ - this.writeNode("Server", { │ │ │ │ - service: OpenLayers.Format.Context.serviceTypes.WFS, │ │ │ │ - version: layer.protocol.version, │ │ │ │ - url: layer.protocol.url │ │ │ │ - }, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "_InlineGeometry": function(layer) { │ │ │ │ - var node = this.createElementNSPlus("Layer", { │ │ │ │ - attributes: { │ │ │ │ - name: this.featureType, │ │ │ │ - hidden: layer.visibility ? "0" : "1" │ │ │ │ - } │ │ │ │ + │ │ │ │ + var sld = this.createSLD(layer, filters, geometryAttributes); │ │ │ │ + │ │ │ │ + selectionLayer.mergeNewParams({ │ │ │ │ + SLD_BODY: sld │ │ │ │ }); │ │ │ │ - this.writeNode("ows:Title", layer.name, node); │ │ │ │ - this.writeNode("InlineGeometry", layer, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "_GML": function(layer) { │ │ │ │ - var node = this.createElementNSPlus("Layer"); │ │ │ │ - this.writeNode("ows:Title", layer.name, node); │ │ │ │ - this.writeNode("Server", { │ │ │ │ - service: OpenLayers.Format.Context.serviceTypes.GML, │ │ │ │ - url: layer.protocol.url, │ │ │ │ - version: layer.protocol.format.version │ │ │ │ - }, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "_KML": function(layer) { │ │ │ │ - var node = this.createElementNSPlus("Layer"); │ │ │ │ - this.writeNode("ows:Title", layer.name, node); │ │ │ │ - this.writeNode("Server", { │ │ │ │ - service: OpenLayers.Format.Context.serviceTypes.KML, │ │ │ │ - version: layer.protocol.format.version, │ │ │ │ - url: layer.protocol.url │ │ │ │ - }, node); │ │ │ │ - return node; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "gml": OpenLayers.Util.applyDefaults({ │ │ │ │ - "boundedBy": function(bounds) { │ │ │ │ - var node = this.createElementNSPlus("gml:boundedBy"); │ │ │ │ - this.writeNode("gml:Box", bounds, node); │ │ │ │ - return node; │ │ │ │ + delete this._queue; │ │ │ │ } │ │ │ │ - }, OpenLayers.Format.GML.v2.prototype.writers.gml), │ │ │ │ - "ows": OpenLayers.Format.OWSCommon.v1_0_0.prototype.writers.ows, │ │ │ │ - "sld": OpenLayers.Format.SLD.v1_0_0.prototype.writers.sld, │ │ │ │ - "feature": OpenLayers.Format.GML.v2.prototype.writers.feature │ │ │ │ + }; │ │ │ │ + this.applySelection(); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.OWSContext.v0_3_1" │ │ │ │ + /** │ │ │ │ + * Method: applySelection │ │ │ │ + * Checks if all required wfs data is cached, and applies the selection │ │ │ │ + */ │ │ │ │ + applySelection: function() { │ │ │ │ + var canApply = true; │ │ │ │ + for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ + if (!this.wfsCache[this.layers[i].id]) { │ │ │ │ + canApply = false; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + canApply && this._queue.call(this); │ │ │ │ + }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Control.SLDSelect" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/XLS/v1.js │ │ │ │ + OpenLayers/Control/Zoom.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XLS.js │ │ │ │ - * @requires OpenLayers/Format/GML/v3.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Events/buttonclick.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.XLS.v1 │ │ │ │ - * Superclass for XLS version 1 parsers. Only supports GeocodeRequest for now. │ │ │ │ + * Class: OpenLayers.Control.Zoom │ │ │ │ + * The Zoom control is a pair of +/- links for zooming in and out. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XML> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.XLS.v1 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: namespaces │ │ │ │ - * {Object} Mapping of namespace aliases to namespace URIs. │ │ │ │ - */ │ │ │ │ - namespaces: { │ │ │ │ - xls: "http://www.opengis.net/xls", │ │ │ │ - gml: "http://www.opengis.net/gml", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: regExes │ │ │ │ - * Compiled regular expressions for manipulating strings. │ │ │ │ - */ │ │ │ │ - regExes: { │ │ │ │ - trimSpace: (/^\s*|\s*$/g), │ │ │ │ - removeSpace: (/\s*/g), │ │ │ │ - splitSpace: (/\s+/), │ │ │ │ - trimComma: (/\s*,\s*/g) │ │ │ │ - }, │ │ │ │ +OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: zoomInText │ │ │ │ + * {String} │ │ │ │ + * Text for zoom-in link. Default is "+". │ │ │ │ */ │ │ │ │ - xy: true, │ │ │ │ + zoomInText: "+", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: defaultPrefix │ │ │ │ + * APIProperty: zoomInId │ │ │ │ + * {String} │ │ │ │ + * Instead of having the control create a zoom in link, you can provide │ │ │ │ + * the identifier for an anchor element already added to the document. │ │ │ │ + * By default, an element with id "olZoomInLink" will be searched for │ │ │ │ + * and used if it exists. │ │ │ │ */ │ │ │ │ - defaultPrefix: "xls", │ │ │ │ + zoomInId: "olZoomInLink", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} Schema location for a particular minor version. │ │ │ │ + * APIProperty: zoomOutText │ │ │ │ + * {String} │ │ │ │ + * Text for zoom-out link. Default is "\u2212". │ │ │ │ */ │ │ │ │ - schemaLocation: null, │ │ │ │ + zoomOutText: "\u2212", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.XLS.v1 │ │ │ │ - * Instances of this class are not created directly. Use the │ │ │ │ - * <OpenLayers.Format.XLS> constructor instead. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * APIProperty: zoomOutId │ │ │ │ + * {String} │ │ │ │ + * Instead of having the control create a zoom out link, you can provide │ │ │ │ + * the identifier for an anchor element already added to the document. │ │ │ │ + * By default, an element with id "olZoomOutLink" will be searched for │ │ │ │ + * and used if it exists. │ │ │ │ */ │ │ │ │ + zoomOutId: "olZoomOutLink", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {DOMElement} An XLS document element. │ │ │ │ - * options - {Object} Options for the reader. │ │ │ │ + * Method: draw │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} An object representing the XLSResponse. │ │ │ │ + * {DOMElement} A reference to the DOMElement containing the zoom links. │ │ │ │ */ │ │ │ │ - read: function(data, options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - var xls = {}; │ │ │ │ - this.readChildNodes(data, xls); │ │ │ │ - return xls; │ │ │ │ - }, │ │ │ │ + draw: function() { │ │ │ │ + var div = OpenLayers.Control.prototype.draw.apply(this), │ │ │ │ + links = this.getOrCreateLinks(div), │ │ │ │ + zoomIn = links.zoomIn, │ │ │ │ + zoomOut = links.zoomOut, │ │ │ │ + eventsInstance = this.map.events; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readers │ │ │ │ - * Contains public functions, grouped by namespace prefix, that will │ │ │ │ - * be applied when a namespaced node is found matching the function │ │ │ │ - * name. The function will be applied in the scope of this parser │ │ │ │ - * with two arguments: the node being read and a context object passed │ │ │ │ - * from the parent. │ │ │ │ - */ │ │ │ │ - readers: { │ │ │ │ - "xls": { │ │ │ │ - "XLS": function(node, xls) { │ │ │ │ - xls.version = node.getAttribute("version"); │ │ │ │ - this.readChildNodes(node, xls); │ │ │ │ - }, │ │ │ │ - "Response": function(node, xls) { │ │ │ │ - this.readChildNodes(node, xls); │ │ │ │ - }, │ │ │ │ - "GeocodeResponse": function(node, xls) { │ │ │ │ - xls.responseLists = []; │ │ │ │ - this.readChildNodes(node, xls); │ │ │ │ - }, │ │ │ │ - "GeocodeResponseList": function(node, xls) { │ │ │ │ - var responseList = { │ │ │ │ - features: [], │ │ │ │ - numberOfGeocodedAddresses: parseInt(node.getAttribute("numberOfGeocodedAddresses")) │ │ │ │ - }; │ │ │ │ - xls.responseLists.push(responseList); │ │ │ │ - this.readChildNodes(node, responseList); │ │ │ │ - }, │ │ │ │ - "GeocodedAddress": function(node, responseList) { │ │ │ │ - var feature = new OpenLayers.Feature.Vector(); │ │ │ │ - responseList.features.push(feature); │ │ │ │ - this.readChildNodes(node, feature); │ │ │ │ - // post-process geometry │ │ │ │ - feature.geometry = feature.components[0]; │ │ │ │ - }, │ │ │ │ - "GeocodeMatchCode": function(node, feature) { │ │ │ │ - feature.attributes.matchCode = { │ │ │ │ - accuracy: parseFloat(node.getAttribute("accuracy")), │ │ │ │ - matchType: node.getAttribute("matchType") │ │ │ │ - }; │ │ │ │ - }, │ │ │ │ - "Address": function(node, feature) { │ │ │ │ - var address = { │ │ │ │ - countryCode: node.getAttribute("countryCode"), │ │ │ │ - addressee: node.getAttribute("addressee"), │ │ │ │ - street: [], │ │ │ │ - place: [] │ │ │ │ - }; │ │ │ │ - feature.attributes.address = address; │ │ │ │ - this.readChildNodes(node, address); │ │ │ │ - }, │ │ │ │ - "freeFormAddress": function(node, address) { │ │ │ │ - address.freeFormAddress = this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "StreetAddress": function(node, address) { │ │ │ │ - this.readChildNodes(node, address); │ │ │ │ - }, │ │ │ │ - "Building": function(node, address) { │ │ │ │ - address.building = { │ │ │ │ - 'number': node.getAttribute("number"), │ │ │ │ - subdivision: node.getAttribute("subdivision"), │ │ │ │ - buildingName: node.getAttribute("buildingName") │ │ │ │ - }; │ │ │ │ - }, │ │ │ │ - "Street": function(node, address) { │ │ │ │ - // only support the built-in primitive type for now │ │ │ │ - address.street.push(this.getChildValue(node)); │ │ │ │ - }, │ │ │ │ - "Place": function(node, address) { │ │ │ │ - // type is one of CountrySubdivision, │ │ │ │ - // CountrySecondarySubdivision, Municipality or │ │ │ │ - // MunicipalitySubdivision │ │ │ │ - address.place[node.getAttribute("type")] = │ │ │ │ - this.getChildValue(node); │ │ │ │ - }, │ │ │ │ - "PostalCode": function(node, address) { │ │ │ │ - address.postalCode = this.getChildValue(node); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - "gml": OpenLayers.Format.GML.v3.prototype.readers.gml │ │ │ │ + if (zoomOut.parentNode !== div) { │ │ │ │ + eventsInstance = this.events; │ │ │ │ + eventsInstance.attachToElement(zoomOut.parentNode); │ │ │ │ + } │ │ │ │ + eventsInstance.register("buttonclick", this, this.onZoomClick); │ │ │ │ + │ │ │ │ + this.zoomInLink = zoomIn; │ │ │ │ + this.zoomOutLink = zoomOut; │ │ │ │ + return div; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write │ │ │ │ - * │ │ │ │ + * Method: getOrCreateLinks │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * request - {Object} An object representing the geocode request. │ │ │ │ + * el - {DOMElement} │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} The root of an XLS document. │ │ │ │ + * Return: │ │ │ │ + * {Object} Object with zoomIn and zoomOut properties referencing links. │ │ │ │ */ │ │ │ │ - write: function(request) { │ │ │ │ - return this.writers.xls.XLS.apply(this, [request]); │ │ │ │ + getOrCreateLinks: function(el) { │ │ │ │ + var zoomIn = document.getElementById(this.zoomInId), │ │ │ │ + zoomOut = document.getElementById(this.zoomOutId); │ │ │ │ + if (!zoomIn) { │ │ │ │ + zoomIn = document.createElement("a"); │ │ │ │ + zoomIn.href = "#zoomIn"; │ │ │ │ + zoomIn.appendChild(document.createTextNode(this.zoomInText)); │ │ │ │ + zoomIn.className = "olControlZoomIn"; │ │ │ │ + el.appendChild(zoomIn); │ │ │ │ + } │ │ │ │ + OpenLayers.Element.addClass(zoomIn, "olButton"); │ │ │ │ + if (!zoomOut) { │ │ │ │ + zoomOut = document.createElement("a"); │ │ │ │ + zoomOut.href = "#zoomOut"; │ │ │ │ + zoomOut.appendChild(document.createTextNode(this.zoomOutText)); │ │ │ │ + zoomOut.className = "olControlZoomOut"; │ │ │ │ + el.appendChild(zoomOut); │ │ │ │ + } │ │ │ │ + OpenLayers.Element.addClass(zoomOut, "olButton"); │ │ │ │ + return { │ │ │ │ + zoomIn: zoomIn, │ │ │ │ + zoomOut: zoomOut │ │ │ │ + }; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: onZoomClick │ │ │ │ + * Called when zoomin/out link is clicked. │ │ │ │ */ │ │ │ │ - writers: { │ │ │ │ - "xls": { │ │ │ │ - "XLS": function(request) { │ │ │ │ - var root = this.createElementNSPlus( │ │ │ │ - "xls:XLS", { │ │ │ │ - attributes: { │ │ │ │ - "version": this.VERSION, │ │ │ │ - "xsi:schemaLocation": this.schemaLocation │ │ │ │ - } │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - this.writeNode("RequestHeader", request.header, root); │ │ │ │ - this.writeNode("Request", request, root); │ │ │ │ - return root; │ │ │ │ - }, │ │ │ │ - "RequestHeader": function(header) { │ │ │ │ - return this.createElementNSPlus("xls:RequestHeader"); │ │ │ │ - }, │ │ │ │ - "Request": function(request) { │ │ │ │ - var node = this.createElementNSPlus("xls:Request", { │ │ │ │ - attributes: { │ │ │ │ - methodName: "GeocodeRequest", │ │ │ │ - requestID: request.requestID || "", │ │ │ │ - version: this.VERSION │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - this.writeNode("GeocodeRequest", request.addresses, node); │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "GeocodeRequest": function(addresses) { │ │ │ │ - var node = this.createElementNSPlus("xls:GeocodeRequest"); │ │ │ │ - for (var i = 0, len = addresses.length; i < len; i++) { │ │ │ │ - this.writeNode("Address", addresses[i], node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Address": function(address) { │ │ │ │ - var node = this.createElementNSPlus("xls:Address", { │ │ │ │ - attributes: { │ │ │ │ - countryCode: address.countryCode │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - if (address.freeFormAddress) { │ │ │ │ - this.writeNode("freeFormAddress", address.freeFormAddress, node); │ │ │ │ - } else { │ │ │ │ - if (address.street) { │ │ │ │ - this.writeNode("StreetAddress", address, node); │ │ │ │ - } │ │ │ │ - if (address.municipality) { │ │ │ │ - this.writeNode("Municipality", address.municipality, node); │ │ │ │ - } │ │ │ │ - if (address.countrySubdivision) { │ │ │ │ - this.writeNode("CountrySubdivision", address.countrySubdivision, node); │ │ │ │ - } │ │ │ │ - if (address.postalCode) { │ │ │ │ - this.writeNode("PostalCode", address.postalCode, node); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "freeFormAddress": function(freeFormAddress) { │ │ │ │ - return this.createElementNSPlus("freeFormAddress", { │ │ │ │ - value: freeFormAddress │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "StreetAddress": function(address) { │ │ │ │ - var node = this.createElementNSPlus("xls:StreetAddress"); │ │ │ │ - if (address.building) { │ │ │ │ - this.writeNode(node, "Building", address.building); │ │ │ │ - } │ │ │ │ - var street = address.street; │ │ │ │ - if (!(OpenLayers.Util.isArray(street))) { │ │ │ │ - street = [street]; │ │ │ │ - } │ │ │ │ - for (var i = 0, len = street.length; i < len; i++) { │ │ │ │ - this.writeNode("Street", street[i], node); │ │ │ │ - } │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - "Building": function(building) { │ │ │ │ - return this.createElementNSPlus("xls:Building", { │ │ │ │ - attributes: { │ │ │ │ - "number": building["number"], │ │ │ │ - "subdivision": building.subdivision, │ │ │ │ - "buildingName": building.buildingName │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "Street": function(street) { │ │ │ │ - return this.createElementNSPlus("xls:Street", { │ │ │ │ - value: street │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "Municipality": function(municipality) { │ │ │ │ - return this.createElementNSPlus("xls:Place", { │ │ │ │ - attributes: { │ │ │ │ - type: "Municipality" │ │ │ │ - }, │ │ │ │ - value: municipality │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "CountrySubdivision": function(countrySubdivision) { │ │ │ │ - return this.createElementNSPlus("xls:Place", { │ │ │ │ - attributes: { │ │ │ │ - type: "CountrySubdivision" │ │ │ │ - }, │ │ │ │ - value: countrySubdivision │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - "PostalCode": function(postalCode) { │ │ │ │ - return this.createElementNSPlus("xls:PostalCode", { │ │ │ │ - value: postalCode │ │ │ │ - }); │ │ │ │ - } │ │ │ │ + onZoomClick: function(evt) { │ │ │ │ + var button = evt.buttonElement; │ │ │ │ + if (button === this.zoomInLink) { │ │ │ │ + this.map.zoomIn(); │ │ │ │ + } else if (button === this.zoomOutLink) { │ │ │ │ + this.map.zoomOut(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.XLS.v1" │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * Clean up. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + if (this.map) { │ │ │ │ + this.map.events.unregister("buttonclick", this, this.onZoomClick); │ │ │ │ + } │ │ │ │ + delete this.zoomInLink; │ │ │ │ + delete this.zoomOutLink; │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this); │ │ │ │ + }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Zoom" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/XLS/v1_1_0.js │ │ │ │ + OpenLayers/Control/ModifyFeature.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Format/XLS/v1.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Handler/Drag.js │ │ │ │ + * @requires OpenLayers/Handler/Keyboard.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.XLS.v1_1_0 │ │ │ │ - * Read / write XLS version 1.1.0. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Format.XLS.v1> │ │ │ │ + * Class: OpenLayers.Control.ModifyFeature │ │ │ │ + * Control to modify features. When activated, a click renders the vertices │ │ │ │ + * of a feature - these vertices can then be dragged. By default, the │ │ │ │ + * delete key will delete the vertex under the mouse. New features are │ │ │ │ + * added by dragging "virtual vertices" between vertices. Create a new │ │ │ │ + * control with the <OpenLayers.Control.ModifyFeature> constructor. │ │ │ │ + * │ │ │ │ + * Inherits From: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.XLS.v1_1_0 = OpenLayers.Class( │ │ │ │ - OpenLayers.Format.XLS.v1, { │ │ │ │ +OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: VERSION │ │ │ │ - * {String} 1.1 │ │ │ │ - */ │ │ │ │ - VERSION: "1.1", │ │ │ │ + /** │ │ │ │ + * APIProperty: documentDrag │ │ │ │ + * {Boolean} If set to true, dragging vertices will continue even if the │ │ │ │ + * mouse cursor leaves the map viewport. Default is false. │ │ │ │ + */ │ │ │ │ + documentDrag: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: schemaLocation │ │ │ │ - * {String} http://www.opengis.net/xls │ │ │ │ - * http://schemas.opengis.net/ols/1.1.0/LocationUtilityService.xsd │ │ │ │ - */ │ │ │ │ - schemaLocation: "http://www.opengis.net/xls http://schemas.opengis.net/ols/1.1.0/LocationUtilityService.xsd", │ │ │ │ + /** │ │ │ │ + * APIProperty: geometryTypes │ │ │ │ + * {Array(String)} To restrict modification to a limited set of geometry │ │ │ │ + * types, send a list of strings corresponding to the geometry class │ │ │ │ + * names. │ │ │ │ + */ │ │ │ │ + geometryTypes: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.XLS.v1_1_0 │ │ │ │ - * Instances of this class are not created directly. Use the │ │ │ │ - * <OpenLayers.Format.XLS> constructor instead. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * APIProperty: clickout │ │ │ │ + * {Boolean} Unselect features when clicking outside any feature. │ │ │ │ + * Default is true. │ │ │ │ + */ │ │ │ │ + clickout: true, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.XLS.v1_1_0" │ │ │ │ + /** │ │ │ │ + * APIProperty: toggle │ │ │ │ + * {Boolean} Unselect a selected feature on click. │ │ │ │ + * Default is true. │ │ │ │ + */ │ │ │ │ + toggle: true, │ │ │ │ │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * APIProperty: standalone │ │ │ │ + * {Boolean} Set to true to create a control without SelectFeature │ │ │ │ + * capabilities. Default is false. If standalone is true, to modify │ │ │ │ + * a feature, call the <selectFeature> method with the target feature. │ │ │ │ + * Note that you must call the <unselectFeature> method to finish │ │ │ │ + * feature modification in standalone mode (before starting to modify │ │ │ │ + * another feature). │ │ │ │ + */ │ │ │ │ + standalone: false, │ │ │ │ │ │ │ │ -// Support non standard implementation │ │ │ │ -OpenLayers.Format.XLS.v1_1 = OpenLayers.Format.XLS.v1_1_0; │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Tile/Image/IFrame.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * Property: layer │ │ │ │ + * {<OpenLayers.Layer.Vector>} │ │ │ │ + */ │ │ │ │ + layer: 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: feature │ │ │ │ + * {<OpenLayers.Feature.Vector>} Feature currently available for modification. │ │ │ │ + */ │ │ │ │ + feature: null, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Property: vertex │ │ │ │ + * {<OpenLayers.Feature.Vector>} Vertex currently being modified. │ │ │ │ + */ │ │ │ │ + vertex: null, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Tile/Image.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Property: vertices │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} Verticies currently available │ │ │ │ + * for dragging. │ │ │ │ + */ │ │ │ │ + vertices: null, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Tile.Image.IFrame │ │ │ │ - * Mixin for tiles that use form-encoded POST requests to get images from │ │ │ │ - * remote services. Images will be loaded using HTTP-POST into an IFrame. │ │ │ │ - * │ │ │ │ - * This mixin will be applied to <OpenLayers.Tile.Image> instances │ │ │ │ - * configured with <OpenLayers.Tile.Image.maxGetUrlLength> set. │ │ │ │ - */ │ │ │ │ -OpenLayers.Tile.Image.IFrame = { │ │ │ │ + /** │ │ │ │ + * Property: virtualVertices │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} Virtual vertices in the middle │ │ │ │ + * of each edge. │ │ │ │ + */ │ │ │ │ + virtualVertices: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Property: handlers │ │ │ │ + * {Object} │ │ │ │ */ │ │ │ │ - useIFrame: null, │ │ │ │ + handlers: 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. │ │ │ │ + * APIProperty: deleteCodes │ │ │ │ + * {Array(Integer)} Keycodes for deleting verticies. Set to null to disable │ │ │ │ + * vertex deltion by keypress. If non-null, keypresses with codes │ │ │ │ + * in this array will delete vertices under the mouse. Default │ │ │ │ + * is 46 and 68, the 'delete' and lowercase 'd' keys. │ │ │ │ */ │ │ │ │ - blankImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAQAIBRAA7", │ │ │ │ + deleteCodes: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ - * Set useIFrame in the instance, and operate the image/iframe switch. │ │ │ │ - * Then call Tile.Image.draw. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} │ │ │ │ + * APIProperty: virtualStyle │ │ │ │ + * {Object} A symbolizer to be used for virtual vertices. │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this); │ │ │ │ - if (draw) { │ │ │ │ + virtualStyle: null, │ │ │ │ │ │ │ │ - // 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); │ │ │ │ + /** │ │ │ │ + * APIProperty: vertexRenderIntent │ │ │ │ + * {String} The renderIntent to use for vertices. If no <virtualStyle> is │ │ │ │ + * provided, this renderIntent will also be used for virtual vertices, with │ │ │ │ + * a fillOpacity and strokeOpacity of 0.3. Default is null, which means │ │ │ │ + * that the layer's default style will be used for vertices. │ │ │ │ + */ │ │ │ │ + vertexRenderIntent: null, │ │ │ │ │ │ │ │ - var usedIFrame = this.useIFrame; │ │ │ │ - this.useIFrame = this.maxGetUrlLength !== null && │ │ │ │ - !this.layer.async && │ │ │ │ - url.length > this.maxGetUrlLength; │ │ │ │ + /** │ │ │ │ + * APIProperty: mode │ │ │ │ + * {Integer} Bitfields specifying the modification mode. Defaults to │ │ │ │ + * OpenLayers.Control.ModifyFeature.RESHAPE. To set the mode to a │ │ │ │ + * combination of options, use the | operator. For example, to allow │ │ │ │ + * the control to both resize and rotate features, use the following │ │ │ │ + * syntax │ │ │ │ + * (code) │ │ │ │ + * control.mode = OpenLayers.Control.ModifyFeature.RESIZE | │ │ │ │ + * OpenLayers.Control.ModifyFeature.ROTATE; │ │ │ │ + * (end) │ │ │ │ + */ │ │ │ │ + mode: null, │ │ │ │ │ │ │ │ - var fromIFrame = usedIFrame && !this.useIFrame; │ │ │ │ - var toIFrame = !usedIFrame && this.useIFrame; │ │ │ │ + /** │ │ │ │ + * APIProperty: createVertices │ │ │ │ + * {Boolean} Create new vertices by dragging the virtual vertices │ │ │ │ + * in the middle of each edge. Default is true. │ │ │ │ + */ │ │ │ │ + createVertices: true, │ │ │ │ │ │ │ │ - if (fromIFrame || toIFrame) { │ │ │ │ + /** │ │ │ │ + * Property: modified │ │ │ │ + * {Boolean} The currently selected feature has been modified. │ │ │ │ + */ │ │ │ │ + modified: false, │ │ │ │ │ │ │ │ - // Switching between GET (image) and POST (iframe). │ │ │ │ + /** │ │ │ │ + * Property: radiusHandle │ │ │ │ + * {<OpenLayers.Feature.Vector>} A handle for rotating/resizing a feature. │ │ │ │ + */ │ │ │ │ + radiusHandle: null, │ │ │ │ │ │ │ │ - // 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. │ │ │ │ + /** │ │ │ │ + * Property: dragHandle │ │ │ │ + * {<OpenLayers.Feature.Vector>} A handle for dragging a feature. │ │ │ │ + */ │ │ │ │ + dragHandle: null, │ │ │ │ │ │ │ │ - if (this.imgDiv && this.imgDiv.parentNode === this.frame) { │ │ │ │ - this.frame.removeChild(this.imgDiv); │ │ │ │ - } │ │ │ │ - this.imgDiv = null; │ │ │ │ + /** │ │ │ │ + * APIProperty: onModificationStart │ │ │ │ + * {Function} *Deprecated*. Register for "beforefeaturemodified" instead. │ │ │ │ + * The "beforefeaturemodified" event is triggered on the layer before │ │ │ │ + * any modification begins. │ │ │ │ + * │ │ │ │ + * Optional function to be called when a feature is selected │ │ │ │ + * to be modified. The function should expect to be called with a │ │ │ │ + * feature. This could be used for example to allow to lock the │ │ │ │ + * feature on server-side. │ │ │ │ + */ │ │ │ │ + onModificationStart: function() {}, │ │ │ │ │ │ │ │ - // And if we had an iframe we also remove the event pane. │ │ │ │ + /** │ │ │ │ + * APIProperty: onModification │ │ │ │ + * {Function} *Deprecated*. Register for "featuremodified" instead. │ │ │ │ + * The "featuremodified" event is triggered on the layer with each │ │ │ │ + * feature modification. │ │ │ │ + * │ │ │ │ + * Optional function to be called when a feature has been │ │ │ │ + * modified. The function should expect to be called with a feature. │ │ │ │ + */ │ │ │ │ + onModification: function() {}, │ │ │ │ │ │ │ │ - if (fromIFrame) { │ │ │ │ - this.frame.removeChild(this.frame.firstChild); │ │ │ │ + /** │ │ │ │ + * APIProperty: onModificationEnd │ │ │ │ + * {Function} *Deprecated*. Register for "afterfeaturemodified" instead. │ │ │ │ + * The "afterfeaturemodified" event is triggered on the layer after │ │ │ │ + * a feature has been modified. │ │ │ │ + * │ │ │ │ + * Optional function to be called when a feature is finished │ │ │ │ + * being modified. The function should expect to be called with a │ │ │ │ + * feature. │ │ │ │ + */ │ │ │ │ + onModificationEnd: function() {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.ModifyFeature │ │ │ │ + * Create a new modify feature control. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that │ │ │ │ + * will be modified. │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * control. │ │ │ │ + */ │ │ │ │ + initialize: function(layer, options) { │ │ │ │ + options = options || {}; │ │ │ │ + this.layer = layer; │ │ │ │ + this.vertices = []; │ │ │ │ + this.virtualVertices = []; │ │ │ │ + this.virtualStyle = OpenLayers.Util.extend({}, │ │ │ │ + this.layer.style || │ │ │ │ + this.layer.styleMap.createSymbolizer(null, options.vertexRenderIntent) │ │ │ │ + ); │ │ │ │ + this.virtualStyle.fillOpacity = 0.3; │ │ │ │ + this.virtualStyle.strokeOpacity = 0.3; │ │ │ │ + this.deleteCodes = [46, 68]; │ │ │ │ + this.mode = OpenLayers.Control.ModifyFeature.RESHAPE; │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + if (!(OpenLayers.Util.isArray(this.deleteCodes))) { │ │ │ │ + this.deleteCodes = [this.deleteCodes]; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // configure the drag handler │ │ │ │ + var dragCallbacks = { │ │ │ │ + down: function(pixel) { │ │ │ │ + this.vertex = null; │ │ │ │ + var feature = this.layer.getFeatureFromEvent( │ │ │ │ + this.handlers.drag.evt); │ │ │ │ + if (feature) { │ │ │ │ + this.dragStart(feature); │ │ │ │ + } else if (this.clickout) { │ │ │ │ + this._unselect = this.feature; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + move: function(pixel) { │ │ │ │ + delete this._unselect; │ │ │ │ + if (this.vertex) { │ │ │ │ + this.dragVertex(this.vertex, pixel); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + up: function() { │ │ │ │ + this.handlers.drag.stopDown = false; │ │ │ │ + if (this._unselect) { │ │ │ │ + this.unselectFeature(this._unselect); │ │ │ │ + delete this._unselect; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + done: function(pixel) { │ │ │ │ + if (this.vertex) { │ │ │ │ + this.dragComplete(this.vertex); │ │ │ │ } │ │ │ │ } │ │ │ │ + }; │ │ │ │ + var dragOptions = { │ │ │ │ + documentDrag: this.documentDrag, │ │ │ │ + stopDown: false │ │ │ │ + }; │ │ │ │ + │ │ │ │ + // configure the keyboard handler │ │ │ │ + var keyboardOptions = { │ │ │ │ + keydown: this.handleKeypress │ │ │ │ + }; │ │ │ │ + this.handlers = { │ │ │ │ + keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions), │ │ │ │ + drag: new OpenLayers.Handler.Drag(this, dragCallbacks, dragOptions) │ │ │ │ + }; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Take care of things that are not handled in superclass. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + if (this.map) { │ │ │ │ + this.map.events.un({ │ │ │ │ + "removelayer": this.handleMapEvents, │ │ │ │ + "changelayer": this.handleMapEvents, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ - return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments); │ │ │ │ + this.layer = null; │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, []); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getImage │ │ │ │ - * Creates the content for the frame on the tile. │ │ │ │ + * APIMethod: activate │ │ │ │ + * Activate the control. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Successfully activated the control. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + activate: function() { │ │ │ │ + this.moveLayerToTop(); │ │ │ │ + this.map.events.on({ │ │ │ │ + "removelayer": this.handleMapEvents, │ │ │ │ + "changelayer": this.handleMapEvents, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + return (this.handlers.keyboard.activate() && │ │ │ │ + this.handlers.drag.activate() && │ │ │ │ + OpenLayers.Control.prototype.activate.apply(this, arguments)); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var id = this.id + '_iFrame', │ │ │ │ - iframe; │ │ │ │ - if (parseFloat(navigator.appVersion.split("MSIE")[1]) < 9) { │ │ │ │ - // Older IE versions do not set the name attribute of an iFrame │ │ │ │ - // properly via DOM manipulation, so we need to do it on our own with │ │ │ │ - // this hack. │ │ │ │ - iframe = document.createElement('<iframe name="' + id + '">'); │ │ │ │ + /** │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivate the control. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Successfully deactivated the control. │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + // the return from the controls is unimportant in this case │ │ │ │ + if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.moveLayerBack(); │ │ │ │ + this.map.events.un({ │ │ │ │ + "removelayer": this.handleMapEvents, │ │ │ │ + "changelayer": this.handleMapEvents, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.layer.removeFeatures(this.vertices, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.layer.removeFeatures(this.virtualVertices, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.vertices = []; │ │ │ │ + this.handlers.drag.deactivate(); │ │ │ │ + this.handlers.keyboard.deactivate(); │ │ │ │ + var feature = this.feature; │ │ │ │ + if (feature && feature.geometry && feature.layer) { │ │ │ │ + this.unselectFeature(feature); │ │ │ │ + } │ │ │ │ + deactivated = true; │ │ │ │ + } │ │ │ │ + return deactivated; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // IFrames in older IE versions are not transparent, if you set │ │ │ │ - // the backgroundColor transparent. This is a workaround to get │ │ │ │ - // transparent iframes. │ │ │ │ - iframe.style.backgroundColor = '#FFFFFF'; │ │ │ │ - iframe.style.filter = 'chroma(color=#FFFFFF)'; │ │ │ │ - } else { │ │ │ │ - iframe = document.createElement('iframe'); │ │ │ │ - iframe.style.backgroundColor = 'transparent'; │ │ │ │ + /** │ │ │ │ + * Method: beforeSelectFeature │ │ │ │ + * Called before a feature is selected. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The feature about to be selected. │ │ │ │ + */ │ │ │ │ + beforeSelectFeature: function(feature) { │ │ │ │ + return this.layer.events.triggerEvent( │ │ │ │ + "beforefeaturemodified", { │ │ │ │ + feature: feature │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // iframe.name needs to be an unique id, otherwise it │ │ │ │ - // could happen that other iframes are overwritten. │ │ │ │ - iframe.name = id; │ │ │ │ + /** │ │ │ │ + * APIMethod: selectFeature │ │ │ │ + * Select a feature for modification in standalone mode. In non-standalone │ │ │ │ + * mode, this method is called when a feature is selected by clicking. │ │ │ │ + * Register a listener to the beforefeaturemodified event and return false │ │ │ │ + * to prevent feature modification. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} the selected feature. │ │ │ │ + */ │ │ │ │ + selectFeature: function(feature) { │ │ │ │ + if (this.feature === feature || │ │ │ │ + (this.geometryTypes && OpenLayers.Util.indexOf(this.geometryTypes, │ │ │ │ + feature.geometry.CLASS_NAME) == -1)) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (this.beforeSelectFeature(feature) !== false) { │ │ │ │ + if (this.feature) { │ │ │ │ + this.unselectFeature(this.feature); │ │ │ │ } │ │ │ │ + this.feature = feature; │ │ │ │ + this.layer.selectedFeatures.push(feature); │ │ │ │ + this.layer.drawFeature(feature, 'select'); │ │ │ │ + this.modified = false; │ │ │ │ + this.resetVertices(); │ │ │ │ + this.onModificationStart(this.feature); │ │ │ │ + } │ │ │ │ + // keep track of geometry modifications │ │ │ │ + var modified = feature.modified; │ │ │ │ + if (feature.geometry && !(modified && modified.geometry)) { │ │ │ │ + this._originalGeometry = feature.geometry.clone(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // some special properties to avoid scaling the images and scrollbars │ │ │ │ - // in the iframe │ │ │ │ - iframe.scrolling = 'no'; │ │ │ │ - iframe.marginWidth = '0px'; │ │ │ │ - iframe.marginHeight = '0px'; │ │ │ │ - iframe.frameBorder = '0'; │ │ │ │ + /** │ │ │ │ + * APIMethod: unselectFeature │ │ │ │ + * Called when the select feature control unselects a feature. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The unselected feature. │ │ │ │ + */ │ │ │ │ + unselectFeature: function(feature) { │ │ │ │ + this.layer.removeFeatures(this.vertices, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.vertices = []; │ │ │ │ + this.layer.destroyFeatures(this.virtualVertices, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.virtualVertices = []; │ │ │ │ + if (this.dragHandle) { │ │ │ │ + this.layer.destroyFeatures([this.dragHandle], { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + delete this.dragHandle; │ │ │ │ + } │ │ │ │ + if (this.radiusHandle) { │ │ │ │ + this.layer.destroyFeatures([this.radiusHandle], { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + delete this.radiusHandle; │ │ │ │ + } │ │ │ │ + this.layer.drawFeature(this.feature, 'default'); │ │ │ │ + this.feature = null; │ │ │ │ + OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature); │ │ │ │ + this.onModificationEnd(feature); │ │ │ │ + this.layer.events.triggerEvent("afterfeaturemodified", { │ │ │ │ + feature: feature, │ │ │ │ + modified: this.modified │ │ │ │ + }); │ │ │ │ + this.modified = false; │ │ │ │ + }, │ │ │ │ │ │ │ │ - iframe.style.position = "absolute"; │ │ │ │ - iframe.style.width = "100%"; │ │ │ │ - iframe.style.height = "100%"; │ │ │ │ │ │ │ │ - if (this.layer.opacity < 1) { │ │ │ │ - OpenLayers.Util.modifyDOMElement(iframe, null, null, null, │ │ │ │ - null, null, null, this.layer.opacity); │ │ │ │ + /** │ │ │ │ + * Method: dragStart │ │ │ │ + * Called by the drag handler before a feature is dragged. This method is │ │ │ │ + * used to differentiate between points and vertices │ │ │ │ + * of higher order geometries. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The point or vertex about to be │ │ │ │ + * dragged. │ │ │ │ + */ │ │ │ │ + dragStart: function(feature) { │ │ │ │ + var isPoint = feature.geometry.CLASS_NAME == │ │ │ │ + 'OpenLayers.Geometry.Point'; │ │ │ │ + if (!this.standalone && │ │ │ │ + ((!feature._sketch && isPoint) || !feature._sketch)) { │ │ │ │ + if (this.toggle && this.feature === feature) { │ │ │ │ + // mark feature for unselection │ │ │ │ + this._unselect = feature; │ │ │ │ } │ │ │ │ - this.frame.appendChild(iframe); │ │ │ │ - this.imgDiv = iframe; │ │ │ │ - return iframe; │ │ │ │ - } else { │ │ │ │ - return OpenLayers.Tile.Image.prototype.getImage.apply(this, arguments); │ │ │ │ + this.selectFeature(feature); │ │ │ │ + } │ │ │ │ + if (feature._sketch || isPoint) { │ │ │ │ + // feature is a drag or virtual handle or point │ │ │ │ + this.vertex = feature; │ │ │ │ + this.handlers.drag.stopDown = true; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createRequestForm │ │ │ │ - * Create the html <form> element with width, height, bbox and all │ │ │ │ - * parameters specified in the layer params. │ │ │ │ + * Method: dragVertex │ │ │ │ + * Called by the drag handler with each drag move of a vertex. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} The form element which sends the HTTP-POST request to the │ │ │ │ - * WMS. │ │ │ │ + * Parameters: │ │ │ │ + * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged. │ │ │ │ + * pixel - {<OpenLayers.Pixel>} Pixel location of the mouse event. │ │ │ │ */ │ │ │ │ - createRequestForm: function() { │ │ │ │ - // creation of the form element │ │ │ │ - var form = document.createElement('form'); │ │ │ │ - form.method = 'POST'; │ │ │ │ - var cacheId = this.layer.params["_OLSALT"]; │ │ │ │ - cacheId = (cacheId ? cacheId + "_" : "") + this.bounds.toBBOX(); │ │ │ │ - form.action = OpenLayers.Util.urlAppend(this.layer.url, cacheId); │ │ │ │ - form.target = this.id + '_iFrame'; │ │ │ │ + dragVertex: function(vertex, pixel) { │ │ │ │ + var pos = this.map.getLonLatFromViewPortPx(pixel); │ │ │ │ + var geom = vertex.geometry; │ │ │ │ + geom.move(pos.lon - geom.x, pos.lat - geom.y); │ │ │ │ + this.modified = true; │ │ │ │ + /** │ │ │ │ + * Five cases: │ │ │ │ + * 1) dragging a simple point │ │ │ │ + * 2) dragging a virtual vertex │ │ │ │ + * 3) dragging a drag handle │ │ │ │ + * 4) dragging a real vertex │ │ │ │ + * 5) dragging a radius handle │ │ │ │ + */ │ │ │ │ + if (this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ + // dragging a simple point │ │ │ │ + this.layer.events.triggerEvent("vertexmodified", { │ │ │ │ + vertex: vertex.geometry, │ │ │ │ + feature: this.feature, │ │ │ │ + pixel: pixel │ │ │ │ + }); │ │ │ │ + } else { │ │ │ │ + if (vertex._index) { │ │ │ │ + // dragging a virtual vertex │ │ │ │ + vertex.geometry.parent.addComponent(vertex.geometry, │ │ │ │ + vertex._index); │ │ │ │ + // move from virtual to real vertex │ │ │ │ + delete vertex._index; │ │ │ │ + OpenLayers.Util.removeItem(this.virtualVertices, vertex); │ │ │ │ + this.vertices.push(vertex); │ │ │ │ + } else if (vertex == this.dragHandle) { │ │ │ │ + // dragging a drag handle │ │ │ │ + this.layer.removeFeatures(this.vertices, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.vertices = []; │ │ │ │ + if (this.radiusHandle) { │ │ │ │ + this.layer.destroyFeatures([this.radiusHandle], { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.radiusHandle = null; │ │ │ │ + } │ │ │ │ + } else if (vertex !== this.radiusHandle) { │ │ │ │ + // dragging a real vertex │ │ │ │ + this.layer.events.triggerEvent("vertexmodified", { │ │ │ │ + vertex: vertex.geometry, │ │ │ │ + feature: this.feature, │ │ │ │ + pixel: pixel │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + // dragging a radius handle - no special treatment │ │ │ │ + if (this.virtualVertices.length > 0) { │ │ │ │ + this.layer.destroyFeatures(this.virtualVertices, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.virtualVertices = []; │ │ │ │ + } │ │ │ │ + this.layer.drawFeature(this.feature, this.standalone ? undefined : │ │ │ │ + 'select'); │ │ │ │ + } │ │ │ │ + // keep the vertex on top so it gets the mouseout after dragging │ │ │ │ + // this should be removed in favor of an option to draw under or │ │ │ │ + // maintain node z-index │ │ │ │ + this.layer.drawFeature(vertex); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // adding all parameters in layer params as hidden fields to the html │ │ │ │ - // form element │ │ │ │ - var imageSize = this.layer.getImageSize(), │ │ │ │ - params = OpenLayers.Util.getParameters(this.url), │ │ │ │ - field; │ │ │ │ + /** │ │ │ │ + * Method: dragComplete │ │ │ │ + * Called by the drag handler when the feature dragging is complete. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * vertex - {<OpenLayers.Feature.Vector>} The vertex being dragged. │ │ │ │ + */ │ │ │ │ + dragComplete: function(vertex) { │ │ │ │ + this.resetVertices(); │ │ │ │ + this.setFeatureState(); │ │ │ │ + this.onModification(this.feature); │ │ │ │ + this.layer.events.triggerEvent("featuremodified", { │ │ │ │ + feature: this.feature │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ │ │ │ │ - for (var par in params) { │ │ │ │ - field = document.createElement('input'); │ │ │ │ - field.type = 'hidden'; │ │ │ │ - field.name = par; │ │ │ │ - field.value = params[par]; │ │ │ │ - form.appendChild(field); │ │ │ │ + /** │ │ │ │ + * Method: setFeatureState │ │ │ │ + * Called when the feature is modified. If the current state is not │ │ │ │ + * INSERT or DELETE, the state is set to UPDATE. │ │ │ │ + */ │ │ │ │ + setFeatureState: function() { │ │ │ │ + if (this.feature.state != OpenLayers.State.INSERT && │ │ │ │ + this.feature.state != OpenLayers.State.DELETE) { │ │ │ │ + this.feature.state = OpenLayers.State.UPDATE; │ │ │ │ + if (this.modified && this._originalGeometry) { │ │ │ │ + var feature = this.feature; │ │ │ │ + feature.modified = OpenLayers.Util.extend(feature.modified, { │ │ │ │ + geometry: this._originalGeometry │ │ │ │ + }); │ │ │ │ + delete this._originalGeometry; │ │ │ │ + } │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - return form; │ │ │ │ + /** │ │ │ │ + * Method: resetVertices │ │ │ │ + */ │ │ │ │ + resetVertices: function() { │ │ │ │ + if (this.vertices.length > 0) { │ │ │ │ + this.layer.removeFeatures(this.vertices, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.vertices = []; │ │ │ │ + } │ │ │ │ + if (this.virtualVertices.length > 0) { │ │ │ │ + this.layer.removeFeatures(this.virtualVertices, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.virtualVertices = []; │ │ │ │ + } │ │ │ │ + if (this.dragHandle) { │ │ │ │ + this.layer.destroyFeatures([this.dragHandle], { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.dragHandle = null; │ │ │ │ + } │ │ │ │ + if (this.radiusHandle) { │ │ │ │ + this.layer.destroyFeatures([this.radiusHandle], { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.radiusHandle = null; │ │ │ │ + } │ │ │ │ + if (this.feature && │ │ │ │ + this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") { │ │ │ │ + if ((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) { │ │ │ │ + this.collectDragHandle(); │ │ │ │ + } │ │ │ │ + if ((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE | │ │ │ │ + OpenLayers.Control.ModifyFeature.RESIZE))) { │ │ │ │ + this.collectRadiusHandle(); │ │ │ │ + } │ │ │ │ + if (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE) { │ │ │ │ + // Don't collect vertices when we're resizing │ │ │ │ + if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)) { │ │ │ │ + this.collectVertices(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setImgSrc │ │ │ │ - * Sets the source for the tile image │ │ │ │ + * Method: handleKeypress │ │ │ │ + * Called by the feature handler on keypress. This is used to delete │ │ │ │ + * vertices. If the <deleteCode> property is set, vertices will │ │ │ │ + * be deleted when a feature is selected for modification and │ │ │ │ + * the mouse is over a vertex. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * url - {String} │ │ │ │ + * evt - {Event} Keypress event. │ │ │ │ */ │ │ │ │ - setImgSrc: function(url) { │ │ │ │ - if (this.useIFrame === true) { │ │ │ │ - if (url) { │ │ │ │ - var form = this.createRequestForm(); │ │ │ │ - this.frame.appendChild(form); │ │ │ │ - form.submit(); │ │ │ │ - this.frame.removeChild(form); │ │ │ │ - } else if (this.imgDiv.parentNode === this.frame) { │ │ │ │ - // we don't reuse iframes to avoid caching issues │ │ │ │ - this.frame.removeChild(this.imgDiv); │ │ │ │ - this.imgDiv = null; │ │ │ │ + handleKeypress: function(evt) { │ │ │ │ + var code = evt.keyCode; │ │ │ │ + │ │ │ │ + // check for delete key │ │ │ │ + if (this.feature && │ │ │ │ + OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) { │ │ │ │ + var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt); │ │ │ │ + if (vertex && │ │ │ │ + OpenLayers.Util.indexOf(this.vertices, vertex) != -1 && │ │ │ │ + !this.handlers.drag.dragging && vertex.geometry.parent) { │ │ │ │ + // remove the vertex │ │ │ │ + vertex.geometry.parent.removeComponent(vertex.geometry); │ │ │ │ + this.layer.events.triggerEvent("vertexremoved", { │ │ │ │ + vertex: vertex.geometry, │ │ │ │ + feature: this.feature, │ │ │ │ + pixel: evt.xy │ │ │ │ + }); │ │ │ │ + this.layer.drawFeature(this.feature, this.standalone ? │ │ │ │ + undefined : 'select'); │ │ │ │ + this.modified = true; │ │ │ │ + this.resetVertices(); │ │ │ │ + this.setFeatureState(); │ │ │ │ + this.onModification(this.feature); │ │ │ │ + this.layer.events.triggerEvent("featuremodified", { │ │ │ │ + feature: this.feature │ │ │ │ + }); │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - OpenLayers.Tile.Image.prototype.setImgSrc.apply(this, arguments); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onImageLoad │ │ │ │ - * Handler for the image onload event │ │ │ │ + * Method: collectVertices │ │ │ │ + * Collect the vertices from the modifiable feature's geometry and push │ │ │ │ + * them on to the control's vertices array. │ │ │ │ */ │ │ │ │ - onImageLoad: function() { │ │ │ │ - //TODO de-uglify opacity handling │ │ │ │ - OpenLayers.Tile.Image.prototype.onImageLoad.apply(this, arguments); │ │ │ │ - if (this.useIFrame === true) { │ │ │ │ - this.imgDiv.style.opacity = 1; │ │ │ │ - this.frame.style.opacity = this.layer.opacity; │ │ │ │ + collectVertices: function() { │ │ │ │ + this.vertices = []; │ │ │ │ + this.virtualVertices = []; │ │ │ │ + var control = this; │ │ │ │ + │ │ │ │ + function collectComponentVertices(geometry) { │ │ │ │ + var i, vertex, component, len; │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ + vertex = new OpenLayers.Feature.Vector(geometry); │ │ │ │ + vertex._sketch = true; │ │ │ │ + vertex.renderIntent = control.vertexRenderIntent; │ │ │ │ + control.vertices.push(vertex); │ │ │ │ + } else { │ │ │ │ + var numVert = geometry.components.length; │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { │ │ │ │ + numVert -= 1; │ │ │ │ + } │ │ │ │ + for (i = 0; i < numVert; ++i) { │ │ │ │ + component = geometry.components[i]; │ │ │ │ + if (component.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ + vertex = new OpenLayers.Feature.Vector(component); │ │ │ │ + vertex._sketch = true; │ │ │ │ + vertex.renderIntent = control.vertexRenderIntent; │ │ │ │ + control.vertices.push(vertex); │ │ │ │ + } else { │ │ │ │ + collectComponentVertices(component); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // add virtual vertices in the middle of each edge │ │ │ │ + if (control.createVertices && geometry.CLASS_NAME != "OpenLayers.Geometry.MultiPoint") { │ │ │ │ + for (i = 0, len = geometry.components.length; i < len - 1; ++i) { │ │ │ │ + var prevVertex = geometry.components[i]; │ │ │ │ + var nextVertex = geometry.components[i + 1]; │ │ │ │ + if (prevVertex.CLASS_NAME == "OpenLayers.Geometry.Point" && │ │ │ │ + nextVertex.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ + var x = (prevVertex.x + nextVertex.x) / 2; │ │ │ │ + var y = (prevVertex.y + nextVertex.y) / 2; │ │ │ │ + var point = new OpenLayers.Feature.Vector( │ │ │ │ + new OpenLayers.Geometry.Point(x, y), │ │ │ │ + null, control.virtualStyle │ │ │ │ + ); │ │ │ │ + // set the virtual parent and intended index │ │ │ │ + point.geometry.parent = geometry; │ │ │ │ + point._index = i + 1; │ │ │ │ + point._sketch = true; │ │ │ │ + control.virtualVertices.push(point); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + collectComponentVertices.call(this, this.feature.geometry); │ │ │ │ + this.layer.addFeatures(this.virtualVertices, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + this.layer.addFeatures(this.vertices, { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createBackBuffer │ │ │ │ - * Override createBackBuffer to do nothing when we use an iframe. Moving an │ │ │ │ - * iframe from one element to another makes it necessary to reload the iframe │ │ │ │ - * because its content is lost. So we just give up. │ │ │ │ + * Method: collectDragHandle │ │ │ │ + * Collect the drag handle for the selected geometry. │ │ │ │ + */ │ │ │ │ + collectDragHandle: function() { │ │ │ │ + var geometry = this.feature.geometry; │ │ │ │ + var center = geometry.getBounds().getCenterLonLat(); │ │ │ │ + var originGeometry = new OpenLayers.Geometry.Point( │ │ │ │ + center.lon, center.lat │ │ │ │ + ); │ │ │ │ + var origin = new OpenLayers.Feature.Vector(originGeometry); │ │ │ │ + originGeometry.move = function(x, y) { │ │ │ │ + OpenLayers.Geometry.Point.prototype.move.call(this, x, y); │ │ │ │ + geometry.move(x, y); │ │ │ │ + }; │ │ │ │ + origin._sketch = true; │ │ │ │ + this.dragHandle = origin; │ │ │ │ + this.dragHandle.renderIntent = this.vertexRenderIntent; │ │ │ │ + this.layer.addFeatures([this.dragHandle], { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: collectRadiusHandle │ │ │ │ + * Collect the radius handle for the selected geometry. │ │ │ │ + */ │ │ │ │ + collectRadiusHandle: function() { │ │ │ │ + var geometry = this.feature.geometry; │ │ │ │ + var bounds = geometry.getBounds(); │ │ │ │ + var center = bounds.getCenterLonLat(); │ │ │ │ + var originGeometry = new OpenLayers.Geometry.Point( │ │ │ │ + center.lon, center.lat │ │ │ │ + ); │ │ │ │ + var radiusGeometry = new OpenLayers.Geometry.Point( │ │ │ │ + bounds.right, bounds.bottom │ │ │ │ + ); │ │ │ │ + var radius = new OpenLayers.Feature.Vector(radiusGeometry); │ │ │ │ + var resize = (this.mode & OpenLayers.Control.ModifyFeature.RESIZE); │ │ │ │ + var reshape = (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE); │ │ │ │ + var rotate = (this.mode & OpenLayers.Control.ModifyFeature.ROTATE); │ │ │ │ + │ │ │ │ + radiusGeometry.move = function(x, y) { │ │ │ │ + OpenLayers.Geometry.Point.prototype.move.call(this, x, y); │ │ │ │ + var dx1 = this.x - originGeometry.x; │ │ │ │ + var dy1 = this.y - originGeometry.y; │ │ │ │ + var dx0 = dx1 - x; │ │ │ │ + var dy0 = dy1 - y; │ │ │ │ + if (rotate) { │ │ │ │ + var a0 = Math.atan2(dy0, dx0); │ │ │ │ + var a1 = Math.atan2(dy1, dx1); │ │ │ │ + var angle = a1 - a0; │ │ │ │ + angle *= 180 / Math.PI; │ │ │ │ + geometry.rotate(angle, originGeometry); │ │ │ │ + } │ │ │ │ + if (resize) { │ │ │ │ + var scale, ratio; │ │ │ │ + // 'resize' together with 'reshape' implies that the aspect │ │ │ │ + // ratio of the geometry will not be preserved whilst resizing │ │ │ │ + if (reshape) { │ │ │ │ + scale = dy1 / dy0; │ │ │ │ + ratio = (dx1 / dx0) / scale; │ │ │ │ + } else { │ │ │ │ + var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0)); │ │ │ │ + var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1)); │ │ │ │ + scale = l1 / l0; │ │ │ │ + } │ │ │ │ + geometry.resize(scale, originGeometry, ratio); │ │ │ │ + } │ │ │ │ + }; │ │ │ │ + radius._sketch = true; │ │ │ │ + this.radiusHandle = radius; │ │ │ │ + this.radiusHandle.renderIntent = this.vertexRenderIntent; │ │ │ │ + this.layer.addFeatures([this.radiusHandle], { │ │ │ │ + silent: true │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the control and all handlers. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} The control's map. │ │ │ │ */ │ │ │ │ - createBackBuffer: function() { │ │ │ │ - var backBuffer; │ │ │ │ - if (this.useIFrame === false) { │ │ │ │ - backBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer.call(this); │ │ │ │ + setMap: function(map) { │ │ │ │ + this.handlers.drag.setMap(map); │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: handleMapEvents │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} │ │ │ │ + */ │ │ │ │ + handleMapEvents: function(evt) { │ │ │ │ + if (evt.type == "removelayer" || evt.property == "order") { │ │ │ │ + this.moveLayerToTop(); │ │ │ │ } │ │ │ │ - return backBuffer; │ │ │ │ - } │ │ │ │ -}; │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Protocol/WFS.js │ │ │ │ - ====================================================================== */ │ │ │ │ + }, │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + /** │ │ │ │ + * Method: moveLayerToTop │ │ │ │ + * Moves the layer for this handler to the top, so mouse events can reach │ │ │ │ + * it. │ │ │ │ + */ │ │ │ │ + moveLayerToTop: function() { │ │ │ │ + var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1, │ │ │ │ + this.layer.getZIndex()) + 1; │ │ │ │ + this.layer.setZIndex(index); │ │ │ │ + │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveLayerBack │ │ │ │ + * Moves the layer back to the position determined by the map's layers │ │ │ │ + * array. │ │ │ │ + */ │ │ │ │ + moveLayerBack: function() { │ │ │ │ + var index = this.layer.getZIndex() - 1; │ │ │ │ + if (index >= this.map.Z_INDEX_BASE['Feature']) { │ │ │ │ + this.layer.setZIndex(index); │ │ │ │ + } else { │ │ │ │ + this.map.setLayerZIndex(this.layer, │ │ │ │ + this.map.getLayerIndex(this.layer)); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Control.ModifyFeature" │ │ │ │ +}); │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Protocol.js │ │ │ │ + * Constant: RESHAPE │ │ │ │ + * {Integer} Constant used to make the control work in reshape mode │ │ │ │ */ │ │ │ │ - │ │ │ │ +OpenLayers.Control.ModifyFeature.RESHAPE = 1; │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Protocol.WFS │ │ │ │ - * Used to create a versioned WFS protocol. Default version is 1.0.0. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Protocol>} A WFS protocol of the given version. │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * var protocol = new OpenLayers.Protocol.WFS({ │ │ │ │ - * version: "1.1.0", │ │ │ │ - * url: "http://demo.opengeo.org/geoserver/wfs", │ │ │ │ - * featureType: "tasmania_roads", │ │ │ │ - * featureNS: "http://www.openplans.org/topp", │ │ │ │ - * geometryName: "the_geom" │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * See the protocols for specific WFS versions for more detail. │ │ │ │ + * Constant: RESIZE │ │ │ │ + * {Integer} Constant used to make the control work in resize mode │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol.WFS = function(options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults( │ │ │ │ - options, OpenLayers.Protocol.WFS.DEFAULTS │ │ │ │ - ); │ │ │ │ - var cls = OpenLayers.Protocol.WFS["v" + options.version.replace(/\./g, "_")]; │ │ │ │ - if (!cls) { │ │ │ │ - throw "Unsupported WFS version: " + options.version; │ │ │ │ - } │ │ │ │ - return new cls(options); │ │ │ │ -}; │ │ │ │ - │ │ │ │ +OpenLayers.Control.ModifyFeature.RESIZE = 2; │ │ │ │ /** │ │ │ │ - * Function: fromWMSLayer │ │ │ │ - * Convenience function to create a WFS protocol from a WMS layer. This makes │ │ │ │ - * the assumption that a WFS requests can be issued at the same URL as │ │ │ │ - * WMS requests and that a WFS featureType exists with the same name as the │ │ │ │ - * WMS layer. │ │ │ │ - * │ │ │ │ - * This function is designed to auto-configure <url>, <featureType>, │ │ │ │ - * <featurePrefix> and <srsName> for WFS <version> 1.1.0. Note that │ │ │ │ - * srsName matching with the WMS layer will not work with WFS 1.0.0. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.WMS>} WMS layer that has a matching WFS │ │ │ │ - * FeatureType at the same server url with the same typename. │ │ │ │ - * options - {Object} Default properties to be set on the protocol. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Protocol.WFS>} │ │ │ │ + * Constant: ROTATE │ │ │ │ + * {Integer} Constant used to make the control work in rotate mode │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) { │ │ │ │ - var typeName, featurePrefix; │ │ │ │ - var param = layer.params["LAYERS"]; │ │ │ │ - var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(":"); │ │ │ │ - if (parts.length > 1) { │ │ │ │ - featurePrefix = parts[0]; │ │ │ │ - } │ │ │ │ - typeName = parts.pop(); │ │ │ │ - var protocolOptions = { │ │ │ │ - url: layer.url, │ │ │ │ - featureType: typeName, │ │ │ │ - featurePrefix: featurePrefix, │ │ │ │ - srsName: layer.projection && layer.projection.getCode() || │ │ │ │ - layer.map && layer.map.getProjectionObject().getCode(), │ │ │ │ - version: "1.1.0" │ │ │ │ - }; │ │ │ │ - return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults( │ │ │ │ - options, protocolOptions │ │ │ │ - )); │ │ │ │ -}; │ │ │ │ - │ │ │ │ +OpenLayers.Control.ModifyFeature.ROTATE = 4; │ │ │ │ /** │ │ │ │ - * Constant: OpenLayers.Protocol.WFS.DEFAULTS │ │ │ │ + * Constant: DRAG │ │ │ │ + * {Integer} Constant used to make the control work in drag mode │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol.WFS.DEFAULTS = { │ │ │ │ - "version": "1.0.0" │ │ │ │ -}; │ │ │ │ +OpenLayers.Control.ModifyFeature.DRAG = 8; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Protocol/Script.js │ │ │ │ + OpenLayers/Control/Snapping.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Protocol.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ - * @requires OpenLayers/Format/GeoJSON.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * if application uses the query string, for example, for BBOX parameters, │ │ │ │ - * OpenLayers/Format/QueryStringFilter.js should be included in the build config file │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Layer/Vector.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Protocol.Script │ │ │ │ - * A basic Script protocol for vector layers. Create a new instance with the │ │ │ │ - * <OpenLayers.Protocol.Script> constructor. A script protocol is used to │ │ │ │ - * get around the same origin policy. It works with services that return │ │ │ │ - * JSONP - that is, JSON wrapped in a client-specified callback. The │ │ │ │ - * protocol handles fetching and parsing of feature data and sends parsed │ │ │ │ - * features to the <callback> configured with the protocol. The protocol │ │ │ │ - * expects features serialized as GeoJSON by default, but can be configured │ │ │ │ - * to work with other formats by setting the <format> property. │ │ │ │ + * Class: OpenLayers.Control.Snapping │ │ │ │ + * Acts as a snapping agent while editing vector features. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Protocol> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: url │ │ │ │ - * {String} Service URL. The service is expected to return serialized │ │ │ │ - * features wrapped in a named callback (where the callback name is │ │ │ │ - * generated by this protocol). │ │ │ │ - * Read-only, set through the options passed to the constructor. │ │ │ │ - */ │ │ │ │ - url: null, │ │ │ │ +OpenLayers.Control.Snapping = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: params │ │ │ │ - * {Object} Query string parameters to be appended to the URL. │ │ │ │ - * Read-only, set through the options passed to the constructor. │ │ │ │ - * Example: {maxFeatures: 50} │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * beforesnap - Triggered before a snap occurs. Listeners receive an │ │ │ │ + * event object with *point*, *x*, *y*, *distance*, *layer*, and │ │ │ │ + * *snapType* properties. The point property will be original point │ │ │ │ + * geometry considered for snapping. The x and y properties represent │ │ │ │ + * coordinates the point will receive. The distance is the distance │ │ │ │ + * of the snap. The layer is the target layer. The snapType property │ │ │ │ + * will be one of "node", "vertex", or "edge". Return false to stop │ │ │ │ + * snapping from occurring. │ │ │ │ + * snap - Triggered when a snap occurs. Listeners receive an event with │ │ │ │ + * *point*, *snapType*, *layer*, and *distance* properties. The point │ │ │ │ + * will be the location snapped to. The snapType will be one of "node", │ │ │ │ + * "vertex", or "edge". The layer will be the target layer. The │ │ │ │ + * distance will be the distance of the snap in map units. │ │ │ │ + * unsnap - Triggered when a vertex is unsnapped. Listeners receive an │ │ │ │ + * event with a *point* property. │ │ │ │ */ │ │ │ │ - params: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: callback │ │ │ │ - * {Object} Function to be called when the <read> operation completes. │ │ │ │ + * CONSTANT: DEFAULTS │ │ │ │ + * Default target properties. │ │ │ │ */ │ │ │ │ - callback: null, │ │ │ │ + DEFAULTS: { │ │ │ │ + tolerance: 10, │ │ │ │ + node: true, │ │ │ │ + edge: true, │ │ │ │ + vertex: true │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: callbackTemplate │ │ │ │ - * {String} Template for creating a unique callback function name │ │ │ │ - * for the registry. Should include ${id}. The ${id} variable will be │ │ │ │ - * replaced with a string identifier prefixed with a "c" (e.g. c1, c2). │ │ │ │ - * Default is "OpenLayers.Protocol.Script.registry.${id}". │ │ │ │ + * Property: greedy │ │ │ │ + * {Boolean} Snap to closest feature in first layer with an eligible │ │ │ │ + * feature. Default is true. │ │ │ │ */ │ │ │ │ - callbackTemplate: "OpenLayers.Protocol.Script.registry.${id}", │ │ │ │ + greedy: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: callbackKey │ │ │ │ - * {String} The name of the query string parameter that the service │ │ │ │ - * recognizes as the callback identifier. Default is "callback". │ │ │ │ - * This key is used to generate the URL for the script. For example │ │ │ │ - * setting <callbackKey> to "myCallback" would result in a URL like │ │ │ │ - * http://example.com/?myCallback=... │ │ │ │ + * Property: precedence │ │ │ │ + * {Array} List representing precedence of different snapping types. │ │ │ │ + * Default is "node", "vertex", "edge". │ │ │ │ */ │ │ │ │ - callbackKey: "callback", │ │ │ │ + precedence: ["node", "vertex", "edge"], │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: callbackPrefix │ │ │ │ - * {String} Where a service requires that the callback query string │ │ │ │ - * parameter value is prefixed by some string, this value may be set. │ │ │ │ - * For example, setting <callbackPrefix> to "foo:" would result in a │ │ │ │ - * URL like http://example.com/?callback=foo:... Default is "". │ │ │ │ + * Property: resolution │ │ │ │ + * {Float} The map resolution for the previously considered snap. │ │ │ │ */ │ │ │ │ - callbackPrefix: "", │ │ │ │ + resolution: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: scope │ │ │ │ - * {Object} Optional ``this`` object for the callback. Read-only, set │ │ │ │ - * through the options passed to the constructor. │ │ │ │ + * Property: geoToleranceCache │ │ │ │ + * {Object} A cache of geo-tolerances. Tolerance values (in map units) are │ │ │ │ + * calculated when the map resolution changes. │ │ │ │ */ │ │ │ │ - scope: null, │ │ │ │ + geoToleranceCache: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: format │ │ │ │ - * {<OpenLayers.Format>} Format for parsing features. Default is an │ │ │ │ - * <OpenLayers.Format.GeoJSON> format. If an alternative is provided, │ │ │ │ - * the format's read method must take an object and return an array │ │ │ │ - * of features. │ │ │ │ + * Property: layer │ │ │ │ + * {<OpenLayers.Layer.Vector>} The current editable layer. Set at │ │ │ │ + * construction or after construction with <setLayer>. │ │ │ │ */ │ │ │ │ - format: null, │ │ │ │ + layer: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: pendingRequests │ │ │ │ - * {Object} References all pending requests. Property names are script │ │ │ │ - * identifiers and property values are script elements. │ │ │ │ + * Property: feature │ │ │ │ + * {<OpenLayers.Feature.Vector>} The current editable feature. │ │ │ │ */ │ │ │ │ - pendingRequests: null, │ │ │ │ + feature: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: srsInBBOX │ │ │ │ - * {Boolean} Include the SRS identifier in BBOX query string parameter. │ │ │ │ - * Setting this property has no effect if a custom filterToParams method │ │ │ │ - * is provided. Default is false. If true and the layer has a │ │ │ │ - * projection object set, any BBOX filter will be serialized with a │ │ │ │ - * fifth item identifying the projection. │ │ │ │ - * E.g. bbox=-1000,-1000,1000,1000,EPSG:900913 │ │ │ │ + * Property: point │ │ │ │ + * {<OpenLayers.Geometry.Point>} The currently snapped vertex. │ │ │ │ */ │ │ │ │ - srsInBBOX: false, │ │ │ │ + point: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Protocol.Script │ │ │ │ - * A class for giving layers generic Script protocol. │ │ │ │ + * Constructor: OpenLayers.Control.Snapping │ │ │ │ + * Creates a new snapping control. A control is constructed with an editable │ │ │ │ + * layer and a set of configuration objects for target layers. While the │ │ │ │ + * control is active, dragging vertices while drawing new features or │ │ │ │ + * modifying existing features on the editable layer will engage │ │ │ │ + * snapping to features on the target layers. Whether a vertex snaps to │ │ │ │ + * a feature on a target layer depends on the target layer configuration. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * options - {Object} An object containing all configuration properties for │ │ │ │ + * the control. │ │ │ │ * │ │ │ │ - * Valid options include: │ │ │ │ - * url - {String} │ │ │ │ - * params - {Object} │ │ │ │ - * callback - {Function} │ │ │ │ - * scope - {Object} │ │ │ │ + * Valid options: │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} The editable layer. Features from this │ │ │ │ + * layer that are digitized or modified may have vertices snapped to │ │ │ │ + * features from any of the target layers. │ │ │ │ + * targets - {Array(Object | OpenLayers.Layer.Vector)} A list of objects for │ │ │ │ + * configuring target layers. See valid properties of the target │ │ │ │ + * objects below. If the items in the targets list are vector layers │ │ │ │ + * (instead of configuration objects), the defaults from the <defaults> │ │ │ │ + * property will apply. The editable layer itself may be a target │ │ │ │ + * layer, allowing newly created or edited features to be snapped to │ │ │ │ + * existing features from the same layer. If no targets are provided │ │ │ │ + * the layer given in the constructor (as <layer>) will become the │ │ │ │ + * initial target. │ │ │ │ + * defaults - {Object} An object with default properties to be applied │ │ │ │ + * to all target objects. │ │ │ │ + * greedy - {Boolean} Snap to closest feature in first target layer that │ │ │ │ + * applies. Default is true. If false, all features in all target │ │ │ │ + * layers will be checked and the closest feature in all target layers │ │ │ │ + * will be chosen. The greedy property determines if the order of the │ │ │ │ + * target layers is significant. By default, the order of the target │ │ │ │ + * layers is significant where layers earlier in the target layer list │ │ │ │ + * have precedence over layers later in the list. Within a single │ │ │ │ + * layer, the closest feature is always chosen for snapping. This │ │ │ │ + * property only determines whether the search for a closer feature │ │ │ │ + * continues after an eligible feature is found in a target layer. │ │ │ │ + * │ │ │ │ + * Valid target properties: │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} A target layer. Features from this │ │ │ │ + * layer will be eligible to act as snapping target for the editable │ │ │ │ + * layer. │ │ │ │ + * tolerance - {Float} The distance (in pixels) at which snapping may occur. │ │ │ │ + * Default is 10. │ │ │ │ + * node - {Boolean} Snap to nodes (first or last point in a geometry) in │ │ │ │ + * target layer. Default is true. │ │ │ │ + * nodeTolerance - {Float} Optional distance at which snapping may occur │ │ │ │ + * for nodes specifically. If none is provided, <tolerance> will be │ │ │ │ + * used. │ │ │ │ + * vertex - {Boolean} Snap to vertices in target layer. Default is true. │ │ │ │ + * vertexTolerance - {Float} Optional distance at which snapping may occur │ │ │ │ + * for vertices specifically. If none is provided, <tolerance> will be │ │ │ │ + * used. │ │ │ │ + * edge - {Boolean} Snap to edges in target layer. Default is true. │ │ │ │ + * edgeTolerance - {Float} Optional distance at which snapping may occur │ │ │ │ + * for edges specifically. If none is provided, <tolerance> will be │ │ │ │ + * used. │ │ │ │ + * filter - {<OpenLayers.Filter>} Optional filter to evaluate to determine if │ │ │ │ + * feature is eligible for snapping. If filter evaluates to true for a │ │ │ │ + * target feature a vertex may be snapped to the feature. │ │ │ │ + * minResolution - {Number} If a minResolution is provided, snapping to this │ │ │ │ + * target will only be considered if the map resolution is greater than │ │ │ │ + * or equal to this value (the minResolution is inclusive). Default is │ │ │ │ + * no minimum resolution limit. │ │ │ │ + * maxResolution - {Number} If a maxResolution is provided, snapping to this │ │ │ │ + * target will only be considered if the map resolution is strictly │ │ │ │ + * less than this value (the maxResolution is exclusive). Default is │ │ │ │ + * no maximum resolution limit. │ │ │ │ */ │ │ │ │ initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - this.params = {}; │ │ │ │ - this.pendingRequests = {}; │ │ │ │ - OpenLayers.Protocol.prototype.initialize.apply(this, arguments); │ │ │ │ - if (!this.format) { │ │ │ │ - this.format = new OpenLayers.Format.GeoJSON(); │ │ │ │ - } │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + this.options = options || {}; // TODO: this could be done by the super │ │ │ │ │ │ │ │ - if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { │ │ │ │ - var format = new OpenLayers.Format.QueryStringFilter({ │ │ │ │ - srsInBBOX: this.srsInBBOX │ │ │ │ - }); │ │ │ │ - this.filterToParams = function(filter, params) { │ │ │ │ - return format.write(filter, params); │ │ │ │ - }; │ │ │ │ + // set the editable layer if provided │ │ │ │ + if (this.options.layer) { │ │ │ │ + this.setLayer(this.options.layer); │ │ │ │ + } │ │ │ │ + // configure target layers │ │ │ │ + var defaults = OpenLayers.Util.extend({}, this.options.defaults); │ │ │ │ + this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS); │ │ │ │ + this.setTargets(this.options.targets); │ │ │ │ + if (this.targets.length === 0 && this.layer) { │ │ │ │ + this.addTargetLayer(this.layer); │ │ │ │ } │ │ │ │ + │ │ │ │ + this.geoToleranceCache = {}; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Construct a request for reading new features. │ │ │ │ + * APIMethod: setLayer │ │ │ │ + * Set the editable layer. Call the setLayer method if the editable layer │ │ │ │ + * changes and the same control should be used on a new editable layer. │ │ │ │ + * If the control is already active, it will be active after the new │ │ │ │ + * layer is set. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * url - {String} Url for the request. │ │ │ │ - * params - {Object} Parameters to get serialized as a query string. │ │ │ │ - * filter - {<OpenLayers.Filter>} Filter to get serialized as a │ │ │ │ - * query string. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property │ │ │ │ - * references the injected script. This object is also passed to the │ │ │ │ - * callback function when the request completes, its "features" property │ │ │ │ - * is then populated with the features received from the server. │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} The new editable layer. │ │ │ │ */ │ │ │ │ - read: function(options) { │ │ │ │ - OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - options.params = OpenLayers.Util.applyDefaults( │ │ │ │ - options.params, this.options.params │ │ │ │ - ); │ │ │ │ - if (options.filter && this.filterToParams) { │ │ │ │ - options.params = this.filterToParams( │ │ │ │ - options.filter, options.params │ │ │ │ - ); │ │ │ │ + setLayer: function(layer) { │ │ │ │ + if (this.active) { │ │ │ │ + this.deactivate(); │ │ │ │ + this.layer = layer; │ │ │ │ + this.activate(); │ │ │ │ + } else { │ │ │ │ + this.layer = layer; │ │ │ │ } │ │ │ │ - var response = new OpenLayers.Protocol.Response({ │ │ │ │ - requestType: "read" │ │ │ │ - }); │ │ │ │ - var request = this.createRequest( │ │ │ │ - options.url, │ │ │ │ - options.params, │ │ │ │ - OpenLayers.Function.bind(function(data) { │ │ │ │ - response.data = data; │ │ │ │ - this.handleRead(response, options); │ │ │ │ - }, this) │ │ │ │ - ); │ │ │ │ - response.priv = request; │ │ │ │ - return response; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: filterToParams │ │ │ │ - * Optional method to translate an <OpenLayers.Filter> object into an object │ │ │ │ - * that can be serialized as request query string provided. If a custom │ │ │ │ - * method is not provided, any filter will not be serialized. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * filter - {<OpenLayers.Filter>} filter to convert. │ │ │ │ - * params - {Object} The parameters object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} The resulting parameters object. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: createRequest │ │ │ │ - * Issues a request for features by creating injecting a script in the │ │ │ │ - * document head. │ │ │ │ + /** │ │ │ │ + * Method: setTargets │ │ │ │ + * Set the targets for the snapping agent. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * url - {String} Service URL. │ │ │ │ - * params - {Object} Query string parameters. │ │ │ │ - * callback - {Function} Callback to be called with resulting data. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {HTMLScriptElement} The script pending execution. │ │ │ │ + * targets - {Array} An array of target configs or target layers. │ │ │ │ */ │ │ │ │ - createRequest: function(url, params, callback) { │ │ │ │ - var id = OpenLayers.Protocol.Script.register(callback); │ │ │ │ - var name = OpenLayers.String.format(this.callbackTemplate, { │ │ │ │ - id: id │ │ │ │ - }); │ │ │ │ - params = OpenLayers.Util.extend({}, params); │ │ │ │ - params[this.callbackKey] = this.callbackPrefix + name; │ │ │ │ - url = OpenLayers.Util.urlAppend( │ │ │ │ - url, OpenLayers.Util.getParameterString(params) │ │ │ │ - ); │ │ │ │ - var script = document.createElement("script"); │ │ │ │ - script.type = "text/javascript"; │ │ │ │ - script.src = url; │ │ │ │ - script.id = "OpenLayers_Protocol_Script_" + id; │ │ │ │ - this.pendingRequests[script.id] = script; │ │ │ │ - var head = document.getElementsByTagName("head")[0]; │ │ │ │ - head.appendChild(script); │ │ │ │ - return script; │ │ │ │ + setTargets: function(targets) { │ │ │ │ + this.targets = []; │ │ │ │ + if (targets && targets.length) { │ │ │ │ + var target; │ │ │ │ + for (var i = 0, len = targets.length; i < len; ++i) { │ │ │ │ + target = targets[i]; │ │ │ │ + if (target instanceof OpenLayers.Layer.Vector) { │ │ │ │ + this.addTargetLayer(target); │ │ │ │ + } else { │ │ │ │ + this.addTarget(target); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: destroyRequest │ │ │ │ - * Remove a script node associated with a response from the document. Also │ │ │ │ - * unregisters the callback and removes the script from the │ │ │ │ - * <pendingRequests> object. │ │ │ │ + /** │ │ │ │ + * Method: addTargetLayer │ │ │ │ + * Add a target layer with the default target config. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * script - {HTMLScriptElement} │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} A target layer. │ │ │ │ */ │ │ │ │ - destroyRequest: function(script) { │ │ │ │ - OpenLayers.Protocol.Script.unregister(script.id.split("_").pop()); │ │ │ │ - delete this.pendingRequests[script.id]; │ │ │ │ - if (script.parentNode) { │ │ │ │ - script.parentNode.removeChild(script); │ │ │ │ - } │ │ │ │ + addTargetLayer: function(layer) { │ │ │ │ + this.addTarget({ │ │ │ │ + layer: layer │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleRead │ │ │ │ - * Individual callbacks are created for read, create and update, should │ │ │ │ - * a subclass need to override each one separately. │ │ │ │ + * Method: addTarget │ │ │ │ + * Add a configured target layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ - * the user callback. │ │ │ │ - * options - {Object} The user options passed to the read call. │ │ │ │ + * target - {Object} A target config. │ │ │ │ */ │ │ │ │ - handleRead: function(response, options) { │ │ │ │ - this.handleResponse(response, options); │ │ │ │ + addTarget: function(target) { │ │ │ │ + target = OpenLayers.Util.applyDefaults(target, this.defaults); │ │ │ │ + target.nodeTolerance = target.nodeTolerance || target.tolerance; │ │ │ │ + target.vertexTolerance = target.vertexTolerance || target.tolerance; │ │ │ │ + target.edgeTolerance = target.edgeTolerance || target.tolerance; │ │ │ │ + this.targets.push(target); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleResponse │ │ │ │ - * Called by CRUD specific handlers. │ │ │ │ + * Method: removeTargetLayer │ │ │ │ + * Remove a target layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ - * any user callback. │ │ │ │ - * options - {Object} The user options passed to the create, read, update, │ │ │ │ - * or delete call. │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} The target layer to remove. │ │ │ │ */ │ │ │ │ - handleResponse: function(response, options) { │ │ │ │ - if (options.callback) { │ │ │ │ - if (response.data) { │ │ │ │ - response.features = this.parseFeatures(response.data); │ │ │ │ - response.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ - } else { │ │ │ │ - response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + removeTargetLayer: function(layer) { │ │ │ │ + var target; │ │ │ │ + for (var i = this.targets.length - 1; i >= 0; --i) { │ │ │ │ + target = this.targets[i]; │ │ │ │ + if (target.layer === layer) { │ │ │ │ + this.removeTarget(target); │ │ │ │ } │ │ │ │ - this.destroyRequest(response.priv); │ │ │ │ - options.callback.call(options.scope, response); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseFeatures │ │ │ │ - * Read Script response body and return features. │ │ │ │ + * Method: removeTarget │ │ │ │ + * Remove a target. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * data - {Object} The data sent to the callback function by the server. │ │ │ │ + * target - {Object} A target config. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ - * {<OpenLayers.Feature.Vector>} Array of features or a single feature. │ │ │ │ + * {Array} The targets array. │ │ │ │ */ │ │ │ │ - parseFeatures: function(data) { │ │ │ │ - return this.format.read(data); │ │ │ │ + removeTarget: function(target) { │ │ │ │ + return OpenLayers.Util.removeItem(this.targets, target); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: abort │ │ │ │ - * Abort an ongoing request. If no response is provided, all pending │ │ │ │ - * requests will be aborted. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} The response object returned │ │ │ │ - * from a <read> request. │ │ │ │ + * APIMethod: activate │ │ │ │ + * Activate the control. Activating the control registers listeners for │ │ │ │ + * editing related events so that during feature creation and │ │ │ │ + * modification, moving vertices will trigger snapping. │ │ │ │ */ │ │ │ │ - abort: function(response) { │ │ │ │ - if (response) { │ │ │ │ - this.destroyRequest(response.priv); │ │ │ │ - } else { │ │ │ │ - for (var key in this.pendingRequests) { │ │ │ │ - this.destroyRequest(this.pendingRequests[key]); │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Control.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + if (this.layer && this.layer.events) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + sketchstarted: this.onSketchModified, │ │ │ │ + sketchmodified: this.onSketchModified, │ │ │ │ + vertexmodified: this.onVertexModified, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ } │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Clean up the protocol. │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivate the control. Deactivating the control unregisters listeners │ │ │ │ + * so feature editing may proceed without engaging the snapping agent. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.abort(); │ │ │ │ - delete this.params; │ │ │ │ - delete this.format; │ │ │ │ - OpenLayers.Protocol.prototype.destroy.apply(this); │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Control.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + if (this.layer && this.layer.events) { │ │ │ │ + this.layer.events.un({ │ │ │ │ + sketchstarted: this.onSketchModified, │ │ │ │ + sketchmodified: this.onSketchModified, │ │ │ │ + vertexmodified: this.onVertexModified, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.feature = null; │ │ │ │ + this.point = null; │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.Script" │ │ │ │ -}); │ │ │ │ - │ │ │ │ -(function() { │ │ │ │ - var o = OpenLayers.Protocol.Script; │ │ │ │ - var counter = 0; │ │ │ │ - o.registry = {}; │ │ │ │ + /** │ │ │ │ + * Method: onSketchModified │ │ │ │ + * Registered as a listener for the sketchmodified event on the editable │ │ │ │ + * layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * event - {Object} The sketch modified event. │ │ │ │ + */ │ │ │ │ + onSketchModified: function(event) { │ │ │ │ + this.feature = event.feature; │ │ │ │ + this.considerSnapping(event.vertex, event.vertex); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: OpenLayers.Protocol.Script.register │ │ │ │ - * Register a callback for a newly created script. │ │ │ │ + * Method: onVertexModified │ │ │ │ + * Registered as a listener for the vertexmodified event on the editable │ │ │ │ + * layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * callback - {Function} The callback to be executed when the newly added │ │ │ │ - * script loads. This callback will be called with a single argument │ │ │ │ - * that is the JSON returned by the service. │ │ │ │ + * event - {Object} The vertex modified event. │ │ │ │ + */ │ │ │ │ + onVertexModified: function(event) { │ │ │ │ + this.feature = event.feature; │ │ │ │ + var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel); │ │ │ │ + this.considerSnapping( │ │ │ │ + event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat) │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: considerSnapping │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Number} An identifier for retrieving the registered callback. │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} The vertex to be snapped (or │ │ │ │ + * unsnapped). │ │ │ │ + * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map │ │ │ │ + * coords. │ │ │ │ */ │ │ │ │ - o.register = function(callback) { │ │ │ │ - var id = "c" + (++counter); │ │ │ │ - o.registry[id] = function() { │ │ │ │ - callback.apply(this, arguments); │ │ │ │ + considerSnapping: function(point, loc) { │ │ │ │ + var best = { │ │ │ │ + rank: Number.POSITIVE_INFINITY, │ │ │ │ + dist: Number.POSITIVE_INFINITY, │ │ │ │ + x: null, │ │ │ │ + y: null │ │ │ │ }; │ │ │ │ - return id; │ │ │ │ - }; │ │ │ │ + var snapped = false; │ │ │ │ + var result, target; │ │ │ │ + for (var i = 0, len = this.targets.length; i < len; ++i) { │ │ │ │ + target = this.targets[i]; │ │ │ │ + result = this.testTarget(target, loc); │ │ │ │ + if (result) { │ │ │ │ + if (this.greedy) { │ │ │ │ + best = result; │ │ │ │ + best.target = target; │ │ │ │ + snapped = true; │ │ │ │ + break; │ │ │ │ + } else { │ │ │ │ + if ((result.rank < best.rank) || │ │ │ │ + (result.rank === best.rank && result.dist < best.dist)) { │ │ │ │ + best = result; │ │ │ │ + best.target = target; │ │ │ │ + snapped = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (snapped) { │ │ │ │ + var proceed = this.events.triggerEvent("beforesnap", { │ │ │ │ + point: point, │ │ │ │ + x: best.x, │ │ │ │ + y: best.y, │ │ │ │ + distance: best.dist, │ │ │ │ + layer: best.target.layer, │ │ │ │ + snapType: this.precedence[best.rank] │ │ │ │ + }); │ │ │ │ + if (proceed !== false) { │ │ │ │ + point.x = best.x; │ │ │ │ + point.y = best.y; │ │ │ │ + this.point = point; │ │ │ │ + this.events.triggerEvent("snap", { │ │ │ │ + point: point, │ │ │ │ + snapType: this.precedence[best.rank], │ │ │ │ + layer: best.target.layer, │ │ │ │ + distance: best.dist │ │ │ │ + }); │ │ │ │ + } else { │ │ │ │ + snapped = false; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (this.point && !snapped) { │ │ │ │ + point.x = loc.x; │ │ │ │ + point.y = loc.y; │ │ │ │ + this.point = null; │ │ │ │ + this.events.triggerEvent("unsnap", { │ │ │ │ + point: point │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: OpenLayers.Protocol.Script.unregister │ │ │ │ - * Unregister a callback previously registered with the register function. │ │ │ │ + * Method: testTarget │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * id - {Number} The identifer returned by the register function. │ │ │ │ + * target - {Object} Object with target layer configuration. │ │ │ │ + * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map │ │ │ │ + * coords. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} A result object with rank, dist, x, and y properties. │ │ │ │ + * Returns null if candidate is not eligible for snapping. │ │ │ │ */ │ │ │ │ - o.unregister = function(id) { │ │ │ │ - delete o.registry[id]; │ │ │ │ - }; │ │ │ │ -})(); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Protocol/CSW.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + testTarget: function(target, loc) { │ │ │ │ + var resolution = this.layer.map.getResolution(); │ │ │ │ + if ("minResolution" in target) { │ │ │ │ + if (resolution < target.minResolution) { │ │ │ │ + return null; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if ("maxResolution" in target) { │ │ │ │ + if (resolution >= target.maxResolution) { │ │ │ │ + return null; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var tolerance = { │ │ │ │ + node: this.getGeoTolerance(target.nodeTolerance, resolution), │ │ │ │ + vertex: this.getGeoTolerance(target.vertexTolerance, resolution), │ │ │ │ + edge: this.getGeoTolerance(target.edgeTolerance, resolution) │ │ │ │ + }; │ │ │ │ + // this could be cached if we don't support setting tolerance values directly │ │ │ │ + var maxTolerance = Math.max( │ │ │ │ + tolerance.node, tolerance.vertex, tolerance.edge │ │ │ │ + ); │ │ │ │ + var result = { │ │ │ │ + rank: Number.POSITIVE_INFINITY, │ │ │ │ + dist: Number.POSITIVE_INFINITY │ │ │ │ + }; │ │ │ │ + var eligible = false; │ │ │ │ + var features = target.layer.features; │ │ │ │ + var feature, type, vertices, vertex, closest, dist, found; │ │ │ │ + var numTypes = this.precedence.length; │ │ │ │ + var ll = new OpenLayers.LonLat(loc.x, loc.y); │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + feature = features[i]; │ │ │ │ + if (feature !== this.feature && !feature._sketch && │ │ │ │ + feature.state !== OpenLayers.State.DELETE && │ │ │ │ + (!target.filter || target.filter.evaluate(feature))) { │ │ │ │ + if (feature.atPoint(ll, maxTolerance, maxTolerance)) { │ │ │ │ + for (var j = 0, stop = Math.min(result.rank + 1, numTypes); j < stop; ++j) { │ │ │ │ + type = this.precedence[j]; │ │ │ │ + if (target[type]) { │ │ │ │ + if (type === "edge") { │ │ │ │ + closest = feature.geometry.distanceTo(loc, { │ │ │ │ + details: true │ │ │ │ + }); │ │ │ │ + dist = closest.distance; │ │ │ │ + if (dist <= tolerance[type] && dist < result.dist) { │ │ │ │ + result = { │ │ │ │ + rank: j, │ │ │ │ + dist: dist, │ │ │ │ + x: closest.x0, │ │ │ │ + y: closest.y0 // closest coords on feature │ │ │ │ + }; │ │ │ │ + eligible = true; │ │ │ │ + // don't look for lower precedence types for this feature │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + // look for nodes or vertices │ │ │ │ + vertices = feature.geometry.getVertices(type === "node"); │ │ │ │ + found = false; │ │ │ │ + for (var k = 0, klen = vertices.length; k < klen; ++k) { │ │ │ │ + vertex = vertices[k]; │ │ │ │ + dist = vertex.distanceTo(loc); │ │ │ │ + if (dist <= tolerance[type] && │ │ │ │ + (j < result.rank || (j === result.rank && dist < result.dist))) { │ │ │ │ + result = { │ │ │ │ + rank: j, │ │ │ │ + dist: dist, │ │ │ │ + x: vertex.x, │ │ │ │ + y: vertex.y │ │ │ │ + }; │ │ │ │ + eligible = true; │ │ │ │ + found = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (found) { │ │ │ │ + // don't look for lower precedence types for this feature │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return eligible ? result : null; │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Protocol.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Method: getGeoTolerance │ │ │ │ + * Calculate a tolerance in map units given a tolerance in pixels. This │ │ │ │ + * takes advantage of the <geoToleranceCache> when the map resolution │ │ │ │ + * has not changed. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * tolerance - {Number} A tolerance value in pixels. │ │ │ │ + * resolution - {Number} Map resolution. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Number} A tolerance value in map units. │ │ │ │ + */ │ │ │ │ + getGeoTolerance: function(tolerance, resolution) { │ │ │ │ + if (resolution !== this.resolution) { │ │ │ │ + this.resolution = resolution; │ │ │ │ + this.geoToleranceCache = {}; │ │ │ │ + } │ │ │ │ + var geoTolerance = this.geoToleranceCache[tolerance]; │ │ │ │ + if (geoTolerance === undefined) { │ │ │ │ + geoTolerance = tolerance * resolution; │ │ │ │ + this.geoToleranceCache[tolerance] = geoTolerance; │ │ │ │ + } │ │ │ │ + return geoTolerance; │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Protocol.CSW │ │ │ │ - * Used to create a versioned CSW protocol. Default version is 2.0.2. │ │ │ │ - */ │ │ │ │ -OpenLayers.Protocol.CSW = function(options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults( │ │ │ │ - options, OpenLayers.Protocol.CSW.DEFAULTS │ │ │ │ - ); │ │ │ │ - var cls = OpenLayers.Protocol.CSW["v" + options.version.replace(/\./g, "_")]; │ │ │ │ - if (!cls) { │ │ │ │ - throw "Unsupported CSW version: " + options.version; │ │ │ │ - } │ │ │ │ - return new cls(options); │ │ │ │ -}; │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * Clean up the control. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + if (this.active) { │ │ │ │ + this.deactivate(); // TODO: this should be handled by the super │ │ │ │ + } │ │ │ │ + delete this.layer; │ │ │ │ + delete this.targets; │ │ │ │ + OpenLayers.Control.prototype.destroy.call(this); │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Protocol.CSW.DEFAULTS │ │ │ │ - */ │ │ │ │ -OpenLayers.Protocol.CSW.DEFAULTS = { │ │ │ │ - "version": "2.0.2" │ │ │ │ -}; │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Snapping" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Protocol/SOS.js │ │ │ │ + OpenLayers/Control/Measure.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Protocol.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: OpenLayers.Protocol.SOS │ │ │ │ - * Used to create a versioned SOS protocol. Default version is 1.0.0. │ │ │ │ + * Class: OpenLayers.Control.Measure │ │ │ │ + * Allows for drawing of features for measurements. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Protocol>} An SOS protocol for the given version. │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol.SOS = function(options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults( │ │ │ │ - options, OpenLayers.Protocol.SOS.DEFAULTS │ │ │ │ - ); │ │ │ │ - var cls = OpenLayers.Protocol.SOS["v" + options.version.replace(/\./g, "_")]; │ │ │ │ - if (!cls) { │ │ │ │ - throw "Unsupported SOS version: " + options.version; │ │ │ │ - } │ │ │ │ - return new cls(options); │ │ │ │ -}; │ │ │ │ +OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Protocol.SOS.DEFAULTS │ │ │ │ - */ │ │ │ │ -OpenLayers.Protocol.SOS.DEFAULTS = { │ │ │ │ - "version": "1.0.0" │ │ │ │ -}; │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Protocol/HTTP.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * measure - Triggered when a measurement sketch is complete. Listeners │ │ │ │ + * will receive an event with measure, units, order, and geometry │ │ │ │ + * properties. │ │ │ │ + * measurepartial - Triggered when a new point is added to the │ │ │ │ + * measurement sketch or if the <immediate> property is true and the │ │ │ │ + * measurement sketch is modified. Listeners receive an event with measure, │ │ │ │ + * units, order, and geometry. │ │ │ │ + */ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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: handlerOptions │ │ │ │ + * {Object} Used to set non-default properties on the control's handler │ │ │ │ + */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Protocol.js │ │ │ │ - * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Property: callbacks │ │ │ │ + * {Object} The functions that are sent to the handler for callback │ │ │ │ + */ │ │ │ │ + callbacks: null, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * if application uses the query string, for example, for BBOX parameters, │ │ │ │ - * OpenLayers/Format/QueryStringFilter.js should be included in the build config file │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * APIProperty: displaySystem │ │ │ │ + * {String} Display system for output measurements. Supported values │ │ │ │ + * are 'english', 'metric', and 'geographic'. Default is 'metric'. │ │ │ │ + */ │ │ │ │ + displaySystem: 'metric', │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Protocol.HTTP │ │ │ │ - * A basic HTTP protocol for vector layers. Create a new instance with the │ │ │ │ - * <OpenLayers.Protocol.HTTP> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Protocol> │ │ │ │ - */ │ │ │ │ -OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ + /** │ │ │ │ + * APIProperty: geodesic │ │ │ │ + * {Boolean} Calculate geodesic metrics instead of planar metrics. This │ │ │ │ + * requires that geometries can be transformed into Geographic/WGS84 │ │ │ │ + * (if that is not already the map projection). Default is false. │ │ │ │ + */ │ │ │ │ + geodesic: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: url │ │ │ │ - * {String} Service URL, read-only, set through the options │ │ │ │ - * passed to constructor. │ │ │ │ + * Property: displaySystemUnits │ │ │ │ + * {Object} Units for various measurement systems. Values are arrays │ │ │ │ + * of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing │ │ │ │ + * order of length. │ │ │ │ */ │ │ │ │ - url: null, │ │ │ │ + displaySystemUnits: { │ │ │ │ + geographic: ['dd'], │ │ │ │ + english: ['mi', 'ft', 'in'], │ │ │ │ + metric: ['km', 'm'] │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: headers │ │ │ │ - * {Object} HTTP request headers, read-only, set through the options │ │ │ │ - * passed to the constructor, │ │ │ │ - * Example: {'Content-Type': 'plain/text'} │ │ │ │ + * Property: delay │ │ │ │ + * {Number} Number of milliseconds between clicks before the event is │ │ │ │ + * considered a double-click. The "measurepartial" event will not │ │ │ │ + * be triggered if the sketch is completed within this time. This │ │ │ │ + * is required for IE where creating a browser reflow (if a listener │ │ │ │ + * is modifying the DOM by displaying the measurement values) messes │ │ │ │ + * with the dblclick listener in the sketch handler. │ │ │ │ */ │ │ │ │ - headers: null, │ │ │ │ + partialDelay: 300, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: params │ │ │ │ - * {Object} Parameters of GET requests, read-only, set through the options │ │ │ │ - * passed to the constructor, │ │ │ │ - * Example: {'bbox': '5,5,5,5'} │ │ │ │ + * Property: delayedTrigger │ │ │ │ + * {Number} Timeout id of trigger for measurepartial. │ │ │ │ */ │ │ │ │ - params: null, │ │ │ │ + delayedTrigger: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: callback │ │ │ │ - * {Object} Function to be called when the <read>, <create>, │ │ │ │ - * <update>, <delete> or <commit> operation completes, read-only, │ │ │ │ - * set through the options passed to the constructor. │ │ │ │ + * APIProperty: persist │ │ │ │ + * {Boolean} Keep the temporary measurement sketch drawn after the │ │ │ │ + * measurement is complete. The geometry will persist until a new │ │ │ │ + * measurement is started, the control is deactivated, or <cancel> is │ │ │ │ + * called. │ │ │ │ */ │ │ │ │ - callback: null, │ │ │ │ + persist: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: scope │ │ │ │ - * {Object} Callback execution scope, read-only, set through the │ │ │ │ - * options passed to the constructor. │ │ │ │ + * APIProperty: immediate │ │ │ │ + * {Boolean} Activates the immediate measurement so that the "measurepartial" │ │ │ │ + * event is also fired once the measurement sketch is modified. │ │ │ │ + * Default is false. │ │ │ │ */ │ │ │ │ - scope: null, │ │ │ │ + immediate: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: readWithPOST │ │ │ │ - * {Boolean} true if read operations are done with POST requests │ │ │ │ - * instead of GET, defaults to false. │ │ │ │ + * Constructor: OpenLayers.Control.Measure │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * handler - {<OpenLayers.Handler>} │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - readWithPOST: false, │ │ │ │ + initialize: function(handler, options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + var callbacks = { │ │ │ │ + done: this.measureComplete, │ │ │ │ + point: this.measurePartial │ │ │ │ + }; │ │ │ │ + if (this.immediate) { │ │ │ │ + callbacks.modify = this.measureImmediate; │ │ │ │ + } │ │ │ │ + this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); │ │ │ │ + │ │ │ │ + // let the handler options override, so old code that passes 'persist' │ │ │ │ + // directly to the handler does not need an update │ │ │ │ + this.handlerOptions = OpenLayers.Util.extend({ │ │ │ │ + persist: this.persist │ │ │ │ + }, this.handlerOptions); │ │ │ │ + this.handler = new handler(this, this.callbacks, this.handlerOptions); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: updateWithPOST │ │ │ │ - * {Boolean} true if update operations are done with POST requests │ │ │ │ - * defaults to false. │ │ │ │ + * APIMethod: deactivate │ │ │ │ */ │ │ │ │ - updateWithPOST: false, │ │ │ │ + deactivate: function() { │ │ │ │ + this.cancelDelay(); │ │ │ │ + return OpenLayers.Control.prototype.deactivate.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: deleteWithPOST │ │ │ │ - * {Boolean} true if delete operations are done with POST requests │ │ │ │ - * defaults to false. │ │ │ │ - * if true, POST data is set to output of format.write(). │ │ │ │ + * APIMethod: cancel │ │ │ │ + * Stop the control from measuring. If <persist> is true, the temporary │ │ │ │ + * sketch will be erased. │ │ │ │ */ │ │ │ │ - deleteWithPOST: false, │ │ │ │ + cancel: function() { │ │ │ │ + this.cancelDelay(); │ │ │ │ + this.handler.cancel(); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: wildcarded. │ │ │ │ - * {Boolean} If true percent signs are added around values │ │ │ │ - * read from LIKE filters, for example if the protocol │ │ │ │ - * read method is passed a LIKE filter whose property │ │ │ │ - * is "foo" and whose value is "bar" the string │ │ │ │ - * "foo__ilike=%bar%" will be sent in the query string; │ │ │ │ - * defaults to false. │ │ │ │ + * APIMethod: setImmediate │ │ │ │ + * Sets the <immediate> property. Changes the activity of immediate │ │ │ │ + * measurement. │ │ │ │ */ │ │ │ │ - wildcarded: false, │ │ │ │ + setImmediate: function(immediate) { │ │ │ │ + this.immediate = immediate; │ │ │ │ + if (this.immediate) { │ │ │ │ + this.callbacks.modify = this.measureImmediate; │ │ │ │ + } else { │ │ │ │ + delete this.callbacks.modify; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: srsInBBOX │ │ │ │ - * {Boolean} Include the SRS identifier in BBOX query string parameter. │ │ │ │ - * Default is false. If true and the layer has a projection object set, │ │ │ │ - * any BBOX filter will be serialized with a fifth item identifying the │ │ │ │ - * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913 │ │ │ │ + * Method: updateHandler │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * handler - {Function} One of the sketch handler constructors. │ │ │ │ + * options - {Object} Options for the handler. │ │ │ │ + */ │ │ │ │ + updateHandler: function(handler, options) { │ │ │ │ + var active = this.active; │ │ │ │ + if (active) { │ │ │ │ + this.deactivate(); │ │ │ │ + } │ │ │ │ + this.handler = new handler(this, this.callbacks, options); │ │ │ │ + if (active) { │ │ │ │ + this.activate(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: measureComplete │ │ │ │ + * Called when the measurement sketch is done. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + */ │ │ │ │ + measureComplete: function(geometry) { │ │ │ │ + this.cancelDelay(); │ │ │ │ + this.measure(geometry, "measure"); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: measurePartial │ │ │ │ + * Called each time a new point is added to the measurement sketch. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} The last point added. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} The sketch geometry. │ │ │ │ */ │ │ │ │ - srsInBBOX: false, │ │ │ │ + measurePartial: function(point, geometry) { │ │ │ │ + this.cancelDelay(); │ │ │ │ + geometry = geometry.clone(); │ │ │ │ + // when we're wating for a dblclick, we have to trigger measurepartial │ │ │ │ + // after some delay to deal with reflow issues in IE │ │ │ │ + if (this.handler.freehandMode(this.handler.evt)) { │ │ │ │ + // no dblclick in freehand mode │ │ │ │ + this.measure(geometry, "measurepartial"); │ │ │ │ + } else { │ │ │ │ + this.delayedTrigger = window.setTimeout( │ │ │ │ + OpenLayers.Function.bind(function() { │ │ │ │ + this.delayedTrigger = null; │ │ │ │ + this.measure(geometry, "measurepartial"); │ │ │ │ + }, this), │ │ │ │ + this.partialDelay │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Protocol.HTTP │ │ │ │ - * A class for giving layers generic HTTP protocol. │ │ │ │ + * Method: measureImmediate │ │ │ │ + * Called each time the measurement sketch is modified. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - * │ │ │ │ - * Valid options include: │ │ │ │ - * url - {String} │ │ │ │ - * headers - {Object} │ │ │ │ - * params - {Object} URL parameters for GET requests │ │ │ │ - * format - {<OpenLayers.Format>} │ │ │ │ - * callback - {Function} │ │ │ │ - * scope - {Object} │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} The point at the mouse position. │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} The sketch feature. │ │ │ │ + * drawing - {Boolean} Indicates whether we're currently drawing. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - this.params = {}; │ │ │ │ - this.headers = {}; │ │ │ │ - OpenLayers.Protocol.prototype.initialize.apply(this, arguments); │ │ │ │ + measureImmediate: function(point, feature, drawing) { │ │ │ │ + if (drawing && !this.handler.freehandMode(this.handler.evt)) { │ │ │ │ + this.cancelDelay(); │ │ │ │ + this.measure(feature.geometry, "measurepartial"); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { │ │ │ │ - var format = new OpenLayers.Format.QueryStringFilter({ │ │ │ │ - wildcarded: this.wildcarded, │ │ │ │ - srsInBBOX: this.srsInBBOX │ │ │ │ - }); │ │ │ │ - this.filterToParams = function(filter, params) { │ │ │ │ - return format.write(filter, params); │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Method: cancelDelay │ │ │ │ + * Cancels the delay measurement that measurePartial began. │ │ │ │ + */ │ │ │ │ + cancelDelay: function() { │ │ │ │ + if (this.delayedTrigger !== null) { │ │ │ │ + window.clearTimeout(this.delayedTrigger); │ │ │ │ + this.delayedTrigger = null; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Clean up the protocol. │ │ │ │ + * Method: measure │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * eventType - {String} │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.params = null; │ │ │ │ - this.headers = null; │ │ │ │ - OpenLayers.Protocol.prototype.destroy.apply(this); │ │ │ │ + measure: function(geometry, eventType) { │ │ │ │ + var stat, order; │ │ │ │ + if (geometry.CLASS_NAME.indexOf('LineString') > -1) { │ │ │ │ + stat = this.getBestLength(geometry); │ │ │ │ + order = 1; │ │ │ │ + } else { │ │ │ │ + stat = this.getBestArea(geometry); │ │ │ │ + order = 2; │ │ │ │ + } │ │ │ │ + this.events.triggerEvent(eventType, { │ │ │ │ + measure: stat[0], │ │ │ │ + units: stat[1], │ │ │ │ + order: order, │ │ │ │ + geometry: geometry │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: filterToParams │ │ │ │ - * Optional method to translate an <OpenLayers.Filter> object into an object │ │ │ │ - * that can be serialized as request query string provided. If a custom │ │ │ │ - * method is not provided, the filter will be serialized using the │ │ │ │ - * <OpenLayers.Format.QueryStringFilter> class. │ │ │ │ + * Method: getBestArea │ │ │ │ + * Based on the <displaySystem> returns the area of a geometry. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * filter - {<OpenLayers.Filter>} filter to convert. │ │ │ │ - * params - {Object} The parameters object. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} The resulting parameters object. │ │ │ │ + * {Array([Float, String])} Returns a two item array containing the │ │ │ │ + * area and the units abbreviation. │ │ │ │ */ │ │ │ │ + getBestArea: function(geometry) { │ │ │ │ + var units = this.displaySystemUnits[this.displaySystem]; │ │ │ │ + var unit, area; │ │ │ │ + for (var i = 0, len = units.length; i < len; ++i) { │ │ │ │ + unit = units[i]; │ │ │ │ + area = this.getArea(geometry, unit); │ │ │ │ + if (area > 1) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return [area, unit]; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Construct a request for reading new features. │ │ │ │ + * Method: getArea │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * url - {String} Url for the request. │ │ │ │ - * params - {Object} Parameters to get serialized as a query string. │ │ │ │ - * headers - {Object} Headers to be set on the request. │ │ │ │ - * filter - {<OpenLayers.Filter>} Filter to get serialized as a │ │ │ │ - * query string. │ │ │ │ - * readWithPOST - {Boolean} If the request should be done with POST. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * units - {String} Unit abbreviation │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property │ │ │ │ - * references the HTTP request, this object is also passed to the │ │ │ │ - * callback function when the request completes, its "features" property │ │ │ │ - * is then populated with the features received from the server. │ │ │ │ + * {Float} The geometry area in the given units. │ │ │ │ */ │ │ │ │ - read: function(options) { │ │ │ │ - OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ - options = options || {}; │ │ │ │ - options.params = OpenLayers.Util.applyDefaults( │ │ │ │ - options.params, this.options.params); │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - if (options.filter && this.filterToParams) { │ │ │ │ - options.params = this.filterToParams( │ │ │ │ - options.filter, options.params │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - var readWithPOST = (options.readWithPOST !== undefined) ? │ │ │ │ - options.readWithPOST : this.readWithPOST; │ │ │ │ - var resp = new OpenLayers.Protocol.Response({ │ │ │ │ - requestType: "read" │ │ │ │ - }); │ │ │ │ - if (readWithPOST) { │ │ │ │ - var headers = options.headers || {}; │ │ │ │ - headers["Content-Type"] = "application/x-www-form-urlencoded"; │ │ │ │ - resp.priv = OpenLayers.Request.POST({ │ │ │ │ - url: options.url, │ │ │ │ - callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ - data: OpenLayers.Util.getParameterString(options.params), │ │ │ │ - headers: headers │ │ │ │ - }); │ │ │ │ + getArea: function(geometry, units) { │ │ │ │ + var area, geomUnits; │ │ │ │ + if (this.geodesic) { │ │ │ │ + area = geometry.getGeodesicArea(this.map.getProjectionObject()); │ │ │ │ + geomUnits = "m"; │ │ │ │ } else { │ │ │ │ - resp.priv = OpenLayers.Request.GET({ │ │ │ │ - url: options.url, │ │ │ │ - callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ - params: options.params, │ │ │ │ - headers: options.headers │ │ │ │ - }); │ │ │ │ + area = geometry.getArea(); │ │ │ │ + geomUnits = this.map.getUnits(); │ │ │ │ } │ │ │ │ - return resp; │ │ │ │ + var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units]; │ │ │ │ + if (inPerDisplayUnit) { │ │ │ │ + var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits]; │ │ │ │ + area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2); │ │ │ │ + } │ │ │ │ + return area; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleRead │ │ │ │ - * Individual callbacks are created for read, create and update, should │ │ │ │ - * a subclass need to override each one separately. │ │ │ │ + * Method: getBestLength │ │ │ │ + * Based on the <displaySystem> returns the length of a geometry. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ - * the user callback. │ │ │ │ - * options - {Object} The user options passed to the read call. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array([Float, String])} Returns a two item array containing the │ │ │ │ + * length and the units abbreviation. │ │ │ │ */ │ │ │ │ - handleRead: function(resp, options) { │ │ │ │ - this.handleResponse(resp, options); │ │ │ │ + getBestLength: function(geometry) { │ │ │ │ + var units = this.displaySystemUnits[this.displaySystem]; │ │ │ │ + var unit, length; │ │ │ │ + for (var i = 0, len = units.length; i < len; ++i) { │ │ │ │ + unit = units[i]; │ │ │ │ + length = this.getLength(geometry, unit); │ │ │ │ + if (length > 1) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return [length, unit]; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: create │ │ │ │ - * Construct a request for writing newly created features. │ │ │ │ + * Method: getLength │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ - * {<OpenLayers.Feature.Vector>} │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * units - {String} Unit abbreviation │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ - * object, whose "priv" property references the HTTP request, this │ │ │ │ - * object is also passed to the callback function when the request │ │ │ │ - * completes, its "features" property is then populated with the │ │ │ │ - * the features received from the server. │ │ │ │ + * {Float} The geometry length in the given units. │ │ │ │ */ │ │ │ │ - create: function(features, options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + getLength: function(geometry, units) { │ │ │ │ + var length, geomUnits; │ │ │ │ + if (this.geodesic) { │ │ │ │ + length = geometry.getGeodesicLength(this.map.getProjectionObject()); │ │ │ │ + geomUnits = "m"; │ │ │ │ + } else { │ │ │ │ + length = geometry.getLength(); │ │ │ │ + geomUnits = this.map.getUnits(); │ │ │ │ + } │ │ │ │ + var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units]; │ │ │ │ + if (inPerDisplayUnit) { │ │ │ │ + var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits]; │ │ │ │ + length *= (inPerMapUnit / inPerDisplayUnit); │ │ │ │ + } │ │ │ │ + return length; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var resp = new OpenLayers.Protocol.Response({ │ │ │ │ - reqFeatures: features, │ │ │ │ - requestType: "create" │ │ │ │ - }); │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Measure" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/CacheWrite.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - resp.priv = OpenLayers.Request.POST({ │ │ │ │ - url: options.url, │ │ │ │ - callback: this.createCallback(this.handleCreate, resp, options), │ │ │ │ - headers: options.headers, │ │ │ │ - data: this.format.write(features) │ │ │ │ - }); │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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 resp; │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Request.js │ │ │ │ + * @requires OpenLayers/Console.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: handleCreate │ │ │ │ - * Called the the request issued by <create> is complete. May be overridden │ │ │ │ - * by subclasses. │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.CacheWrite │ │ │ │ + * A control for caching image tiles in the browser's local storage. The │ │ │ │ + * <OpenLayers.Control.CacheRead> control is used to fetch and use the cached │ │ │ │ + * tile images. │ │ │ │ + * │ │ │ │ + * Note: Before using this control on any layer that is not your own, make sure │ │ │ │ + * that the terms of service of the tile provider allow local storage of tiles. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ - * any user callback. │ │ │ │ - * options - {Object} The user options passed to the create call. │ │ │ │ + * To register events in the constructor, configure <eventListeners>. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * cachefull - Triggered when the cache is full. Listeners receive an │ │ │ │ + * object with a tile property as first argument. The tile references │ │ │ │ + * the tile that couldn't be cached. │ │ │ │ */ │ │ │ │ - handleCreate: function(resp, options) { │ │ │ │ - this.handleResponse(resp, options); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: update │ │ │ │ - * Construct a request updating modified feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ - * object, whose "priv" property references the HTTP request, this │ │ │ │ - * object is also passed to the callback function when the request │ │ │ │ - * completes, its "features" property is then populated with the │ │ │ │ - * the feature received from the server. │ │ │ │ + * APIProperty: eventListeners │ │ │ │ + * {Object} Object with event listeners, keyed by event name. An optional │ │ │ │ + * scope property defines the scope that listeners will be executed in. │ │ │ │ */ │ │ │ │ - update: function(feature, options) { │ │ │ │ - options = options || {}; │ │ │ │ - var url = options.url || │ │ │ │ - feature.url || │ │ │ │ - this.options.url + "/" + feature.fid; │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ │ │ │ │ - var resp = new OpenLayers.Protocol.Response({ │ │ │ │ - reqFeatures: feature, │ │ │ │ - requestType: "update" │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * APIProperty: layers │ │ │ │ + * {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, caching │ │ │ │ + * will be enabled for these layers only, otherwise for all cacheable │ │ │ │ + * layers. │ │ │ │ + */ │ │ │ │ + layers: null, │ │ │ │ │ │ │ │ - var method = this.updateWithPOST ? "POST" : "PUT"; │ │ │ │ - resp.priv = OpenLayers.Request[method]({ │ │ │ │ - url: url, │ │ │ │ - callback: this.createCallback(this.handleUpdate, resp, options), │ │ │ │ - headers: options.headers, │ │ │ │ - data: this.format.write(feature) │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * APIProperty: imageFormat │ │ │ │ + * {String} The image format used for caching. The default is "image/png". │ │ │ │ + * Supported formats depend on the user agent. If an unsupported │ │ │ │ + * <imageFormat> is provided, "image/png" will be used. For aerial │ │ │ │ + * imagery, "image/jpeg" is recommended. │ │ │ │ + */ │ │ │ │ + imageFormat: "image/png", │ │ │ │ │ │ │ │ - return resp; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: quotaRegEx │ │ │ │ + * {RegExp} │ │ │ │ + */ │ │ │ │ + quotaRegEx: (/quota/i), │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleUpdate │ │ │ │ - * Called the the request issued by <update> is complete. May be overridden │ │ │ │ - * by subclasses. │ │ │ │ + * Constructor: OpenLayers.Control.CacheWrite │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ - * any user callback. │ │ │ │ - * options - {Object} The user options passed to the update call. │ │ │ │ + * options - {Object} Object with API properties for this control. │ │ │ │ */ │ │ │ │ - handleUpdate: function(resp, options) { │ │ │ │ - this.handleResponse(resp, options); │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the control. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ + var i, layers = this.layers || map.layers; │ │ │ │ + for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ + this.addLayer({ │ │ │ │ + layer: layers[i] │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + if (!this.layers) { │ │ │ │ + map.events.on({ │ │ │ │ + addlayer: this.addLayer, │ │ │ │ + removeLayer: this.removeLayer, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: delete │ │ │ │ - * Construct a request deleting a removed feature. │ │ │ │ + * Method: addLayer │ │ │ │ + * Adds a layer to the control. Once added, tiles requested for this layer │ │ │ │ + * will be cached. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ - * object, whose "priv" property references the HTTP request, this │ │ │ │ - * object is also passed to the callback function when the request │ │ │ │ - * completes. │ │ │ │ + * evt - {Object} Object with a layer property referencing an │ │ │ │ + * <OpenLayers.Layer> instance │ │ │ │ */ │ │ │ │ - "delete": function(feature, options) { │ │ │ │ - options = options || {}; │ │ │ │ - var url = options.url || │ │ │ │ - feature.url || │ │ │ │ - this.options.url + "/" + feature.fid; │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - │ │ │ │ - var resp = new OpenLayers.Protocol.Response({ │ │ │ │ - reqFeatures: feature, │ │ │ │ - requestType: "delete" │ │ │ │ + addLayer: function(evt) { │ │ │ │ + evt.layer.events.on({ │ │ │ │ + tileloadstart: this.makeSameOrigin, │ │ │ │ + tileloaded: this.onTileLoaded, │ │ │ │ + scope: this │ │ │ │ }); │ │ │ │ - │ │ │ │ - var method = this.deleteWithPOST ? "POST" : "DELETE"; │ │ │ │ - var requestOptions = { │ │ │ │ - url: url, │ │ │ │ - callback: this.createCallback(this.handleDelete, resp, options), │ │ │ │ - headers: options.headers │ │ │ │ - }; │ │ │ │ - if (this.deleteWithPOST) { │ │ │ │ - requestOptions.data = this.format.write(feature); │ │ │ │ - } │ │ │ │ - resp.priv = OpenLayers.Request[method](requestOptions); │ │ │ │ - │ │ │ │ - return resp; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleDelete │ │ │ │ - * Called the the request issued by <delete> is complete. May be overridden │ │ │ │ - * by subclasses. │ │ │ │ + * Method: removeLayer │ │ │ │ + * Removes a layer from the control. Once removed, tiles requested for this │ │ │ │ + * layer will no longer be cached. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ - * any user callback. │ │ │ │ - * options - {Object} The user options passed to the delete call. │ │ │ │ + * evt - {Object} Object with a layer property referencing an │ │ │ │ + * <OpenLayers.Layer> instance │ │ │ │ */ │ │ │ │ - handleDelete: function(resp, options) { │ │ │ │ - this.handleResponse(resp, options); │ │ │ │ + removeLayer: function(evt) { │ │ │ │ + evt.layer.events.un({ │ │ │ │ + tileloadstart: this.makeSameOrigin, │ │ │ │ + tileloaded: this.onTileLoaded, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleResponse │ │ │ │ - * Called by CRUD specific handlers. │ │ │ │ + * Method: makeSameOrigin │ │ │ │ + * If the tile does not have CORS image loading enabled and is from a │ │ │ │ + * different origin, use OpenLayers.ProxyHost to make it a same origin url. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ - * any user callback. │ │ │ │ - * options - {Object} The user options passed to the create, read, update, │ │ │ │ - * or delete call. │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ */ │ │ │ │ - handleResponse: function(resp, options) { │ │ │ │ - var request = resp.priv; │ │ │ │ - if (options.callback) { │ │ │ │ - if (request.status >= 200 && request.status < 300) { │ │ │ │ - // success │ │ │ │ - if (resp.requestType != "delete") { │ │ │ │ - resp.features = this.parseFeatures(request); │ │ │ │ - } │ │ │ │ - resp.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ - } else { │ │ │ │ - // failure │ │ │ │ - resp.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + makeSameOrigin: function(evt) { │ │ │ │ + if (this.active) { │ │ │ │ + var tile = evt.tile; │ │ │ │ + if (tile instanceof OpenLayers.Tile.Image && │ │ │ │ + !tile.crossOriginKeyword && │ │ │ │ + tile.url.substr(0, 5) !== "data:") { │ │ │ │ + var sameOriginUrl = OpenLayers.Request.makeSameOrigin( │ │ │ │ + tile.url, OpenLayers.ProxyHost │ │ │ │ + ); │ │ │ │ + OpenLayers.Control.CacheWrite.urlMap[sameOriginUrl] = tile.url; │ │ │ │ + tile.url = sameOriginUrl; │ │ │ │ } │ │ │ │ - options.callback.call(options.scope, resp); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseFeatures │ │ │ │ - * Read HTTP response body and return features. │ │ │ │ + * Method: onTileLoaded │ │ │ │ + * Decides whether a tile can be cached and calls the cache method. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * request - {XMLHttpRequest} The request object │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ - * {<OpenLayers.Feature.Vector>} Array of features or a single feature. │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - parseFeatures: function(request) { │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText; │ │ │ │ - } │ │ │ │ - if (!doc || doc.length <= 0) { │ │ │ │ - return null; │ │ │ │ + onTileLoaded: function(evt) { │ │ │ │ + if (this.active && !evt.aborted && │ │ │ │ + evt.tile instanceof OpenLayers.Tile.Image && │ │ │ │ + evt.tile.url.substr(0, 5) !== 'data:') { │ │ │ │ + this.cache({ │ │ │ │ + tile: evt.tile │ │ │ │ + }); │ │ │ │ + delete OpenLayers.Control.CacheWrite.urlMap[evt.tile.url]; │ │ │ │ } │ │ │ │ - return this.format.read(doc); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: commit │ │ │ │ - * Iterate over each feature and take action based on the feature state. │ │ │ │ - * Possible actions are create, update and delete. │ │ │ │ + * Method: cache │ │ │ │ + * Adds a tile to the cache. When the cache is full, the "cachefull" event │ │ │ │ + * is triggered. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array({<OpenLayers.Feature.Vector>})} │ │ │ │ - * options - {Object} Optional object for setting up intermediate commit │ │ │ │ - * callbacks. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * create - {Object} Optional object to be passed to the <create> method. │ │ │ │ - * update - {Object} Optional object to be passed to the <update> method. │ │ │ │ - * delete - {Object} Optional object to be passed to the <delete> method. │ │ │ │ - * callback - {Function} Optional function to be called when the commit │ │ │ │ - * is complete. │ │ │ │ - * scope - {Object} Optional object to be set as the scope of the callback. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array(<OpenLayers.Protocol.Response>)} An array of response objects, │ │ │ │ - * one per request made to the server, each object's "priv" property │ │ │ │ - * references the corresponding HTTP request. │ │ │ │ + * obj - {Object} Object with a tile property, tile being the │ │ │ │ + * <OpenLayers.Tile.Image> with the data to add to the cache │ │ │ │ */ │ │ │ │ - commit: function(features, options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - var resp = [], │ │ │ │ - nResponses = 0; │ │ │ │ - │ │ │ │ - // Divide up features before issuing any requests. This properly │ │ │ │ - // counts requests in the event that any responses come in before │ │ │ │ - // all requests have been issued. │ │ │ │ - var types = {}; │ │ │ │ - types[OpenLayers.State.INSERT] = []; │ │ │ │ - types[OpenLayers.State.UPDATE] = []; │ │ │ │ - types[OpenLayers.State.DELETE] = []; │ │ │ │ - var feature, list, requestFeatures = []; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - feature = features[i]; │ │ │ │ - list = types[feature.state]; │ │ │ │ - if (list) { │ │ │ │ - list.push(feature); │ │ │ │ - requestFeatures.push(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - // tally up number of requests │ │ │ │ - var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + │ │ │ │ - types[OpenLayers.State.UPDATE].length + │ │ │ │ - types[OpenLayers.State.DELETE].length; │ │ │ │ - │ │ │ │ - // This response will be sent to the final callback after all the others │ │ │ │ - // have been fired. │ │ │ │ - var success = true; │ │ │ │ - var finalResponse = new OpenLayers.Protocol.Response({ │ │ │ │ - reqFeatures: requestFeatures │ │ │ │ - }); │ │ │ │ - │ │ │ │ - function insertCallback(response) { │ │ │ │ - var len = response.features ? response.features.length : 0; │ │ │ │ - var fids = new Array(len); │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ - fids[i] = response.features[i].fid; │ │ │ │ - } │ │ │ │ - finalResponse.insertIds = fids; │ │ │ │ - callback.apply(this, [response]); │ │ │ │ - } │ │ │ │ - │ │ │ │ - function callback(response) { │ │ │ │ - this.callUserCallback(response, options); │ │ │ │ - success = success && response.success(); │ │ │ │ - nResponses++; │ │ │ │ - if (nResponses >= nRequests) { │ │ │ │ - if (options.callback) { │ │ │ │ - finalResponse.code = success ? │ │ │ │ - OpenLayers.Protocol.Response.SUCCESS : │ │ │ │ - OpenLayers.Protocol.Response.FAILURE; │ │ │ │ - options.callback.apply(options.scope, [finalResponse]); │ │ │ │ + cache: function(obj) { │ │ │ │ + if (window.localStorage) { │ │ │ │ + var tile = obj.tile; │ │ │ │ + try { │ │ │ │ + var canvasContext = tile.getCanvasContext(); │ │ │ │ + if (canvasContext) { │ │ │ │ + var urlMap = OpenLayers.Control.CacheWrite.urlMap; │ │ │ │ + var url = urlMap[tile.url] || tile.url; │ │ │ │ + window.localStorage.setItem( │ │ │ │ + "olCache_" + url, │ │ │ │ + canvasContext.canvas.toDataURL(this.imageFormat) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } catch (e) { │ │ │ │ + // local storage full or CORS violation │ │ │ │ + var reason = e.name || e.message; │ │ │ │ + if (reason && this.quotaRegEx.test(reason)) { │ │ │ │ + this.events.triggerEvent("cachefull", { │ │ │ │ + tile: tile │ │ │ │ + }); │ │ │ │ + } else { │ │ │ │ + OpenLayers.Console.error(e.toString()); │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ - │ │ │ │ - // start issuing requests │ │ │ │ - var queue = types[OpenLayers.State.INSERT]; │ │ │ │ - if (queue.length > 0) { │ │ │ │ - resp.push(this.create( │ │ │ │ - queue, OpenLayers.Util.applyDefaults({ │ │ │ │ - callback: insertCallback, │ │ │ │ - scope: this │ │ │ │ - }, options.create) │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - queue = types[OpenLayers.State.UPDATE]; │ │ │ │ - for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ - resp.push(this.update( │ │ │ │ - queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ - callback: callback, │ │ │ │ - scope: this │ │ │ │ - }, options.update))); │ │ │ │ - } │ │ │ │ - queue = types[OpenLayers.State.DELETE]; │ │ │ │ - for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ - resp.push(this["delete"]( │ │ │ │ - queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ - callback: callback, │ │ │ │ - scope: this │ │ │ │ - }, options["delete"]))); │ │ │ │ - } │ │ │ │ - return resp; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: abort │ │ │ │ - * Abort an ongoing request, the response object passed to │ │ │ │ - * this method must come from this HTTP protocol (as a result │ │ │ │ - * of a create, read, update, delete or commit operation). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} │ │ │ │ + * Method: destroy │ │ │ │ + * The destroy method is used to perform any clean up before the control │ │ │ │ + * is dereferenced. Typically this is where event listeners are removed │ │ │ │ + * to prevent memory leaks. │ │ │ │ */ │ │ │ │ - abort: function(response) { │ │ │ │ - if (response) { │ │ │ │ - response.priv.abort(); │ │ │ │ + destroy: function() { │ │ │ │ + if (this.layers || this.map) { │ │ │ │ + var i, layers = this.layers || this.map.layers; │ │ │ │ + for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ + this.removeLayer({ │ │ │ │ + layer: layers[i] │ │ │ │ + }); │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: callUserCallback │ │ │ │ - * This method is used from within the commit method each time an │ │ │ │ - * an HTTP response is received from the server, it is responsible │ │ │ │ - * for calling the user-supplied callbacks. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} │ │ │ │ - * options - {Object} The map of options passed to the commit call. │ │ │ │ - */ │ │ │ │ - callUserCallback: function(resp, options) { │ │ │ │ - var opt = options[resp.requestType]; │ │ │ │ - if (opt && opt.callback) { │ │ │ │ - opt.callback.call(opt.scope, resp); │ │ │ │ + if (this.map) { │ │ │ │ + this.map.events.un({ │ │ │ │ + addlayer: this.addLayer, │ │ │ │ + removeLayer: this.removeLayer, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.HTTP" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.CacheWrite" │ │ │ │ }); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * APIFunction: OpenLayers.Control.CacheWrite.clearCache │ │ │ │ + * Clears all tiles cached with <OpenLayers.Control.CacheWrite> from the cache. │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.CacheWrite.clearCache = function() { │ │ │ │ + if (!window.localStorage) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + var i, key; │ │ │ │ + for (i = window.localStorage.length - 1; i >= 0; --i) { │ │ │ │ + key = window.localStorage.key(i); │ │ │ │ + if (key.substr(0, 8) === "olCache_") { │ │ │ │ + window.localStorage.removeItem(key); │ │ │ │ + } │ │ │ │ + } │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Property: OpenLayers.Control.CacheWrite.urlMap │ │ │ │ + * {Object} Mapping of same origin urls to cache url keys. Entries will be │ │ │ │ + * deleted as soon as a tile was cached. │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.CacheWrite.urlMap = {}; │ │ │ │ + │ │ │ │ + │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Protocol/CSW/v2_0_2.js │ │ │ │ + OpenLayers/Control/CacheRead.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Protocol/CSW.js │ │ │ │ - * @requires OpenLayers/Format/CSWGetRecords/v2_0_2.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Protocol.CSW.v2_0_2 │ │ │ │ - * CS-W (Catalogue services for the Web) version 2.0.2 protocol. │ │ │ │ + * Class: OpenLayers.Control.CacheRead │ │ │ │ + * A control for using image tiles cached with <OpenLayers.Control.CacheWrite> │ │ │ │ + * from the browser's local storage. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Protocol> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol.CSW.v2_0_2 = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ +OpenLayers.Control.CacheRead = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: formatOptions │ │ │ │ - * {Object} Optional options for the format. If a format is not provided, │ │ │ │ - * this property can be used to extend the default format options. │ │ │ │ + * APIProperty: fetchEvent │ │ │ │ + * {String} The layer event to listen to for replacing remote resource tile │ │ │ │ + * URLs with cached data URIs. Supported values are "tileerror" (try │ │ │ │ + * remote first, fall back to cached) and "tileloadstart" (try cache │ │ │ │ + * first, fall back to remote). Default is "tileloadstart". │ │ │ │ + * │ │ │ │ + * Note that "tileerror" will not work for CORS enabled images (see │ │ │ │ + * https://developer.mozilla.org/en/CORS_Enabled_Image), i.e. layers │ │ │ │ + * configured with a <OpenLayers.Tile.Image.crossOriginKeyword> in │ │ │ │ + * <OpenLayers.Layer.Grid.tileOptions>. │ │ │ │ */ │ │ │ │ - formatOptions: null, │ │ │ │ + fetchEvent: "tileloadstart", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Protocol.CSW.v2_0_2 │ │ │ │ - * A class for CSW version 2.0.2 protocol management. │ │ │ │ + * APIProperty: layers │ │ │ │ + * {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, only these │ │ │ │ + * layers will receive tiles from the cache. │ │ │ │ + */ │ │ │ │ + layers: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * true. │ │ │ │ + */ │ │ │ │ + autoActivate: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.CacheRead │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * options - {Object} Object with API properties for this control │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Protocol.prototype.initialize.apply(this, [options]); │ │ │ │ - if (!options.format) { │ │ │ │ - this.format = new OpenLayers.Format.CSWGetRecords.v2_0_2(OpenLayers.Util.extend({}, this.formatOptions)); │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the control. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ + var i, layers = this.layers || map.layers; │ │ │ │ + for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ + this.addLayer({ │ │ │ │ + layer: layers[i] │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + if (!this.layers) { │ │ │ │ + map.events.on({ │ │ │ │ + addlayer: this.addLayer, │ │ │ │ + removeLayer: this.removeLayer, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Clean up the protocol. │ │ │ │ + * Method: addLayer │ │ │ │ + * Adds a layer to the control. Once added, tiles requested for this layer │ │ │ │ + * will be cached. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} Object with a layer property referencing an │ │ │ │ + * <OpenLayers.Layer> instance │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.options && !this.options.format) { │ │ │ │ - this.format.destroy(); │ │ │ │ - } │ │ │ │ - this.format = null; │ │ │ │ - OpenLayers.Protocol.prototype.destroy.apply(this); │ │ │ │ + addLayer: function(evt) { │ │ │ │ + evt.layer.events.register(this.fetchEvent, this, this.fetch); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read │ │ │ │ - * Construct a request for reading new records from the Catalogue. │ │ │ │ + * Method: removeLayer │ │ │ │ + * Removes a layer from the control. Once removed, tiles requested for this │ │ │ │ + * layer will no longer be cached. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} Object with a layer property referencing an │ │ │ │ + * <OpenLayers.Layer> instance │ │ │ │ */ │ │ │ │ - read: function(options) { │ │ │ │ - options = OpenLayers.Util.extend({}, options); │ │ │ │ - OpenLayers.Util.applyDefaults(options, this.options || {}); │ │ │ │ - var response = new OpenLayers.Protocol.Response({ │ │ │ │ - requestType: "read" │ │ │ │ - }); │ │ │ │ - │ │ │ │ - var data = this.format.write(options.params || options); │ │ │ │ - │ │ │ │ - response.priv = OpenLayers.Request.POST({ │ │ │ │ - url: options.url, │ │ │ │ - callback: this.createCallback(this.handleRead, response, options), │ │ │ │ - params: options.params, │ │ │ │ - headers: options.headers, │ │ │ │ - data: data │ │ │ │ - }); │ │ │ │ - │ │ │ │ - return response; │ │ │ │ + removeLayer: function(evt) { │ │ │ │ + evt.layer.events.unregister(this.fetchEvent, this, this.fetch); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleRead │ │ │ │ - * Deal with response from the read request. │ │ │ │ + * Method: fetch │ │ │ │ + * Listener to the <fetchEvent> event. Replaces a tile's url with a data │ │ │ │ + * URI from the cache. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} The response object to pass │ │ │ │ - * to the user callback. │ │ │ │ - * This response is given a code property, and optionally a data property. │ │ │ │ - * The latter represents the CSW records as returned by the call to │ │ │ │ - * the CSW format read method. │ │ │ │ - * options - {Object} The user options passed to the read call. │ │ │ │ + * evt - {Object} Event object with a tile property. │ │ │ │ */ │ │ │ │ - handleRead: function(response, options) { │ │ │ │ - if (options.callback) { │ │ │ │ - var request = response.priv; │ │ │ │ - if (request.status >= 200 && request.status < 300) { │ │ │ │ - // success │ │ │ │ - response.data = this.parseData(request); │ │ │ │ - response.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ - } else { │ │ │ │ - // failure │ │ │ │ - response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + fetch: function(evt) { │ │ │ │ + if (this.active && window.localStorage && │ │ │ │ + evt.tile instanceof OpenLayers.Tile.Image) { │ │ │ │ + var tile = evt.tile, │ │ │ │ + url = tile.url; │ │ │ │ + // deal with modified tile urls when both CacheWrite and CacheRead │ │ │ │ + // are active │ │ │ │ + if (!tile.layer.crossOriginKeyword && OpenLayers.ProxyHost && │ │ │ │ + url.indexOf(OpenLayers.ProxyHost) === 0) { │ │ │ │ + url = OpenLayers.Control.CacheWrite.urlMap[url]; │ │ │ │ + } │ │ │ │ + var dataURI = window.localStorage.getItem("olCache_" + url); │ │ │ │ + if (dataURI) { │ │ │ │ + tile.url = dataURI; │ │ │ │ + if (evt.type === "tileerror") { │ │ │ │ + tile.setImgSrc(dataURI); │ │ │ │ + } │ │ │ │ } │ │ │ │ - options.callback.call(options.scope, response); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseData │ │ │ │ - * Read HTTP response body and return records │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * request - {XMLHttpRequest} The request object │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} The CSW records as returned by the call to the format read method. │ │ │ │ + * Method: destroy │ │ │ │ + * The destroy method is used to perform any clean up before the control │ │ │ │ + * is dereferenced. Typically this is where event listeners are removed │ │ │ │ + * to prevent memory leaks. │ │ │ │ */ │ │ │ │ - parseData: function(request) { │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText; │ │ │ │ + destroy: function() { │ │ │ │ + if (this.layers || this.map) { │ │ │ │ + var i, layers = this.layers || this.map.layers; │ │ │ │ + for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ + this.removeLayer({ │ │ │ │ + layer: layers[i] │ │ │ │ + }); │ │ │ │ + } │ │ │ │ } │ │ │ │ - if (!doc || doc.length <= 0) { │ │ │ │ - return null; │ │ │ │ + if (this.map) { │ │ │ │ + this.map.events.un({ │ │ │ │ + addlayer: this.addLayer, │ │ │ │ + removeLayer: this.removeLayer, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ - return this.format.read(doc); │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.CSW.v2_0_2" │ │ │ │ - │ │ │ │ + CLASS_NAME: "OpenLayers.Control.CacheRead" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Protocol/SOS/v1_0_0.js │ │ │ │ + OpenLayers/Control/KeyboardDefaults.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Protocol/SOS.js │ │ │ │ - * @requires OpenLayers/Format/SOSGetFeatureOfInterest.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Handler/Keyboard.js │ │ │ │ + * @requires OpenLayers/Events.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Protocol.SOS.v1_0_0 │ │ │ │ - * An SOS v1.0.0 Protocol for vector layers. Create a new instance with the │ │ │ │ - * <OpenLayers.Protocol.SOS.v1_0_0> constructor. │ │ │ │ + * Class: OpenLayers.Control.KeyboardDefaults │ │ │ │ + * The KeyboardDefaults control adds panning and zooming functions, controlled │ │ │ │ + * with the keyboard. By default arrow keys pan, +/- keys zoom & Page Up/Page │ │ │ │ + * Down/Home/End scroll by three quarters of a page. │ │ │ │ + * │ │ │ │ + * This control has no visible appearance. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Protocol> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol.SOS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ +OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: fois │ │ │ │ - * {Array(String)} Array of features of interest (foi) │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * true. │ │ │ │ */ │ │ │ │ - fois: null, │ │ │ │ + autoActivate: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: formatOptions │ │ │ │ - * {Object} Optional options for the format. If a format is not provided, │ │ │ │ - * this property can be used to extend the default format options. │ │ │ │ + * APIProperty: slideFactor │ │ │ │ + * Pixels to slide by. │ │ │ │ */ │ │ │ │ - formatOptions: null, │ │ │ │ + slideFactor: 75, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Protocol.SOS │ │ │ │ - * A class for giving layers an SOS protocol. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - * │ │ │ │ - * Valid options properties: │ │ │ │ - * url - {String} URL to send requests to (required). │ │ │ │ - * fois - {Array} The features of interest (required). │ │ │ │ + * APIProperty: observeElement │ │ │ │ + * {DOMelement|String} The DOM element to handle keys for. You │ │ │ │ + * can use the map div here, to have the navigation keys │ │ │ │ + * work when the map div has the focus. If undefined the │ │ │ │ + * document is used. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Protocol.prototype.initialize.apply(this, [options]); │ │ │ │ - if (!options.format) { │ │ │ │ - this.format = new OpenLayers.Format.SOSGetFeatureOfInterest( │ │ │ │ - this.formatOptions); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + observeElement: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Clean up the protocol. │ │ │ │ + * Constructor: OpenLayers.Control.KeyboardDefaults │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.options && !this.options.format) { │ │ │ │ - this.format.destroy(); │ │ │ │ - } │ │ │ │ - this.format = null; │ │ │ │ - OpenLayers.Protocol.prototype.destroy.apply(this); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Construct a request for reading new sensor positions. This is done by │ │ │ │ - * issuing one GetFeatureOfInterest request. │ │ │ │ + * Method: draw │ │ │ │ + * Create handler. │ │ │ │ */ │ │ │ │ - read: function(options) { │ │ │ │ - options = OpenLayers.Util.extend({}, options); │ │ │ │ - OpenLayers.Util.applyDefaults(options, this.options || {}); │ │ │ │ - var response = new OpenLayers.Protocol.Response({ │ │ │ │ - requestType: "read" │ │ │ │ - }); │ │ │ │ - var format = this.format; │ │ │ │ - var data = OpenLayers.Format.XML.prototype.write.apply(format, │ │ │ │ - [format.writeNode("sos:GetFeatureOfInterest", { │ │ │ │ - fois: this.fois │ │ │ │ - })] │ │ │ │ - ); │ │ │ │ - response.priv = OpenLayers.Request.POST({ │ │ │ │ - url: options.url, │ │ │ │ - callback: this.createCallback(this.handleRead, response, options), │ │ │ │ - data: data │ │ │ │ + draw: function() { │ │ │ │ + var observeElement = this.observeElement || document; │ │ │ │ + this.handler = new OpenLayers.Handler.Keyboard(this, { │ │ │ │ + "keydown": this.defaultKeyPress │ │ │ │ + }, { │ │ │ │ + observeElement: observeElement │ │ │ │ }); │ │ │ │ - return response; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleRead │ │ │ │ - * Deal with response from the read request. │ │ │ │ + * Method: defaultKeyPress │ │ │ │ + * When handling the key event, we only use evt.keyCode. This holds │ │ │ │ + * some drawbacks, though we get around them below. When interpretting │ │ │ │ + * the keycodes below (including the comments associated with them), │ │ │ │ + * consult the URL below. For instance, the Safari browser returns │ │ │ │ + * "IE keycodes", and so is supported by any keycode labeled "IE". │ │ │ │ + * │ │ │ │ + * Very informative URL: │ │ │ │ + * http://unixpapa.com/js/key.html │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} The response object to pass │ │ │ │ - * to the user callback. │ │ │ │ - * options - {Object} The user options passed to the read call. │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - handleRead: function(response, options) { │ │ │ │ - if (options.callback) { │ │ │ │ - var request = response.priv; │ │ │ │ - if (request.status >= 200 && request.status < 300) { │ │ │ │ - // success │ │ │ │ - response.features = this.parseFeatures(request); │ │ │ │ - response.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ - } else { │ │ │ │ - // failure │ │ │ │ - response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ - } │ │ │ │ - options.callback.call(options.scope, response); │ │ │ │ + defaultKeyPress: function(evt) { │ │ │ │ + var size, handled = true; │ │ │ │ + │ │ │ │ + var target = OpenLayers.Event.element(evt); │ │ │ │ + if (target && │ │ │ │ + (target.tagName == 'INPUT' || │ │ │ │ + target.tagName == 'TEXTAREA' || │ │ │ │ + target.tagName == 'SELECT')) { │ │ │ │ + return; │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: parseFeatures │ │ │ │ - * Read HTTP response body and return features │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * request - {XMLHttpRequest} The request object │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array({<OpenLayers.Feature.Vector>})} Array of features │ │ │ │ - */ │ │ │ │ - parseFeatures: function(request) { │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText; │ │ │ │ + switch (evt.keyCode) { │ │ │ │ + case OpenLayers.Event.KEY_LEFT: │ │ │ │ + this.map.pan(-this.slideFactor, 0); │ │ │ │ + break; │ │ │ │ + case OpenLayers.Event.KEY_RIGHT: │ │ │ │ + this.map.pan(this.slideFactor, 0); │ │ │ │ + break; │ │ │ │ + case OpenLayers.Event.KEY_UP: │ │ │ │ + this.map.pan(0, -this.slideFactor); │ │ │ │ + break; │ │ │ │ + case OpenLayers.Event.KEY_DOWN: │ │ │ │ + this.map.pan(0, this.slideFactor); │ │ │ │ + break; │ │ │ │ + │ │ │ │ + case 33: // Page Up. Same in all browsers. │ │ │ │ + size = this.map.getSize(); │ │ │ │ + this.map.pan(0, -0.75 * size.h); │ │ │ │ + break; │ │ │ │ + case 34: // Page Down. Same in all browsers. │ │ │ │ + size = this.map.getSize(); │ │ │ │ + this.map.pan(0, 0.75 * size.h); │ │ │ │ + break; │ │ │ │ + case 35: // End. Same in all browsers. │ │ │ │ + size = this.map.getSize(); │ │ │ │ + this.map.pan(0.75 * size.w, 0); │ │ │ │ + break; │ │ │ │ + case 36: // Home. Same in all browsers. │ │ │ │ + size = this.map.getSize(); │ │ │ │ + this.map.pan(-0.75 * size.w, 0); │ │ │ │ + break; │ │ │ │ + │ │ │ │ + case 43: // +/= (ASCII), keypad + (ASCII, Opera) │ │ │ │ + case 61: // +/= (Mozilla, Opera, some ASCII) │ │ │ │ + case 187: // +/= (IE) │ │ │ │ + case 107: // keypad + (IE, Mozilla) │ │ │ │ + this.map.zoomIn(); │ │ │ │ + break; │ │ │ │ + case 45: // -/_ (ASCII, Opera), keypad - (ASCII, Opera) │ │ │ │ + case 109: // -/_ (Mozilla), keypad - (Mozilla, IE) │ │ │ │ + case 189: // -/_ (IE) │ │ │ │ + case 95: // -/_ (some ASCII) │ │ │ │ + this.map.zoomOut(); │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + handled = false; │ │ │ │ } │ │ │ │ - if (!doc || doc.length <= 0) { │ │ │ │ - return null; │ │ │ │ + if (handled) { │ │ │ │ + // prevent browser default not to move the page │ │ │ │ + // when moving the page with the keyboard │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ } │ │ │ │ - return this.format.read(doc); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.SOS.v1_0_0" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.KeyboardDefaults" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Protocol/WFS/v1.js │ │ │ │ + OpenLayers/Control/PanZoomBar.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Protocol/WFS.js │ │ │ │ + * @requires OpenLayers/Control/PanZoom.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Protocol.WFS.v1 │ │ │ │ - * Abstract class for for v1.0.0 and v1.1.0 protocol. │ │ │ │ + * Class: OpenLayers.Control.PanZoomBar │ │ │ │ + * The PanZoomBar is a visible control composed of a │ │ │ │ + * <OpenLayers.Control.PanPanel> and a <OpenLayers.Control.ZoomBar>. │ │ │ │ + * By default it is displayed in the upper left corner of the map as 4 │ │ │ │ + * directional arrows above a vertical slider. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Protocol> │ │ │ │ + * - <OpenLayers.Control.PanZoom> │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ +OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: version │ │ │ │ - * {String} WFS version number. │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomStopWidth │ │ │ │ */ │ │ │ │ - version: null, │ │ │ │ + zoomStopWidth: 18, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: srsName │ │ │ │ - * {String} Name of spatial reference system. Default is "EPSG:4326". │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomStopHeight │ │ │ │ */ │ │ │ │ - srsName: "EPSG:4326", │ │ │ │ + zoomStopHeight: 11, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: featureType │ │ │ │ - * {String} Local feature typeName. │ │ │ │ + /** │ │ │ │ + * Property: slider │ │ │ │ */ │ │ │ │ - featureType: null, │ │ │ │ + slider: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: featureNS │ │ │ │ - * {String} Feature namespace. │ │ │ │ + /** │ │ │ │ + * Property: sliderEvents │ │ │ │ + * {<OpenLayers.Events>} │ │ │ │ */ │ │ │ │ - featureNS: null, │ │ │ │ + sliderEvents: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: geometryName │ │ │ │ - * {String} Name of the geometry attribute for features. Default is │ │ │ │ - * "the_geom" for WFS <version> 1.0, and null for higher versions. │ │ │ │ + /** │ │ │ │ + * Property: zoombarDiv │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - geometryName: "the_geom", │ │ │ │ + zoombarDiv: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: maxFeatures │ │ │ │ - * {Integer} Optional maximum number of features to retrieve. │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomWorldIcon │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ + zoomWorldIcon: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: schema │ │ │ │ - * {String} Optional schema location that will be included in the │ │ │ │ - * schemaLocation attribute value. Note that the feature type schema │ │ │ │ - * is required for a strict XML validator (on transactions with an │ │ │ │ - * insert for example), but is *not* required by the WFS specification │ │ │ │ - * (since the server is supposed to know about feature type schemas). │ │ │ │ + * APIProperty: panIcons │ │ │ │ + * {Boolean} Set this property to false not to display the pan icons. If │ │ │ │ + * false the zoom world icon is placed under the zoom bar. Defaults to │ │ │ │ + * true. │ │ │ │ */ │ │ │ │ - schema: null, │ │ │ │ + panIcons: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: featurePrefix │ │ │ │ - * {String} Namespace alias for feature type. Default is "feature". │ │ │ │ + * APIProperty: forceFixedZoomLevel │ │ │ │ + * {Boolean} Force a fixed zoom level even though the map has │ │ │ │ + * fractionalZoom │ │ │ │ */ │ │ │ │ - featurePrefix: "feature", │ │ │ │ + forceFixedZoomLevel: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: formatOptions │ │ │ │ - * {Object} Optional options for the format. If a format is not provided, │ │ │ │ - * this property can be used to extend the default format options. │ │ │ │ + * Property: mouseDragStart │ │ │ │ + * {<OpenLayers.Pixel>} │ │ │ │ */ │ │ │ │ - formatOptions: null, │ │ │ │ + mouseDragStart: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: readFormat │ │ │ │ - * {<OpenLayers.Format>} For WFS requests it is possible to get a │ │ │ │ - * different output format than GML. In that case, we cannot parse │ │ │ │ - * the response with the default format (WFST) and we need a different │ │ │ │ - * format for reading. │ │ │ │ + /** │ │ │ │ + * Property: deltaY │ │ │ │ + * {Number} The cumulative vertical pixel offset during a zoom bar drag. │ │ │ │ */ │ │ │ │ - readFormat: null, │ │ │ │ + deltaY: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: readOptions │ │ │ │ - * {Object} Optional object to pass to format's read. │ │ │ │ + * Property: zoomStart │ │ │ │ + * {<OpenLayers.Pixel>} │ │ │ │ */ │ │ │ │ - readOptions: null, │ │ │ │ + zoomStart: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Protocol.WFS │ │ │ │ - * A class for giving layers WFS protocol. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - * │ │ │ │ - * Valid options properties: │ │ │ │ - * url - {String} URL to send requests to (required). │ │ │ │ - * featureType - {String} Local (without prefix) feature typeName (required). │ │ │ │ - * featureNS - {String} Feature namespace (required, but can be autodetected │ │ │ │ - * during the first query if GML is used as readFormat and │ │ │ │ - * featurePrefix is provided and matches the prefix used by the server │ │ │ │ - * for this featureType). │ │ │ │ - * featurePrefix - {String} Feature namespace alias (optional - only used │ │ │ │ - * for writing if featureNS is provided). Default is 'feature'. │ │ │ │ - * geometryName - {String} Name of geometry attribute. The default is │ │ │ │ - * 'the_geom' for WFS <version> 1.0, and null for higher versions. If │ │ │ │ - * null, it will be set to the name of the first geometry found in the │ │ │ │ - * first read operation. │ │ │ │ - * multi - {Boolean} If set to true, geometries will be casted to Multi │ │ │ │ - * geometries before they are written in a transaction. No casting will │ │ │ │ - * be done when reading features. │ │ │ │ + * Constructor: OpenLayers.Control.PanZoomBar │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Protocol.prototype.initialize.apply(this, [options]); │ │ │ │ - if (!options.format) { │ │ │ │ - this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({ │ │ │ │ - version: this.version, │ │ │ │ - featureType: this.featureType, │ │ │ │ - featureNS: this.featureNS, │ │ │ │ - featurePrefix: this.featurePrefix, │ │ │ │ - geometryName: this.geometryName, │ │ │ │ - srsName: this.srsName, │ │ │ │ - schema: this.schema │ │ │ │ - }, this.formatOptions)); │ │ │ │ - } │ │ │ │ - if (!options.geometryName && parseFloat(this.format.version) > 1.0) { │ │ │ │ - this.setGeometryName(null); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ * APIMethod: destroy │ │ │ │ - * Clean up the protocol. │ │ │ │ */ │ │ │ │ destroy: function() { │ │ │ │ - if (this.options && !this.options.format) { │ │ │ │ - this.format.destroy(); │ │ │ │ - } │ │ │ │ - this.format = null; │ │ │ │ - OpenLayers.Protocol.prototype.destroy.apply(this); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Construct a request for reading new features. Since WFS splits the │ │ │ │ - * basic CRUD operations into GetFeature requests (for read) and │ │ │ │ - * Transactions (for all others), this method does not make use of the │ │ │ │ - * format's read method (that is only about reading transaction │ │ │ │ - * responses). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Options for the read operation, in addition to the │ │ │ │ - * options set on the instance (options set here will take precedence). │ │ │ │ - * │ │ │ │ - * To use a configured protocol to get e.g. a WFS hit count, applications │ │ │ │ - * could do the following: │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * protocol.read({ │ │ │ │ - * readOptions: {output: "object"}, │ │ │ │ - * resultType: "hits", │ │ │ │ - * maxFeatures: null, │ │ │ │ - * callback: function(resp) { │ │ │ │ - * // process resp.numberOfFeatures here │ │ │ │ - * } │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * To use a configured protocol to use WFS paging (if supported by the │ │ │ │ - * server), applications could do the following: │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * protocol.read({ │ │ │ │ - * startIndex: 0, │ │ │ │ - * count: 50 │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * To limit the attributes returned by the GetFeature request, applications │ │ │ │ - * can use the propertyNames option to specify the properties to include in │ │ │ │ - * the response: │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * protocol.read({ │ │ │ │ - * propertyNames: ["DURATION", "INTENSITY"] │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ - read: function(options) { │ │ │ │ - OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ - options = OpenLayers.Util.extend({}, options); │ │ │ │ - OpenLayers.Util.applyDefaults(options, this.options || {}); │ │ │ │ - var response = new OpenLayers.Protocol.Response({ │ │ │ │ - requestType: "read" │ │ │ │ - }); │ │ │ │ │ │ │ │ - var data = OpenLayers.Format.XML.prototype.write.apply( │ │ │ │ - this.format, [this.format.writeNode("wfs:GetFeature", options)] │ │ │ │ - ); │ │ │ │ + this._removeZoomBar(); │ │ │ │ │ │ │ │ - response.priv = OpenLayers.Request.POST({ │ │ │ │ - url: options.url, │ │ │ │ - callback: this.createCallback(this.handleRead, response, options), │ │ │ │ - params: options.params, │ │ │ │ - headers: options.headers, │ │ │ │ - data: data │ │ │ │ + this.map.events.un({ │ │ │ │ + "changebaselayer": this.redraw, │ │ │ │ + "updatesize": this.redraw, │ │ │ │ + scope: this │ │ │ │ }); │ │ │ │ │ │ │ │ - return response; │ │ │ │ + OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments); │ │ │ │ + │ │ │ │ + delete this.mouseDragStart; │ │ │ │ + delete this.zoomStart; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setFeatureType │ │ │ │ - * Change the feature type on the fly. │ │ │ │ - * │ │ │ │ + * Method: setMap │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * featureType - {String} Local (without prefix) feature typeName. │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - setFeatureType: function(featureType) { │ │ │ │ - this.featureType = featureType; │ │ │ │ - this.format.featureType = featureType; │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments); │ │ │ │ + this.map.events.on({ │ │ │ │ + "changebaselayer": this.redraw, │ │ │ │ + "updatesize": this.redraw, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: setGeometryName │ │ │ │ - * Sets the geometryName option after instantiation. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometryName - {String} Name of geometry attribute. │ │ │ │ + /** │ │ │ │ + * Method: redraw │ │ │ │ + * clear the div and start over. │ │ │ │ */ │ │ │ │ - setGeometryName: function(geometryName) { │ │ │ │ - this.geometryName = geometryName; │ │ │ │ - this.format.geometryName = geometryName; │ │ │ │ + redraw: function() { │ │ │ │ + if (this.div != null) { │ │ │ │ + this.removeButtons(); │ │ │ │ + this._removeZoomBar(); │ │ │ │ + } │ │ │ │ + this.draw(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleRead │ │ │ │ - * Deal with response from the read request. │ │ │ │ + * Method: draw │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} The response object to pass │ │ │ │ - * to the user callback. │ │ │ │ - * options - {Object} The user options passed to the read call. │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ */ │ │ │ │ - handleRead: function(response, options) { │ │ │ │ - options = OpenLayers.Util.extend({}, options); │ │ │ │ - OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + draw: function(px) { │ │ │ │ + // initialize our internal div │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ + px = this.position.clone(); │ │ │ │ │ │ │ │ - if (options.callback) { │ │ │ │ - var request = response.priv; │ │ │ │ - if (request.status >= 200 && request.status < 300) { │ │ │ │ - // success │ │ │ │ - var result = this.parseResponse(request, options.readOptions); │ │ │ │ - if (result && result.success !== false) { │ │ │ │ - if (options.readOptions && options.readOptions.output == "object") { │ │ │ │ - OpenLayers.Util.extend(response, result); │ │ │ │ - } else { │ │ │ │ - response.features = result; │ │ │ │ - } │ │ │ │ - response.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ - } else { │ │ │ │ - // failure (service exception) │ │ │ │ - response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ - response.error = result; │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - // failure │ │ │ │ - response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + // place the controls │ │ │ │ + this.buttons = []; │ │ │ │ + │ │ │ │ + var sz = { │ │ │ │ + w: 18, │ │ │ │ + h: 18 │ │ │ │ + }; │ │ │ │ + if (this.panIcons) { │ │ │ │ + var centered = new OpenLayers.Pixel(px.x + sz.w / 2, px.y); │ │ │ │ + var wposition = sz.w; │ │ │ │ + │ │ │ │ + if (this.zoomWorldIcon) { │ │ │ │ + centered = new OpenLayers.Pixel(px.x + sz.w, px.y); │ │ │ │ } │ │ │ │ - options.callback.call(options.scope, response); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: parseResponse │ │ │ │ - * Read HTTP response body and return features │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * request - {XMLHttpRequest} The request object │ │ │ │ - * options - {Object} Optional object to pass to format's read │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} or {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ - * {<OpenLayers.Feature.Vector>} │ │ │ │ - * An object with a features property, an array of features or a single │ │ │ │ - * feature. │ │ │ │ - */ │ │ │ │ - parseResponse: function(request, options) { │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText; │ │ │ │ - } │ │ │ │ - if (!doc || doc.length <= 0) { │ │ │ │ - return null; │ │ │ │ - } │ │ │ │ - var result = (this.readFormat !== null) ? this.readFormat.read(doc) : │ │ │ │ - this.format.read(doc, options); │ │ │ │ - if (!this.featureNS) { │ │ │ │ - var format = this.readFormat || this.format; │ │ │ │ - this.featureNS = format.featureNS; │ │ │ │ - // no need to auto-configure again on subsequent reads │ │ │ │ - format.autoConfig = false; │ │ │ │ - if (!this.geometryName) { │ │ │ │ - this.setGeometryName(format.geometryName); │ │ │ │ + this._addButton("panup", "north-mini.png", centered, sz); │ │ │ │ + px.y = centered.y + sz.h; │ │ │ │ + this._addButton("panleft", "west-mini.png", px, sz); │ │ │ │ + if (this.zoomWorldIcon) { │ │ │ │ + this._addButton("zoomworld", "zoom-world-mini.png", px.add(sz.w, 0), sz); │ │ │ │ + │ │ │ │ + wposition *= 2; │ │ │ │ + } │ │ │ │ + this._addButton("panright", "east-mini.png", px.add(wposition, 0), sz); │ │ │ │ + this._addButton("pandown", "south-mini.png", centered.add(0, sz.h * 2), sz); │ │ │ │ + this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h * 3 + 5), sz); │ │ │ │ + centered = this._addZoomBar(centered.add(0, sz.h * 4 + 5)); │ │ │ │ + this._addButton("zoomout", "zoom-minus-mini.png", centered, sz); │ │ │ │ + } else { │ │ │ │ + this._addButton("zoomin", "zoom-plus-mini.png", px, sz); │ │ │ │ + centered = this._addZoomBar(px.add(0, sz.h)); │ │ │ │ + this._addButton("zoomout", "zoom-minus-mini.png", centered, sz); │ │ │ │ + if (this.zoomWorldIcon) { │ │ │ │ + centered = centered.add(0, sz.h + 3); │ │ │ │ + this._addButton("zoomworld", "zoom-world-mini.png", centered, sz); │ │ │ │ } │ │ │ │ } │ │ │ │ - return result; │ │ │ │ + return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: commit │ │ │ │ - * Given a list of feature, assemble a batch request for update, create, │ │ │ │ - * and delete transactions. A commit call on the prototype amounts │ │ │ │ - * to writing a WFS transaction - so the write method on the format │ │ │ │ - * is used. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: _addZoomBar │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ - * options - {Object} │ │ │ │ - * │ │ │ │ - * Valid options properties: │ │ │ │ - * nativeElements - {Array({Object})} Array of objects with information for writing │ │ │ │ - * out <Native> elements, these objects have vendorId, safeToIgnore and │ │ │ │ - * value properties. The <Native> element is intended to allow access to │ │ │ │ - * vendor specific capabilities of any particular web feature server or │ │ │ │ - * datastore. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} A response object with a features │ │ │ │ - * property containing any insertIds and a priv property referencing │ │ │ │ - * the XMLHttpRequest object. │ │ │ │ + * centered - {<OpenLayers.Pixel>} where zoombar drawing is to start. │ │ │ │ */ │ │ │ │ - commit: function(features, options) { │ │ │ │ - │ │ │ │ - options = OpenLayers.Util.extend({}, options); │ │ │ │ - OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + _addZoomBar: function(centered) { │ │ │ │ + var imgLocation = OpenLayers.Util.getImageLocation("slider.png"); │ │ │ │ + var id = this.id + "_" + this.map.id; │ │ │ │ + var minZoom = this.map.getMinZoom(); │ │ │ │ + var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom(); │ │ │ │ + var slider = OpenLayers.Util.createAlphaImageDiv(id, │ │ │ │ + centered.add(-1, zoomsToEnd * this.zoomStopHeight), { │ │ │ │ + w: 20, │ │ │ │ + h: 9 │ │ │ │ + }, │ │ │ │ + imgLocation, │ │ │ │ + "absolute"); │ │ │ │ + slider.style.cursor = "move"; │ │ │ │ + this.slider = slider; │ │ │ │ │ │ │ │ - var response = new OpenLayers.Protocol.Response({ │ │ │ │ - requestType: "commit", │ │ │ │ - reqFeatures: features │ │ │ │ + this.sliderEvents = new OpenLayers.Events(this, slider, null, true, { │ │ │ │ + includeXY: true │ │ │ │ }); │ │ │ │ - response.priv = OpenLayers.Request.POST({ │ │ │ │ - url: options.url, │ │ │ │ - headers: options.headers, │ │ │ │ - data: this.format.write(features, options), │ │ │ │ - callback: this.createCallback(this.handleCommit, response, options) │ │ │ │ + this.sliderEvents.on({ │ │ │ │ + "touchstart": this.zoomBarDown, │ │ │ │ + "touchmove": this.zoomBarDrag, │ │ │ │ + "touchend": this.zoomBarUp, │ │ │ │ + "mousedown": this.zoomBarDown, │ │ │ │ + "mousemove": this.zoomBarDrag, │ │ │ │ + "mouseup": this.zoomBarUp │ │ │ │ }); │ │ │ │ │ │ │ │ - return response; │ │ │ │ + var sz = { │ │ │ │ + w: this.zoomStopWidth, │ │ │ │ + h: this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom) │ │ │ │ + }; │ │ │ │ + var imgLocation = OpenLayers.Util.getImageLocation("zoombar.png"); │ │ │ │ + var div = null; │ │ │ │ + │ │ │ │ + if (OpenLayers.Util.alphaHack()) { │ │ │ │ + var id = this.id + "_" + this.map.id; │ │ │ │ + div = OpenLayers.Util.createAlphaImageDiv(id, centered, { │ │ │ │ + w: sz.w, │ │ │ │ + h: this.zoomStopHeight │ │ │ │ + }, │ │ │ │ + imgLocation, │ │ │ │ + "absolute", null, "crop"); │ │ │ │ + div.style.height = sz.h + "px"; │ │ │ │ + } else { │ │ │ │ + div = OpenLayers.Util.createDiv( │ │ │ │ + 'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id, │ │ │ │ + centered, │ │ │ │ + sz, │ │ │ │ + imgLocation); │ │ │ │ + } │ │ │ │ + div.style.cursor = "pointer"; │ │ │ │ + div.className = "olButton"; │ │ │ │ + this.zoombarDiv = div; │ │ │ │ + │ │ │ │ + this.div.appendChild(div); │ │ │ │ + │ │ │ │ + this.startTop = parseInt(div.style.top); │ │ │ │ + this.div.appendChild(slider); │ │ │ │ + │ │ │ │ + this.map.events.register("zoomend", this, this.moveZoomBar); │ │ │ │ + │ │ │ │ + centered = centered.add(0, │ │ │ │ + this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom)); │ │ │ │ + return centered; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleCommit │ │ │ │ - * Called when the commit request returns. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} The response object to pass │ │ │ │ - * to the user callback. │ │ │ │ - * options - {Object} The user options passed to the commit call. │ │ │ │ + * Method: _removeZoomBar │ │ │ │ */ │ │ │ │ - handleCommit: function(response, options) { │ │ │ │ - if (options.callback) { │ │ │ │ - var request = response.priv; │ │ │ │ + _removeZoomBar: function() { │ │ │ │ + this.sliderEvents.un({ │ │ │ │ + "touchstart": this.zoomBarDown, │ │ │ │ + "touchmove": this.zoomBarDrag, │ │ │ │ + "touchend": this.zoomBarUp, │ │ │ │ + "mousedown": this.zoomBarDown, │ │ │ │ + "mousemove": this.zoomBarDrag, │ │ │ │ + "mouseup": this.zoomBarUp │ │ │ │ + }); │ │ │ │ + this.sliderEvents.destroy(); │ │ │ │ │ │ │ │ - // ensure that we have an xml doc │ │ │ │ - var data = request.responseXML; │ │ │ │ - if (!data || !data.documentElement) { │ │ │ │ - data = request.responseText; │ │ │ │ - } │ │ │ │ + this.div.removeChild(this.zoombarDiv); │ │ │ │ + this.zoombarDiv = null; │ │ │ │ + this.div.removeChild(this.slider); │ │ │ │ + this.slider = null; │ │ │ │ │ │ │ │ - var obj = this.format.read(data) || {}; │ │ │ │ + this.map.events.unregister("zoomend", this, this.moveZoomBar); │ │ │ │ + }, │ │ │ │ │ │ │ │ - response.insertIds = obj.insertIds || []; │ │ │ │ - if (obj.success) { │ │ │ │ - response.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ - } else { │ │ │ │ - response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ - response.error = obj; │ │ │ │ + /** │ │ │ │ + * Method: onButtonClick │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + */ │ │ │ │ + onButtonClick: function(evt) { │ │ │ │ + OpenLayers.Control.PanZoom.prototype.onButtonClick.apply(this, arguments); │ │ │ │ + if (evt.buttonElement === this.zoombarDiv) { │ │ │ │ + var levels = evt.buttonXY.y / this.zoomStopHeight; │ │ │ │ + if (this.forceFixedZoomLevel || !this.map.fractionalZoom) { │ │ │ │ + levels = Math.floor(levels); │ │ │ │ } │ │ │ │ - options.callback.call(options.scope, response); │ │ │ │ + var zoom = (this.map.getNumZoomLevels() - 1) - levels; │ │ │ │ + zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1); │ │ │ │ + this.map.zoomTo(zoom); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: filterDelete │ │ │ │ - * Send a request that deletes all features by their filter. │ │ │ │ - * │ │ │ │ + * Method: passEventToSlider │ │ │ │ + * This function is used to pass events that happen on the div, or the map, │ │ │ │ + * through to the slider, which then does its moving thing. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * filter - {<OpenLayers.Filter>} filter │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ */ │ │ │ │ - filterDelete: function(filter, options) { │ │ │ │ - options = OpenLayers.Util.extend({}, options); │ │ │ │ - OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - │ │ │ │ - var response = new OpenLayers.Protocol.Response({ │ │ │ │ - requestType: "commit" │ │ │ │ - }); │ │ │ │ + passEventToSlider: function(evt) { │ │ │ │ + this.sliderEvents.handleBrowserEvent(evt); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var root = this.format.createElementNSPlus("wfs:Transaction", { │ │ │ │ - attributes: { │ │ │ │ - service: "WFS", │ │ │ │ - version: this.version │ │ │ │ - } │ │ │ │ + /* │ │ │ │ + * Method: zoomBarDown │ │ │ │ + * event listener for clicks on the slider │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ + */ │ │ │ │ + zoomBarDown: function(evt) { │ │ │ │ + if (!OpenLayers.Event.isLeftClick(evt) && !OpenLayers.Event.isSingleTouch(evt)) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + this.map.events.on({ │ │ │ │ + "touchmove": this.passEventToSlider, │ │ │ │ + "mousemove": this.passEventToSlider, │ │ │ │ + "mouseup": this.passEventToSlider, │ │ │ │ + scope: this │ │ │ │ }); │ │ │ │ + this.mouseDragStart = evt.xy.clone(); │ │ │ │ + this.zoomStart = evt.xy.clone(); │ │ │ │ + this.div.style.cursor = "move"; │ │ │ │ + // reset the div offsets just in case the div moved │ │ │ │ + this.zoombarDiv.offsets = null; │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var deleteNode = this.format.createElementNSPlus("wfs:Delete", { │ │ │ │ - attributes: { │ │ │ │ - typeName: (options.featureNS ? this.featurePrefix + ":" : "") + │ │ │ │ - options.featureType │ │ │ │ + /* │ │ │ │ + * Method: zoomBarDrag │ │ │ │ + * This is what happens when a click has occurred, and the client is │ │ │ │ + * dragging. Here we must ensure that the slider doesn't go beyond the │ │ │ │ + * bottom/top of the zoombar div, as well as moving the slider to its new │ │ │ │ + * visual location │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ + */ │ │ │ │ + zoomBarDrag: function(evt) { │ │ │ │ + if (this.mouseDragStart != null) { │ │ │ │ + var deltaY = this.mouseDragStart.y - evt.xy.y; │ │ │ │ + var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv); │ │ │ │ + if ((evt.clientY - offsets[1]) > 0 && │ │ │ │ + (evt.clientY - offsets[1]) < parseInt(this.zoombarDiv.style.height) - 2) { │ │ │ │ + var newTop = parseInt(this.slider.style.top) - deltaY; │ │ │ │ + this.slider.style.top = newTop + "px"; │ │ │ │ + this.mouseDragStart = evt.xy.clone(); │ │ │ │ } │ │ │ │ - }); │ │ │ │ - │ │ │ │ - if (options.featureNS) { │ │ │ │ - deleteNode.setAttribute("xmlns:" + this.featurePrefix, options.featureNS); │ │ │ │ + // set cumulative displacement │ │ │ │ + this.deltaY = this.zoomStart.y - evt.xy.y; │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ } │ │ │ │ - var filterNode = this.format.writeNode("ogc:Filter", filter); │ │ │ │ - │ │ │ │ - deleteNode.appendChild(filterNode); │ │ │ │ - │ │ │ │ - root.appendChild(deleteNode); │ │ │ │ - │ │ │ │ - var data = OpenLayers.Format.XML.prototype.write.apply( │ │ │ │ - this.format, [root] │ │ │ │ - ); │ │ │ │ - │ │ │ │ - return OpenLayers.Request.POST({ │ │ │ │ - url: this.url, │ │ │ │ - callback: options.callback || function() {}, │ │ │ │ - data: data │ │ │ │ - }); │ │ │ │ - │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: abort │ │ │ │ - * Abort an ongoing request, the response object passed to │ │ │ │ - * this method must come from this protocol (as a result │ │ │ │ - * of a read, or commit operation). │ │ │ │ + /* │ │ │ │ + * Method: zoomBarUp │ │ │ │ + * Perform cleanup when a mouseup event is received -- discover new zoom │ │ │ │ + * level and switch to it. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ */ │ │ │ │ - abort: function(response) { │ │ │ │ - if (response) { │ │ │ │ - response.priv.abort(); │ │ │ │ + zoomBarUp: function(evt) { │ │ │ │ + if (!OpenLayers.Event.isLeftClick(evt) && evt.type !== "touchend") { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (this.mouseDragStart) { │ │ │ │ + this.div.style.cursor = ""; │ │ │ │ + this.map.events.un({ │ │ │ │ + "touchmove": this.passEventToSlider, │ │ │ │ + "mouseup": this.passEventToSlider, │ │ │ │ + "mousemove": this.passEventToSlider, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + var zoomLevel = this.map.zoom; │ │ │ │ + if (!this.forceFixedZoomLevel && this.map.fractionalZoom) { │ │ │ │ + zoomLevel += this.deltaY / this.zoomStopHeight; │ │ │ │ + zoomLevel = Math.min(Math.max(zoomLevel, 0), │ │ │ │ + this.map.getNumZoomLevels() - 1); │ │ │ │ + } else { │ │ │ │ + zoomLevel += this.deltaY / this.zoomStopHeight; │ │ │ │ + zoomLevel = Math.max(Math.round(zoomLevel), 0); │ │ │ │ + } │ │ │ │ + this.map.zoomTo(zoomLevel); │ │ │ │ + this.mouseDragStart = null; │ │ │ │ + this.zoomStart = null; │ │ │ │ + this.deltaY = 0; │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.WFS.v1" │ │ │ │ + /* │ │ │ │ + * Method: moveZoomBar │ │ │ │ + * Change the location of the slider to match the current zoom level. │ │ │ │ + */ │ │ │ │ + moveZoomBar: function() { │ │ │ │ + var newTop = │ │ │ │ + ((this.map.getNumZoomLevels() - 1) - this.map.getZoom()) * │ │ │ │ + this.zoomStopHeight + this.startTop + 1; │ │ │ │ + this.slider.style.top = newTop + "px"; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Control.PanZoomBar" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Protocol/WFS/v1_1_0.js │ │ │ │ + OpenLayers/Control/TransformFeature.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Protocol/WFS/v1.js │ │ │ │ - * @requires OpenLayers/Format/WFST/v1_1_0.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Control/DragFeature.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Geometry/LineString.js │ │ │ │ + * @requires OpenLayers/Geometry/Point.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Protocol.WFS.v1_1_0 │ │ │ │ - * A WFS v1.1.0 protocol for vector layers. Create a new instance with the │ │ │ │ - * <OpenLayers.Protocol.WFS.v1_1_0> constructor. │ │ │ │ + * Class: OpenLayers.Control.TransformFeature │ │ │ │ + * Control to transform features with a standard transformation box. │ │ │ │ * │ │ │ │ - * Differences from the v1.0.0 protocol: │ │ │ │ - * - uses Filter Encoding 1.1.0 instead of 1.0.0 │ │ │ │ - * - uses GML 3 instead of 2 if no format is provided │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Protocol.WFS.v1> │ │ │ │ + * Inherits From: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol.WFS.v1_1_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, { │ │ │ │ +OpenLayers.Control.TransformFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * beforesetfeature - Triggered before a feature is set for │ │ │ │ + * tranformation. The feature will not be set if a listener returns │ │ │ │ + * false. Listeners receive a *feature* property, with the feature │ │ │ │ + * that will be set for transformation. Listeners are allowed to │ │ │ │ + * set the control's *scale*, *ratio* and *rotation* properties, │ │ │ │ + * which will set the initial scale, ratio and rotation of the │ │ │ │ + * feature, like the <setFeature> method's initialParams argument. │ │ │ │ + * setfeature - Triggered when a feature is set for tranformation. │ │ │ │ + * Listeners receive a *feature* property, with the feature that │ │ │ │ + * is now set for transformation. │ │ │ │ + * beforetransform - Triggered while dragging, before a feature is │ │ │ │ + * transformed. The feature will not be transformed if a listener │ │ │ │ + * returns false (but the box still will). Listeners receive one or │ │ │ │ + * more of *center*, *scale*, *ratio* and *rotation*. The *center* │ │ │ │ + * property is an <OpenLayers.Geometry.Point> object with the new │ │ │ │ + * center of the transformed feature, the others are Floats with the │ │ │ │ + * scale, ratio or rotation change since the last transformation. │ │ │ │ + * transform - Triggered while dragging, when a feature is transformed. │ │ │ │ + * Listeners receive an event object with one or more of *center*, │ │ │ │ + * scale*, *ratio* and *rotation*. The *center* property is an │ │ │ │ + * <OpenLayers.Geometry.Point> object with the new center of the │ │ │ │ + * transformed feature, the others are Floats with the scale, ratio │ │ │ │ + * or rotation change of the feature since the last transformation. │ │ │ │ + * transformcomplete - Triggered after dragging. Listeners receive │ │ │ │ + * an event object with the transformed *feature*. │ │ │ │ + */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: version │ │ │ │ - * {String} WFS version number. │ │ │ │ + * APIProperty: geometryTypes │ │ │ │ + * {Array(String)} To restrict transformation to a limited set of geometry │ │ │ │ + * types, send a list of strings corresponding to the geometry class │ │ │ │ + * names. │ │ │ │ */ │ │ │ │ - version: "1.1.0", │ │ │ │ + geometryTypes: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Protocol.WFS.v1_1_0 │ │ │ │ - * A class for giving layers WFS v1.1.0 protocol. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - * │ │ │ │ - * Valid options properties: │ │ │ │ - * featureType - {String} Local (without prefix) feature typeName (required). │ │ │ │ - * featureNS - {String} Feature namespace (optional). │ │ │ │ - * featurePrefix - {String} Feature namespace alias (optional - only used │ │ │ │ - * if featureNS is provided). Default is 'feature'. │ │ │ │ - * geometryName - {String} Name of geometry attribute. Default is 'the_geom'. │ │ │ │ - * outputFormat - {String} Optional output format to use for WFS GetFeature │ │ │ │ - * requests. This can be any format advertized by the WFS's │ │ │ │ - * GetCapabilities response. If set, an appropriate readFormat also │ │ │ │ - * has to be provided, unless outputFormat is GML3, GML2 or JSON. │ │ │ │ - * readFormat - {<OpenLayers.Format>} An appropriate format parser if │ │ │ │ - * outputFormat is none of GML3, GML2 or JSON. │ │ │ │ + * Property: layer │ │ │ │ + * {<OpenLayers.Layer.Vector>} │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Protocol.WFS.v1.prototype.initialize.apply(this, arguments); │ │ │ │ - if (this.outputFormat && !this.readFormat) { │ │ │ │ - if (this.outputFormat.toLowerCase() == "gml2") { │ │ │ │ - this.readFormat = new OpenLayers.Format.GML.v2({ │ │ │ │ - featureType: this.featureType, │ │ │ │ - featureNS: this.featureNS, │ │ │ │ - geometryName: this.geometryName │ │ │ │ - }); │ │ │ │ - } else if (this.outputFormat.toLowerCase() == "json") { │ │ │ │ - this.readFormat = new OpenLayers.Format.GeoJSON(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + layer: null, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.WFS.v1_1_0" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Protocol/WFS/v1_0_0.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * APIProperty: preserveAspectRatio │ │ │ │ + * {Boolean} set to true to not change the feature's aspect ratio. │ │ │ │ + */ │ │ │ │ + preserveAspectRatio: 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. */ │ │ │ │ + /** │ │ │ │ + * APIProperty: rotate │ │ │ │ + * {Boolean} set to false if rotation should be disabled. Default is true. │ │ │ │ + * To be passed with the constructor or set when the control is not │ │ │ │ + * active. │ │ │ │ + */ │ │ │ │ + rotate: true, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Protocol/WFS/v1.js │ │ │ │ - * @requires OpenLayers/Format/WFST/v1_0_0.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * APIProperty: feature │ │ │ │ + * {<OpenLayers.Feature.Vector>} Feature currently available for │ │ │ │ + * transformation. Read-only, use <setFeature> to set it manually. │ │ │ │ + */ │ │ │ │ + feature: null, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Protocol.WFS.v1_0_0 │ │ │ │ - * A WFS v1.0.0 protocol for vector layers. Create a new instance with the │ │ │ │ - * <OpenLayers.Protocol.WFS.v1_0_0> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Protocol.WFS.v1> │ │ │ │ - */ │ │ │ │ -OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, { │ │ │ │ + /** │ │ │ │ + * APIProperty: renderIntent │ │ │ │ + * {String|Object} Render intent for the transformation box and │ │ │ │ + * handles. A symbolizer object can also be provided here. │ │ │ │ + */ │ │ │ │ + renderIntent: "temporary", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: version │ │ │ │ - * {String} WFS version number. │ │ │ │ + * APIProperty: rotationHandleSymbolizer │ │ │ │ + * {Object|String} Optional. A custom symbolizer for the rotation handles. │ │ │ │ + * A render intent can also be provided here. Defaults to │ │ │ │ + * (code) │ │ │ │ + * { │ │ │ │ + * stroke: false, │ │ │ │ + * pointRadius: 10, │ │ │ │ + * fillOpacity: 0, │ │ │ │ + * cursor: "pointer" │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ */ │ │ │ │ - version: "1.0.0", │ │ │ │ + rotationHandleSymbolizer: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Protocol.WFS.v1_0_0 │ │ │ │ - * A class for giving layers WFS v1.0.0 protocol. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - * │ │ │ │ - * Valid options properties: │ │ │ │ - * featureType - {String} Local (without prefix) feature typeName (required). │ │ │ │ - * featureNS - {String} Feature namespace (optional). │ │ │ │ - * featurePrefix - {String} Feature namespace alias (optional - only used │ │ │ │ - * if featureNS is provided). Default is 'feature'. │ │ │ │ - * geometryName - {String} Name of geometry attribute. Default is 'the_geom'. │ │ │ │ + * APIProperty: box │ │ │ │ + * {<OpenLayers.Feature.Vector>} The transformation box rectangle. │ │ │ │ + * Read-only. │ │ │ │ */ │ │ │ │ + box: null, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.WFS.v1_0_0" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Events/featureclick.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * APIProperty: center │ │ │ │ + * {<OpenLayers.Geometry.Point>} The center of the feature bounds. │ │ │ │ + * Read-only. │ │ │ │ + */ │ │ │ │ + center: 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: scale │ │ │ │ + * {Float} The scale of the feature, relative to the scale the time the │ │ │ │ + * feature was set. Read-only, except for *beforesetfeature* │ │ │ │ + * listeners. │ │ │ │ + */ │ │ │ │ + scale: 1, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * APIProperty: ratio │ │ │ │ + * {Float} The ratio of the feature relative to the ratio the time the │ │ │ │ + * feature was set. Read-only, except for *beforesetfeature* │ │ │ │ + * listeners. │ │ │ │ + */ │ │ │ │ + ratio: 1, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Events.featureclick │ │ │ │ - * │ │ │ │ - * Extension event type for handling feature click events, including overlapping │ │ │ │ - * features. │ │ │ │ - * │ │ │ │ - * Event types provided by this extension: │ │ │ │ - * - featureclick │ │ │ │ - */ │ │ │ │ -OpenLayers.Events.featureclick = OpenLayers.Class({ │ │ │ │ + /** │ │ │ │ + * Property: rotation │ │ │ │ + * {Integer} the current rotation angle of the box. Read-only, except for │ │ │ │ + * *beforesetfeature* listeners. │ │ │ │ + */ │ │ │ │ + rotation: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: cache │ │ │ │ - * {Object} A cache of features under the mouse. │ │ │ │ + * APIProperty: handles │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} The 8 handles currently available │ │ │ │ + * for scaling/resizing. Numbered counterclockwise, starting from the │ │ │ │ + * southwest corner. Read-only. │ │ │ │ */ │ │ │ │ - cache: null, │ │ │ │ + handles: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: map │ │ │ │ - * {<OpenLayers.Map>} The map to register browser events on. │ │ │ │ + * APIProperty: rotationHandles │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} The 4 rotation handles currently │ │ │ │ + * available for rotating. Numbered counterclockwise, starting from │ │ │ │ + * the southwest corner. Read-only. │ │ │ │ */ │ │ │ │ - map: null, │ │ │ │ + rotationHandles: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: provides │ │ │ │ - * {Array(String)} The event types provided by this extension. │ │ │ │ + * Property: dragControl │ │ │ │ + * {<OpenLayers.Control.DragFeature>} │ │ │ │ */ │ │ │ │ - provides: ["featureclick", "nofeatureclick", "featureover", "featureout"], │ │ │ │ + dragControl: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Events.featureclick │ │ │ │ - * Create a new featureclick event type. │ │ │ │ + * APIProperty: irregular │ │ │ │ + * {Boolean} Make scaling/resizing work irregularly. If true then │ │ │ │ + * dragging a handle causes the feature to resize in the direction │ │ │ │ + * of movement. If false then the feature resizes symetrically │ │ │ │ + * about it's center. │ │ │ │ + */ │ │ │ │ + irregular: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.TransformFeature │ │ │ │ + * Create a new transform feature control. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * target - {<OpenLayers.Events>} The events instance to create the events │ │ │ │ - * for. │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} Layer that contains features that │ │ │ │ + * will be transformed. │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * control. │ │ │ │ */ │ │ │ │ - initialize: function(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; │ │ │ │ + initialize: function(layer, options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + │ │ │ │ + this.layer = layer; │ │ │ │ + │ │ │ │ + if (!this.rotationHandleSymbolizer) { │ │ │ │ + this.rotationHandleSymbolizer = { │ │ │ │ + stroke: false, │ │ │ │ + pointRadius: 10, │ │ │ │ + fillOpacity: 0, │ │ │ │ + cursor: "pointer" │ │ │ │ + }; │ │ │ │ } │ │ │ │ + │ │ │ │ + this.createBox(); │ │ │ │ + this.createControl(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setMap │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} The map to register browser events on. │ │ │ │ + * APIMethod: activate │ │ │ │ + * Activates the control. │ │ │ │ */ │ │ │ │ - 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 │ │ │ │ - }); │ │ │ │ + activate: function() { │ │ │ │ + var activated = false; │ │ │ │ + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.dragControl.activate(); │ │ │ │ + this.layer.addFeatures([this.box]); │ │ │ │ + this.rotate && this.layer.addFeatures(this.rotationHandles); │ │ │ │ + this.layer.addFeatures(this.handles); │ │ │ │ + activated = true; │ │ │ │ + } │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: start │ │ │ │ - * Sets startEvt = evt. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivates the control. │ │ │ │ */ │ │ │ │ - start: function(evt) { │ │ │ │ - this.startEvt = evt; │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.layer.removeFeatures(this.handles); │ │ │ │ + this.rotate && this.layer.removeFeatures(this.rotationHandles); │ │ │ │ + this.layer.removeFeatures([this.box]); │ │ │ │ + this.dragControl.deactivate(); │ │ │ │ + deactivated = true; │ │ │ │ + } │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: cancel │ │ │ │ - * Deletes the start event. │ │ │ │ - * │ │ │ │ + * Method: setMap │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - cancel: function(evt) { │ │ │ │ - delete this.startEvt; │ │ │ │ + setMap: function(map) { │ │ │ │ + this.dragControl.setMap(map); │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onClick │ │ │ │ - * Listener for the click event. │ │ │ │ - * │ │ │ │ + * APIMethod: setFeature │ │ │ │ + * Place the transformation box on a feature and start transforming it. │ │ │ │ + * If the control is not active, it will be activated. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * initialParams - {Object} Initial values for rotation, scale or ratio. │ │ │ │ + * Setting a rotation value here will cause the transformation box to │ │ │ │ + * start rotated. Setting a scale or ratio will not affect the │ │ │ │ + * transormation box, but applications may use this to keep track of │ │ │ │ + * scale and ratio of a feature across multiple transforms. │ │ │ │ */ │ │ │ │ - onClick: function(evt) { │ │ │ │ - if (!this.startEvt || evt.type !== "touchend" && │ │ │ │ - !OpenLayers.Event.isLeftClick(evt)) { │ │ │ │ + setFeature: function(feature, initialParams) { │ │ │ │ + initialParams = OpenLayers.Util.applyDefaults(initialParams, { │ │ │ │ + rotation: 0, │ │ │ │ + scale: 1, │ │ │ │ + ratio: 1 │ │ │ │ + }); │ │ │ │ + │ │ │ │ + var oldRotation = this.rotation; │ │ │ │ + var oldCenter = this.center; │ │ │ │ + OpenLayers.Util.extend(this, initialParams); │ │ │ │ + │ │ │ │ + var cont = this.events.triggerEvent("beforesetfeature", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + if (cont === false) { │ │ │ │ return; │ │ │ │ } │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ + │ │ │ │ + this.feature = feature; │ │ │ │ + this.activate(); │ │ │ │ + │ │ │ │ + this._setfeature = true; │ │ │ │ + │ │ │ │ + var featureBounds = this.feature.geometry.getBounds(); │ │ │ │ + this.box.move(featureBounds.getCenterLonLat()); │ │ │ │ + this.box.geometry.rotate(-oldRotation, oldCenter); │ │ │ │ + this._angle = 0; │ │ │ │ + │ │ │ │ + var ll; │ │ │ │ + if (this.rotation) { │ │ │ │ + var geom = feature.geometry.clone(); │ │ │ │ + geom.rotate(-this.rotation, this.center); │ │ │ │ + var box = new OpenLayers.Feature.Vector( │ │ │ │ + geom.getBounds().toGeometry()); │ │ │ │ + box.geometry.rotate(this.rotation, this.center); │ │ │ │ + this.box.geometry.rotate(this.rotation, this.center); │ │ │ │ + this.box.move(box.geometry.getBounds().getCenterLonLat()); │ │ │ │ + var llGeom = box.geometry.components[0].components[0]; │ │ │ │ + ll = llGeom.getBounds().getCenterLonLat(); │ │ │ │ + } else { │ │ │ │ + ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom); │ │ │ │ } │ │ │ │ - // 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 │ │ │ │ - }); │ │ │ │ - } │ │ │ │ + this.handles[0].move(ll); │ │ │ │ + │ │ │ │ + delete this._setfeature; │ │ │ │ + │ │ │ │ + this.events.triggerEvent("setfeature", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: unsetFeature │ │ │ │ + * Remove the transformation box off any feature. │ │ │ │ + * If the control is active, it will be deactivated first. │ │ │ │ + */ │ │ │ │ + unsetFeature: function() { │ │ │ │ + if (this.active) { │ │ │ │ + this.deactivate(); │ │ │ │ + } else { │ │ │ │ + this.feature = null; │ │ │ │ + this.rotation = 0; │ │ │ │ + this.scale = 1; │ │ │ │ + this.ratio = 1; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onMousemove │ │ │ │ - * Listener for the mousemove event. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * Method: createBox │ │ │ │ + * Creates the box with all handles and transformation handles. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + createBox: function() { │ │ │ │ + var control = this; │ │ │ │ + │ │ │ │ + this.center = new OpenLayers.Geometry.Point(0, 0); │ │ │ │ + this.box = new OpenLayers.Feature.Vector( │ │ │ │ + new OpenLayers.Geometry.LineString([ │ │ │ │ + new OpenLayers.Geometry.Point(-1, -1), │ │ │ │ + new OpenLayers.Geometry.Point(0, -1), │ │ │ │ + new OpenLayers.Geometry.Point(1, -1), │ │ │ │ + new OpenLayers.Geometry.Point(1, 0), │ │ │ │ + new OpenLayers.Geometry.Point(1, 1), │ │ │ │ + new OpenLayers.Geometry.Point(0, 1), │ │ │ │ + new OpenLayers.Geometry.Point(-1, 1), │ │ │ │ + new OpenLayers.Geometry.Point(-1, 0), │ │ │ │ + new OpenLayers.Geometry.Point(-1, -1) │ │ │ │ + ]), null, │ │ │ │ + typeof this.renderIntent == "string" ? null : this.renderIntent │ │ │ │ + ); │ │ │ │ + │ │ │ │ + // Override for box move - make sure that the center gets updated │ │ │ │ + this.box.geometry.move = function(x, y) { │ │ │ │ + control._moving = true; │ │ │ │ + OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments); │ │ │ │ + control.center.move(x, y); │ │ │ │ + delete control._moving; │ │ │ │ + }; │ │ │ │ + │ │ │ │ + // Overrides for vertex move, resize and rotate - make sure that │ │ │ │ + // handle and rotationHandle geometries are also moved, resized and │ │ │ │ + // rotated. │ │ │ │ + var vertexMoveFn = function(x, y) { │ │ │ │ + OpenLayers.Geometry.Point.prototype.move.apply(this, arguments); │ │ │ │ + this._rotationHandle && this._rotationHandle.geometry.move(x, y); │ │ │ │ + this._handle.geometry.move(x, y); │ │ │ │ + }; │ │ │ │ + var vertexResizeFn = function(scale, center, ratio) { │ │ │ │ + OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments); │ │ │ │ + this._rotationHandle && this._rotationHandle.geometry.resize( │ │ │ │ + scale, center, ratio); │ │ │ │ + this._handle.geometry.resize(scale, center, ratio); │ │ │ │ + }; │ │ │ │ + var vertexRotateFn = function(angle, center) { │ │ │ │ + OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments); │ │ │ │ + this._rotationHandle && this._rotationHandle.geometry.rotate( │ │ │ │ + angle, center); │ │ │ │ + this._handle.geometry.rotate(angle, center); │ │ │ │ + }; │ │ │ │ + │ │ │ │ + // Override for handle move - make sure that the box and other handles │ │ │ │ + // are updated, and finally transform the feature. │ │ │ │ + var handleMoveFn = function(x, y) { │ │ │ │ + var oldX = this.x, │ │ │ │ + oldY = this.y; │ │ │ │ + OpenLayers.Geometry.Point.prototype.move.call(this, x, y); │ │ │ │ + if (control._moving) { │ │ │ │ + return; │ │ │ │ } │ │ │ │ - } │ │ │ │ - // 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); │ │ │ │ - } │ │ │ │ + var evt = control.dragControl.handlers.drag.evt; │ │ │ │ + var preserveAspectRatio = !control._setfeature && │ │ │ │ + control.preserveAspectRatio; │ │ │ │ + var reshape = !preserveAspectRatio && !(evt && evt.shiftKey); │ │ │ │ + var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY); │ │ │ │ + var centerGeometry = control.center; │ │ │ │ + this.rotate(-control.rotation, centerGeometry); │ │ │ │ + oldGeom.rotate(-control.rotation, centerGeometry); │ │ │ │ + var dx1 = this.x - centerGeometry.x; │ │ │ │ + var dy1 = this.y - centerGeometry.y; │ │ │ │ + var dx0 = dx1 - (this.x - oldGeom.x); │ │ │ │ + var dy0 = dy1 - (this.y - oldGeom.y); │ │ │ │ + if (control.irregular && !control._setfeature) { │ │ │ │ + dx1 -= (this.x - oldGeom.x) / 2; │ │ │ │ + dy1 -= (this.y - oldGeom.y) / 2; │ │ │ │ + } │ │ │ │ + this.x = oldX; │ │ │ │ + this.y = oldY; │ │ │ │ + var scale, ratio = 1; │ │ │ │ + if (reshape) { │ │ │ │ + scale = Math.abs(dy0) < 0.00001 ? 1 : dy1 / dy0; │ │ │ │ + ratio = (Math.abs(dx0) < 0.00001 ? 1 : (dx1 / dx0)) / scale; │ │ │ │ } else { │ │ │ │ - // removed │ │ │ │ - delete this.cache[id]; │ │ │ │ + var l0 = Math.sqrt((dx0 * dx0) + (dy0 * dy0)); │ │ │ │ + var l1 = Math.sqrt((dx1 * dx1) + (dy1 * dy1)); │ │ │ │ + scale = l1 / l0; │ │ │ │ } │ │ │ │ - } │ │ │ │ - // 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 │ │ │ │ + │ │ │ │ + // rotate the box to 0 before resizing - saves us some │ │ │ │ + // calculations and is inexpensive because we don't drawFeature. │ │ │ │ + control._moving = true; │ │ │ │ + control.box.geometry.rotate(-control.rotation, centerGeometry); │ │ │ │ + delete control._moving; │ │ │ │ + │ │ │ │ + control.box.geometry.resize(scale, centerGeometry, ratio); │ │ │ │ + control.box.geometry.rotate(control.rotation, centerGeometry); │ │ │ │ + control.transformFeature({ │ │ │ │ + scale: scale, │ │ │ │ + ratio: ratio │ │ │ │ }); │ │ │ │ - if (more === false) { │ │ │ │ - break; │ │ │ │ + if (control.irregular && !control._setfeature) { │ │ │ │ + var newCenter = centerGeometry.clone(); │ │ │ │ + newCenter.x += Math.abs(oldX - centerGeometry.x) < 0.00001 ? 0 : (this.x - oldX); │ │ │ │ + newCenter.y += Math.abs(oldY - centerGeometry.y) < 0.00001 ? 0 : (this.y - oldY); │ │ │ │ + control.box.geometry.move(this.x - oldX, this.y - oldY); │ │ │ │ + control.transformFeature({ │ │ │ │ + center: newCenter │ │ │ │ + }); │ │ │ │ } │ │ │ │ - } │ │ │ │ - // 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; │ │ │ │ + }; │ │ │ │ + │ │ │ │ + // Override for rotation handle move - make sure that the box and │ │ │ │ + // other handles are updated, and finally transform the feature. │ │ │ │ + var rotationHandleMoveFn = function(x, y) { │ │ │ │ + var oldX = this.x, │ │ │ │ + oldY = this.y; │ │ │ │ + OpenLayers.Geometry.Point.prototype.move.call(this, x, y); │ │ │ │ + if (control._moving) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + var evt = control.dragControl.handlers.drag.evt; │ │ │ │ + var constrain = (evt && evt.shiftKey) ? 45 : 1; │ │ │ │ + var centerGeometry = control.center; │ │ │ │ + var dx1 = this.x - centerGeometry.x; │ │ │ │ + var dy1 = this.y - centerGeometry.y; │ │ │ │ + var dx0 = dx1 - x; │ │ │ │ + var dy0 = dy1 - y; │ │ │ │ + this.x = oldX; │ │ │ │ + this.y = oldY; │ │ │ │ + var a0 = Math.atan2(dy0, dx0); │ │ │ │ + var a1 = Math.atan2(dy1, dx1); │ │ │ │ + var angle = a1 - a0; │ │ │ │ + angle *= 180 / Math.PI; │ │ │ │ + control._angle = (control._angle + angle) % 360; │ │ │ │ + var diff = control.rotation % constrain; │ │ │ │ + if (Math.abs(control._angle) >= constrain || diff !== 0) { │ │ │ │ + angle = Math.round(control._angle / constrain) * constrain - │ │ │ │ + diff; │ │ │ │ + control._angle = 0; │ │ │ │ + control.box.geometry.rotate(angle, centerGeometry); │ │ │ │ + control.transformFeature({ │ │ │ │ + rotation: angle │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }; │ │ │ │ + │ │ │ │ + var handles = new Array(8); │ │ │ │ + var rotationHandles = new Array(4); │ │ │ │ + var geom, handle, rotationHandle; │ │ │ │ + var positions = ["sw", "s", "se", "e", "ne", "n", "nw", "w"]; │ │ │ │ + for (var i = 0; i < 8; ++i) { │ │ │ │ + geom = this.box.geometry.components[i]; │ │ │ │ + handle = new OpenLayers.Feature.Vector(geom.clone(), { │ │ │ │ + role: positions[i] + "-resize" │ │ │ │ + }, typeof this.renderIntent == "string" ? null : │ │ │ │ + this.renderIntent); │ │ │ │ + if (i % 2 == 0) { │ │ │ │ + rotationHandle = new OpenLayers.Feature.Vector(geom.clone(), { │ │ │ │ + role: positions[i] + "-rotate" │ │ │ │ + }, typeof this.rotationHandleSymbolizer == "string" ? │ │ │ │ + null : this.rotationHandleSymbolizer); │ │ │ │ + rotationHandle.geometry.move = rotationHandleMoveFn; │ │ │ │ + geom._rotationHandle = rotationHandle; │ │ │ │ + rotationHandles[i / 2] = rotationHandle; │ │ │ │ } │ │ │ │ + geom.move = vertexMoveFn; │ │ │ │ + geom.resize = vertexResizeFn; │ │ │ │ + geom.rotate = vertexRotateFn; │ │ │ │ + handle.geometry.move = handleMoveFn; │ │ │ │ + geom._handle = handle; │ │ │ │ + handles[i] = handle; │ │ │ │ } │ │ │ │ + │ │ │ │ + this.rotationHandles = rotationHandles; │ │ │ │ + this.handles = handles; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: triggerEvent │ │ │ │ - * Determines where to trigger the event and triggers it. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * type - {String} The event type to trigger │ │ │ │ - * evt - {Object} The listener argument │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The last listener return. │ │ │ │ + * Method: createControl │ │ │ │ + * Creates a DragFeature control for this control. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + createControl: function() { │ │ │ │ + var control = this; │ │ │ │ + this.dragControl = new OpenLayers.Control.DragFeature(this.layer, { │ │ │ │ + documentDrag: true, │ │ │ │ + // avoid moving the feature itself - move the box instead │ │ │ │ + moveFeature: function(pixel) { │ │ │ │ + if (this.feature === control.feature) { │ │ │ │ + this.feature = control.box; │ │ │ │ + } │ │ │ │ + OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this, │ │ │ │ + arguments); │ │ │ │ + }, │ │ │ │ + // transform while dragging │ │ │ │ + onDrag: function(feature, pixel) { │ │ │ │ + if (feature === control.box) { │ │ │ │ + control.transformFeature({ │ │ │ │ + center: control.center │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + // set a new feature │ │ │ │ + onStart: function(feature, pixel) { │ │ │ │ + var eligible = !control.geometryTypes || │ │ │ │ + OpenLayers.Util.indexOf(control.geometryTypes, │ │ │ │ + feature.geometry.CLASS_NAME) !== -1; │ │ │ │ + var i = OpenLayers.Util.indexOf(control.handles, feature); │ │ │ │ + i += OpenLayers.Util.indexOf(control.rotationHandles, │ │ │ │ + feature); │ │ │ │ + if (feature !== control.feature && feature !== control.box && │ │ │ │ + i == -2 && eligible) { │ │ │ │ + control.setFeature(feature); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + onComplete: function(feature, pixel) { │ │ │ │ + control.events.triggerEvent("transformcomplete", { │ │ │ │ + feature: control.feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: drawHandles │ │ │ │ + * Draws the handles to match the box. │ │ │ │ + */ │ │ │ │ + drawHandles: function() { │ │ │ │ + var layer = this.layer; │ │ │ │ + for (var i = 0; i < 8; ++i) { │ │ │ │ + if (this.rotate && i % 2 === 0) { │ │ │ │ + layer.drawFeature(this.rotationHandles[i / 2], │ │ │ │ + this.rotationHandleSymbolizer); │ │ │ │ + } │ │ │ │ + layer.drawFeature(this.handles[i], this.renderIntent); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getFeatures │ │ │ │ - * Get all features at the given screen location. │ │ │ │ - * │ │ │ │ + * Method: transformFeature │ │ │ │ + * Transforms the feature. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} Event object. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} List of features at the given point. │ │ │ │ + * mods - {Object} An object with optional scale, ratio, rotation and │ │ │ │ + * center properties. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + transformFeature: function(mods) { │ │ │ │ + if (!this._setfeature) { │ │ │ │ + this.scale *= (mods.scale || 1); │ │ │ │ + this.ratio *= (mods.ratio || 1); │ │ │ │ + var oldRotation = this.rotation; │ │ │ │ + this.rotation = (this.rotation + (mods.rotation || 0)) % 360; │ │ │ │ + │ │ │ │ + if (this.events.triggerEvent("beforetransform", mods) !== false) { │ │ │ │ + var feature = this.feature; │ │ │ │ + var geom = feature.geometry; │ │ │ │ + var center = this.center; │ │ │ │ + geom.rotate(-oldRotation, center); │ │ │ │ + if (mods.scale || mods.ratio) { │ │ │ │ + geom.resize(mods.scale, center, mods.ratio); │ │ │ │ + } else if (mods.center) { │ │ │ │ + feature.move(mods.center.getBounds().getCenterLonLat()); │ │ │ │ } │ │ │ │ + geom.rotate(this.rotation, center); │ │ │ │ + this.layer.drawFeature(feature); │ │ │ │ + feature.toState(OpenLayers.State.UPDATE); │ │ │ │ + this.events.triggerEvent("transform", mods); │ │ │ │ } │ │ │ │ } │ │ │ │ - // restore feature visibility │ │ │ │ - for (i = 0, len = targets.length; i < len; ++i) { │ │ │ │ - targets[i].style.display = ""; │ │ │ │ - } │ │ │ │ - // restore layer visibility │ │ │ │ - for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ - layers[i].div.style.display = "block"; │ │ │ │ - } │ │ │ │ - return features; │ │ │ │ + this.layer.drawFeature(this.box, this.renderIntent); │ │ │ │ + this.drawHandles(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ * APIMethod: destroy │ │ │ │ - * Clean up. │ │ │ │ + * Take care of things that are not handled in superclass. │ │ │ │ */ │ │ │ │ destroy: function() { │ │ │ │ - for (var i = this.provides.length - 1; i >= 0; --i) { │ │ │ │ - delete this.target.extensions[this.provides[i]]; │ │ │ │ + var geom; │ │ │ │ + for (var i = 0; i < 8; ++i) { │ │ │ │ + geom = this.box.geometry.components[i]; │ │ │ │ + geom._handle.destroy(); │ │ │ │ + geom._handle = null; │ │ │ │ + geom._rotationHandle && geom._rotationHandle.destroy(); │ │ │ │ + geom._rotationHandle = null; │ │ │ │ } │ │ │ │ - this.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; │ │ │ │ - } │ │ │ │ + this.center = null; │ │ │ │ + this.feature = null; │ │ │ │ + this.handles = null; │ │ │ │ + this.rotationHandleSymbolizer = null; │ │ │ │ + this.rotationHandles = null; │ │ │ │ + this.box.destroy(); │ │ │ │ + this.box = null; │ │ │ │ + this.layer = null; │ │ │ │ + this.dragControl.destroy(); │ │ │ │ + this.dragControl = null; │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Control.TransformFeature" │ │ │ │ }); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Events.nofeatureclick │ │ │ │ - * │ │ │ │ - * Extension event type for handling click events that do not hit a feature. │ │ │ │ - * │ │ │ │ - * Event types provided by this extension: │ │ │ │ - * - nofeatureclick │ │ │ │ - */ │ │ │ │ -OpenLayers.Events.nofeatureclick = OpenLayers.Events.featureclick; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Events.featureover │ │ │ │ - * │ │ │ │ - * Extension event type for handling hovering over a feature. │ │ │ │ - * │ │ │ │ - * Event types provided by this extension: │ │ │ │ - * - featureover │ │ │ │ - */ │ │ │ │ -OpenLayers.Events.featureover = OpenLayers.Events.featureclick; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Events.featureout │ │ │ │ - * │ │ │ │ - * Extension event type for handling leaving a feature. │ │ │ │ - * │ │ │ │ - * Event types provided by this extension: │ │ │ │ - * - featureout │ │ │ │ - */ │ │ │ │ -OpenLayers.Events.featureout = OpenLayers.Events.featureclick; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Popup/Anchored.js │ │ │ │ + OpenLayers/Control/WMTSGetFeatureInfo.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Popup.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Handler/Click.js │ │ │ │ + * @requires OpenLayers/Handler/Hover.js │ │ │ │ + * @requires OpenLayers/Request.js │ │ │ │ + * @requires OpenLayers/Format/WMSGetFeatureInfo.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Popup.Anchored │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Control.WMTSGetFeatureInfo │ │ │ │ + * The WMTSGetFeatureInfo control uses a WMTS query to get information about a │ │ │ │ + * point on the map. The information may be in a display-friendly format │ │ │ │ + * such as HTML, or a machine-friendly format such as GML, depending on the │ │ │ │ + * server's capabilities and the client's configuration. This control │ │ │ │ + * handles click or hover events, attempts to parse the results using an │ │ │ │ + * OpenLayers.Format, and fires a 'getfeatureinfo' event for each layer │ │ │ │ + * queried. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Popup> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Popup.Anchored = │ │ │ │ - OpenLayers.Class(OpenLayers.Popup, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: relativePosition │ │ │ │ - * {String} Relative position of the popup ("br", "tr", "tl" or "bl"). │ │ │ │ - */ │ │ │ │ - relativePosition: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: keepInMap │ │ │ │ - * {Boolean} If panMapIfOutOfView is false, and this property is true, │ │ │ │ - * contrain the popup such that it always fits in the available map │ │ │ │ - * space. By default, this is set. If you are creating popups that are │ │ │ │ - * near map edges and not allowing pannning, and especially if you have │ │ │ │ - * a popup which has a fixedRelativePosition, setting this to false may │ │ │ │ - * be a smart thing to do. │ │ │ │ - * │ │ │ │ - * For anchored popups, default is true, since subclasses will │ │ │ │ - * usually want this functionality. │ │ │ │ - */ │ │ │ │ - keepInMap: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: anchor │ │ │ │ - * {Object} Object to which we'll anchor the popup. Must expose a │ │ │ │ - * 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>). │ │ │ │ - */ │ │ │ │ - anchor: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Popup.Anchored │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * id - {String} │ │ │ │ - * lonlat - {<OpenLayers.LonLat>} │ │ │ │ - * contentSize - {<OpenLayers.Size>} │ │ │ │ - * contentHTML - {String} │ │ │ │ - * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size> │ │ │ │ - * and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>). │ │ │ │ - * closeBox - {Boolean} │ │ │ │ - * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ - */ │ │ │ │ - initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, │ │ │ │ - closeBoxCallback) { │ │ │ │ - var newArguments = [ │ │ │ │ - id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback │ │ │ │ - ]; │ │ │ │ - OpenLayers.Popup.prototype.initialize.apply(this, newArguments); │ │ │ │ - │ │ │ │ - this.anchor = (anchor != null) ? anchor : │ │ │ │ - { │ │ │ │ - size: new OpenLayers.Size(0, 0), │ │ │ │ - offset: new OpenLayers.Pixel(0, 0) │ │ │ │ - }; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - this.anchor = null; │ │ │ │ - this.relativePosition = null; │ │ │ │ - │ │ │ │ - OpenLayers.Popup.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: show │ │ │ │ - * Overridden from Popup since user might hide popup and then show() it │ │ │ │ - * in a new location (meaning we might want to update the relative │ │ │ │ - * position on the show) │ │ │ │ - */ │ │ │ │ - show: function() { │ │ │ │ - this.updatePosition(); │ │ │ │ - OpenLayers.Popup.prototype.show.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * Since the popup is moving to a new px, it might need also to be moved │ │ │ │ - * relative to where the marker is. We first calculate the new │ │ │ │ - * relativePosition, and then we calculate the new px where we will │ │ │ │ - * put the popup, based on the new relative position. │ │ │ │ - * │ │ │ │ - * If the relativePosition has changed, we must also call │ │ │ │ - * updateRelativePosition() to make any visual changes to the popup │ │ │ │ - * which are associated with putting it in a new relativePosition. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ - */ │ │ │ │ - moveTo: function(px) { │ │ │ │ - var oldRelativePosition = this.relativePosition; │ │ │ │ - this.relativePosition = this.calculateRelativePosition(px); │ │ │ │ - │ │ │ │ - OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px)); │ │ │ │ - │ │ │ │ - //if this move has caused the popup to change its relative position, │ │ │ │ - // we need to make the appropriate cosmetic changes. │ │ │ │ - if (this.relativePosition != oldRelativePosition) { │ │ │ │ - this.updateRelativePosition(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setSize │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * contentSize - {<OpenLayers.Size>} the new size for the popup's │ │ │ │ - * contents div (in pixels). │ │ │ │ - */ │ │ │ │ - setSize: function(contentSize) { │ │ │ │ - OpenLayers.Popup.prototype.setSize.apply(this, arguments); │ │ │ │ +OpenLayers.Control.WMTSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - if ((this.lonlat) && (this.map)) { │ │ │ │ - var px = this.map.getLayerPxFromLonLat(this.lonlat); │ │ │ │ - this.moveTo(px); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: hover │ │ │ │ + * {Boolean} Send GetFeatureInfo requests when mouse stops moving. │ │ │ │ + * Default is false. │ │ │ │ + */ │ │ │ │ + hover: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: calculateRelativePosition │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The relative position ("br" "tr" "tl" "bl") at which the popup │ │ │ │ - * should be placed. │ │ │ │ - */ │ │ │ │ - calculateRelativePosition: function(px) { │ │ │ │ - var lonlat = this.map.getLonLatFromLayerPx(px); │ │ │ │ + /** │ │ │ │ + * Property: requestEncoding │ │ │ │ + * {String} One of "KVP" or "REST". Only KVP encoding is supported at this │ │ │ │ + * time. │ │ │ │ + */ │ │ │ │ + requestEncoding: "KVP", │ │ │ │ │ │ │ │ - var extent = this.map.getExtent(); │ │ │ │ - var quadrant = extent.determineQuadrant(lonlat); │ │ │ │ + /** │ │ │ │ + * APIProperty: drillDown │ │ │ │ + * {Boolean} Drill down over all WMTS layers in the map. When │ │ │ │ + * using drillDown mode, hover is not possible. A getfeatureinfo event │ │ │ │ + * will be fired for each layer queried. │ │ │ │ + */ │ │ │ │ + drillDown: false, │ │ │ │ │ │ │ │ - return OpenLayers.Bounds.oppositeQuadrant(quadrant); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: maxFeatures │ │ │ │ + * {Integer} Maximum number of features to return from a WMTS query. This │ │ │ │ + * sets the feature_count parameter on WMTS GetFeatureInfo │ │ │ │ + * requests. │ │ │ │ + */ │ │ │ │ + maxFeatures: 10, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: updateRelativePosition │ │ │ │ - * The popup has been moved to a new relative location, so we may want to │ │ │ │ - * make some cosmetic adjustments to it. │ │ │ │ - * │ │ │ │ - * Note that in the classic Anchored popup, there is nothing to do │ │ │ │ - * here, since the popup looks exactly the same in all four positions. │ │ │ │ - * Subclasses such as Framed, however, will want to do something │ │ │ │ - * special here. │ │ │ │ - */ │ │ │ │ - updateRelativePosition: function() { │ │ │ │ - //to be overridden by subclasses │ │ │ │ - }, │ │ │ │ + /** APIProperty: clickCallback │ │ │ │ + * {String} The click callback to register in the │ │ │ │ + * {<OpenLayers.Handler.Click>} object created when the hover │ │ │ │ + * option is set to false. Default is "click". │ │ │ │ + */ │ │ │ │ + clickCallback: "click", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: calculateNewPx │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} The the new px position of the popup on the screen │ │ │ │ - * relative to the passed-in px. │ │ │ │ - */ │ │ │ │ - calculateNewPx: function(px) { │ │ │ │ - var newPx = px.offset(this.anchor.offset); │ │ │ │ + /** │ │ │ │ + * Property: layers │ │ │ │ + * {Array(<OpenLayers.Layer.WMTS>)} The layers to query for feature info. │ │ │ │ + * If omitted, all map WMTS layers will be considered. │ │ │ │ + */ │ │ │ │ + layers: null, │ │ │ │ │ │ │ │ - //use contentSize if size is not already set │ │ │ │ - var size = this.size || this.contentSize; │ │ │ │ + /** │ │ │ │ + * APIProperty: queryVisible │ │ │ │ + * {Boolean} Filter out hidden layers when searching the map for layers to │ │ │ │ + * query. Default is true. │ │ │ │ + */ │ │ │ │ + queryVisible: true, │ │ │ │ │ │ │ │ - var top = (this.relativePosition.charAt(0) == 't'); │ │ │ │ - newPx.y += (top) ? -size.h : this.anchor.size.h; │ │ │ │ + /** │ │ │ │ + * Property: infoFormat │ │ │ │ + * {String} The mimetype to request from the server │ │ │ │ + */ │ │ │ │ + infoFormat: 'text/html', │ │ │ │ │ │ │ │ - var left = (this.relativePosition.charAt(1) == 'l'); │ │ │ │ - newPx.x += (left) ? -size.w : this.anchor.size.w; │ │ │ │ + /** │ │ │ │ + * Property: vendorParams │ │ │ │ + * {Object} Additional parameters that will be added to the request, for │ │ │ │ + * WMTS implementations that support them. This could e.g. look like │ │ │ │ + * (start code) │ │ │ │ + * { │ │ │ │ + * radius: 5 │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ + */ │ │ │ │ + vendorParams: {}, │ │ │ │ │ │ │ │ - return newPx; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: format │ │ │ │ + * {<OpenLayers.Format>} A format for parsing GetFeatureInfo responses. │ │ │ │ + * Default is <OpenLayers.Format.WMSGetFeatureInfo>. │ │ │ │ + */ │ │ │ │ + format: null, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Popup.Anchored" │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Popup/Framed.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * Property: formatOptions │ │ │ │ + * {Object} Optional properties to set on the format (if one is not provided │ │ │ │ + * in the <format> property. │ │ │ │ + */ │ │ │ │ + formatOptions: 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: handlerOptions │ │ │ │ + * {Object} Additional options for the handlers used by this control, e.g. │ │ │ │ + * (start code) │ │ │ │ + * { │ │ │ │ + * "click": {delay: 100}, │ │ │ │ + * "hover": {delay: 300} │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ + */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Popup/Anchored.js │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Property: handler │ │ │ │ + * {Object} Reference to the <OpenLayers.Handler> for this control │ │ │ │ + */ │ │ │ │ + handler: null, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Popup.Framed │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Popup.Anchored> │ │ │ │ - */ │ │ │ │ -OpenLayers.Popup.Framed = │ │ │ │ - OpenLayers.Class(OpenLayers.Popup.Anchored, { │ │ │ │ + /** │ │ │ │ + * Property: hoverRequest │ │ │ │ + * {<OpenLayers.Request>} contains the currently running hover request │ │ │ │ + * (if any). │ │ │ │ + */ │ │ │ │ + hoverRequest: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: imageSrc │ │ │ │ - * {String} location of the image to be used as the popup frame │ │ │ │ - */ │ │ │ │ - imageSrc: null, │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * beforegetfeatureinfo - Triggered before each request is sent. │ │ │ │ + * The event object has an *xy* property with the position of the │ │ │ │ + * mouse click or hover event that triggers the request and a *layer* │ │ │ │ + * property referencing the layer about to be queried. If a listener │ │ │ │ + * returns false, the request will not be issued. │ │ │ │ + * getfeatureinfo - Triggered when a GetFeatureInfo response is received. │ │ │ │ + * The event object has a *text* property with the body of the │ │ │ │ + * response (String), a *features* property with an array of the │ │ │ │ + * parsed features, an *xy* property with the position of the mouse │ │ │ │ + * click or hover event that triggered the request, a *layer* property │ │ │ │ + * referencing the layer queried and a *request* property with the │ │ │ │ + * request itself. If drillDown is set to true, one event will be fired │ │ │ │ + * for each layer queried. │ │ │ │ + * exception - Triggered when a GetFeatureInfo request fails (with a │ │ │ │ + * status other than 200) or whenparsing fails. Listeners will receive │ │ │ │ + * an event with *request*, *xy*, and *layer* properties. In the case │ │ │ │ + * of a parsing error, the event will also contain an *error* property. │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: imageSize │ │ │ │ - * {<OpenLayers.Size>} Size (measured in pixels) of the image located │ │ │ │ - * by the 'imageSrc' property. │ │ │ │ - */ │ │ │ │ - imageSize: null, │ │ │ │ + /** │ │ │ │ + * Property: pending │ │ │ │ + * {Number} The number of pending requests. │ │ │ │ + */ │ │ │ │ + pending: 0, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: isAlphaImage │ │ │ │ - * {Boolean} The image has some alpha and thus needs to use the alpha │ │ │ │ - * image hack. Note that setting this to true will have no noticeable │ │ │ │ - * effect in FF or IE7 browsers, but will all but crush the ie6 │ │ │ │ - * browser. │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - isAlphaImage: false, │ │ │ │ + /** │ │ │ │ + * Constructor: <OpenLayers.Control.WMTSGetFeatureInfo> │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + options.handlerOptions = options.handlerOptions || {}; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: positionBlocks │ │ │ │ - * {Object} Hash of different position blocks (Object/Hashs). Each block │ │ │ │ - * will be keyed by a two-character 'relativePosition' │ │ │ │ - * code string (ie "tl", "tr", "bl", "br"). Block properties are │ │ │ │ - * 'offset', 'padding' (self-explanatory), and finally the 'blocks' │ │ │ │ - * parameter, which is an array of the block objects. │ │ │ │ - * │ │ │ │ - * Each block object must have 'size', 'anchor', and 'position' │ │ │ │ - * properties. │ │ │ │ - * │ │ │ │ - * Note that positionBlocks should never be modified at runtime. │ │ │ │ - */ │ │ │ │ - positionBlocks: null, │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: blocks │ │ │ │ - * {Array[Object]} Array of objects, each of which is one "block" of the │ │ │ │ - * popup. Each block has a 'div' and an 'image' property, both of │ │ │ │ - * which are DOMElements, and the latter of which is appended to the │ │ │ │ - * former. These are reused as the popup goes changing positions for │ │ │ │ - * great economy and elegance. │ │ │ │ - */ │ │ │ │ - blocks: null, │ │ │ │ + if (!this.format) { │ │ │ │ + this.format = new OpenLayers.Format.WMSGetFeatureInfo( │ │ │ │ + options.formatOptions │ │ │ │ + ); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: fixedRelativePosition │ │ │ │ - * {Boolean} We want the framed popup to work dynamically placed relative │ │ │ │ - * to its anchor but also in just one fixed position. A well designed │ │ │ │ - * framed popup will have the pixels and logic to display itself in │ │ │ │ - * any of the four relative positions, but (understandably), this will │ │ │ │ - * not be the case for all of them. By setting this property to 'true', │ │ │ │ - * framed popup will not recalculate for the best placement each time │ │ │ │ - * it's open, but will always open the same way. │ │ │ │ - * Note that if this is set to true, it is generally advisable to also │ │ │ │ - * set the 'panIntoView' property to true so that the popup can be │ │ │ │ - * scrolled into view (since it will often be offscreen on open) │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - fixedRelativePosition: false, │ │ │ │ + if (this.drillDown === true) { │ │ │ │ + this.hover = false; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Popup.Framed │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * id - {String} │ │ │ │ - * lonlat - {<OpenLayers.LonLat>} │ │ │ │ - * contentSize - {<OpenLayers.Size>} │ │ │ │ - * contentHTML - {String} │ │ │ │ - * anchor - {Object} Object to which we'll anchor the popup. Must expose │ │ │ │ - * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) │ │ │ │ - * (Note that this is generally an <OpenLayers.Icon>). │ │ │ │ - * closeBox - {Boolean} │ │ │ │ - * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ - */ │ │ │ │ - initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, │ │ │ │ - closeBoxCallback) { │ │ │ │ + if (this.hover) { │ │ │ │ + this.handler = new OpenLayers.Handler.Hover( │ │ │ │ + this, { │ │ │ │ + move: this.cancelHover, │ │ │ │ + pause: this.getInfoForHover │ │ │ │ + }, │ │ │ │ + OpenLayers.Util.extend( │ │ │ │ + this.handlerOptions.hover || {}, { │ │ │ │ + delay: 250 │ │ │ │ + } │ │ │ │ + ) │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + var callbacks = {}; │ │ │ │ + callbacks[this.clickCallback] = this.getInfoForClick; │ │ │ │ + this.handler = new OpenLayers.Handler.Click( │ │ │ │ + this, callbacks, this.handlerOptions.click || {} │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments); │ │ │ │ + /** │ │ │ │ + * Method: getInfoForClick │ │ │ │ + * Called on click │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ + */ │ │ │ │ + getInfoForClick: function(evt) { │ │ │ │ + this.request(evt.xy, {}); │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.fixedRelativePosition) { │ │ │ │ - //based on our decided relativePostion, set the current padding │ │ │ │ - // this keeps us from getting into trouble │ │ │ │ - this.updateRelativePosition(); │ │ │ │ + /** │ │ │ │ + * Method: getInfoForHover │ │ │ │ + * Pause callback for the hover handler │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} │ │ │ │ + */ │ │ │ │ + getInfoForHover: function(evt) { │ │ │ │ + this.request(evt.xy, { │ │ │ │ + hover: true │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ │ │ │ │ - //make calculateRelativePosition always return the specified │ │ │ │ - // fixed position. │ │ │ │ - this.calculateRelativePosition = function(px) { │ │ │ │ - return this.relativePosition; │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Method: cancelHover │ │ │ │ + * Cancel callback for the hover handler │ │ │ │ + */ │ │ │ │ + cancelHover: function() { │ │ │ │ + if (this.hoverRequest) { │ │ │ │ + --this.pending; │ │ │ │ + if (this.pending <= 0) { │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ + this.pending = 0; │ │ │ │ } │ │ │ │ + this.hoverRequest.abort(); │ │ │ │ + this.hoverRequest = null; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.contentDiv.style.position = "absolute"; │ │ │ │ - this.contentDiv.style.zIndex = 1; │ │ │ │ - │ │ │ │ - if (closeBox) { │ │ │ │ - this.closeDiv.style.zIndex = 1; │ │ │ │ + /** │ │ │ │ + * Method: findLayers │ │ │ │ + * Internal method to get the layers, independent of whether we are │ │ │ │ + * inspecting the map or using a client-provided array │ │ │ │ + */ │ │ │ │ + findLayers: function() { │ │ │ │ + var candidates = this.layers || this.map.layers; │ │ │ │ + var layers = []; │ │ │ │ + var layer; │ │ │ │ + for (var i = candidates.length - 1; i >= 0; --i) { │ │ │ │ + layer = candidates[i]; │ │ │ │ + if (layer instanceof OpenLayers.Layer.WMTS && │ │ │ │ + layer.requestEncoding === this.requestEncoding && │ │ │ │ + (!this.queryVisible || layer.getVisibility())) { │ │ │ │ + layers.push(layer); │ │ │ │ + if (!this.drillDown || this.hover) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ } │ │ │ │ + } │ │ │ │ + return layers; │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.groupDiv.style.position = "absolute"; │ │ │ │ - this.groupDiv.style.top = "0px"; │ │ │ │ - this.groupDiv.style.left = "0px"; │ │ │ │ - this.groupDiv.style.height = "100%"; │ │ │ │ - this.groupDiv.style.width = "100%"; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - this.imageSrc = null; │ │ │ │ - this.imageSize = null; │ │ │ │ - this.isAlphaImage = null; │ │ │ │ - │ │ │ │ - this.fixedRelativePosition = false; │ │ │ │ - this.positionBlocks = null; │ │ │ │ - │ │ │ │ - //remove our blocks │ │ │ │ - for (var i = 0; i < this.blocks.length; i++) { │ │ │ │ - var block = this.blocks[i]; │ │ │ │ - │ │ │ │ - if (block.image) { │ │ │ │ - block.div.removeChild(block.image); │ │ │ │ - } │ │ │ │ - block.image = null; │ │ │ │ + /** │ │ │ │ + * Method: buildRequestOptions │ │ │ │ + * Build an object with the relevant options for the GetFeatureInfo request. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * layer - {<OpenLayers.Layer.WMTS>} A WMTS layer. │ │ │ │ + * xy - {<OpenLayers.Pixel>} The position on the map where the │ │ │ │ + * mouse event occurred. │ │ │ │ + */ │ │ │ │ + buildRequestOptions: function(layer, xy) { │ │ │ │ + var loc = this.map.getLonLatFromPixel(xy); │ │ │ │ + var getTileUrl = layer.getURL( │ │ │ │ + new OpenLayers.Bounds(loc.lon, loc.lat, loc.lon, loc.lat) │ │ │ │ + ); │ │ │ │ + var params = OpenLayers.Util.getParameters(getTileUrl); │ │ │ │ + var tileInfo = layer.getTileInfo(loc); │ │ │ │ + OpenLayers.Util.extend(params, { │ │ │ │ + service: "WMTS", │ │ │ │ + version: layer.version, │ │ │ │ + request: "GetFeatureInfo", │ │ │ │ + infoFormat: this.infoFormat, │ │ │ │ + i: tileInfo.i, │ │ │ │ + j: tileInfo.j │ │ │ │ + }); │ │ │ │ + OpenLayers.Util.applyDefaults(params, this.vendorParams); │ │ │ │ + return { │ │ │ │ + url: OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url, │ │ │ │ + params: OpenLayers.Util.upperCaseObject(params), │ │ │ │ + callback: function(request) { │ │ │ │ + this.handleResponse(xy, request, layer); │ │ │ │ + }, │ │ │ │ + scope: this │ │ │ │ + }; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (block.div) { │ │ │ │ - this.groupDiv.removeChild(block.div); │ │ │ │ + /** │ │ │ │ + * Method: request │ │ │ │ + * Sends a GetFeatureInfo request to the WMTS │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * xy - {<OpenLayers.Pixel>} The position on the map where the mouse event │ │ │ │ + * occurred. │ │ │ │ + * options - {Object} additional options for this method. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * - *hover* {Boolean} true if we do the request for the hover handler │ │ │ │ + */ │ │ │ │ + request: function(xy, options) { │ │ │ │ + options = options || {}; │ │ │ │ + var layers = this.findLayers(); │ │ │ │ + if (layers.length > 0) { │ │ │ │ + var issue, layer; │ │ │ │ + for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ + layer = layers[i]; │ │ │ │ + issue = this.events.triggerEvent("beforegetfeatureinfo", { │ │ │ │ + xy: xy, │ │ │ │ + layer: layer │ │ │ │ + }); │ │ │ │ + if (issue !== false) { │ │ │ │ + ++this.pending; │ │ │ │ + var requestOptions = this.buildRequestOptions(layer, xy); │ │ │ │ + var request = OpenLayers.Request.GET(requestOptions); │ │ │ │ + if (options.hover === true) { │ │ │ │ + this.hoverRequest = request; │ │ │ │ + } │ │ │ │ } │ │ │ │ - block.div = null; │ │ │ │ } │ │ │ │ - this.blocks = null; │ │ │ │ - │ │ │ │ - OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setBackgroundColor │ │ │ │ - */ │ │ │ │ - setBackgroundColor: function(color) { │ │ │ │ - //does nothing since the framed popup's entire scheme is based on a │ │ │ │ - // an image -- changing the background color makes no sense. │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setBorder │ │ │ │ - */ │ │ │ │ - setBorder: function() { │ │ │ │ - //does nothing since the framed popup's entire scheme is based on a │ │ │ │ - // an image -- changing the popup's border makes no sense. │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setOpacity │ │ │ │ - * Sets the opacity of the popup. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). │ │ │ │ - */ │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ - //does nothing since we suppose that we'll never apply an opacity │ │ │ │ - // to a framed popup │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setSize │ │ │ │ - * Overridden here, because we need to update the blocks whenever the size │ │ │ │ - * of the popup has changed. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * contentSize - {<OpenLayers.Size>} the new size for the popup's │ │ │ │ - * contents div (in pixels). │ │ │ │ - */ │ │ │ │ - setSize: function(contentSize) { │ │ │ │ - OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments); │ │ │ │ - │ │ │ │ - this.updateBlocks(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: updateRelativePosition │ │ │ │ - * When the relative position changes, we need to set the new padding │ │ │ │ - * BBOX on the popup, reposition the close div, and update the blocks. │ │ │ │ - */ │ │ │ │ - updateRelativePosition: function() { │ │ │ │ - │ │ │ │ - //update the padding │ │ │ │ - this.padding = this.positionBlocks[this.relativePosition].padding; │ │ │ │ - │ │ │ │ - //update the position of our close box to new padding │ │ │ │ - if (this.closeDiv) { │ │ │ │ - // use the content div's css padding to determine if we should │ │ │ │ - // padd the close div │ │ │ │ - var contentDivPadding = this.getContentDivPadding(); │ │ │ │ - │ │ │ │ - this.closeDiv.style.right = contentDivPadding.right + │ │ │ │ - this.padding.right + "px"; │ │ │ │ - this.closeDiv.style.top = contentDivPadding.top + │ │ │ │ - this.padding.top + "px"; │ │ │ │ + if (this.pending > 0) { │ │ │ │ + OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.updateBlocks(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: calculateNewPx │ │ │ │ - * Besides the standard offset as determined by the Anchored class, our │ │ │ │ - * Framed popups have a special 'offset' property for each of their │ │ │ │ - * positions, which is used to offset the popup relative to its anchor. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} The the new px position of the popup on the screen │ │ │ │ - * relative to the passed-in px. │ │ │ │ - */ │ │ │ │ - calculateNewPx: function(px) { │ │ │ │ - var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply( │ │ │ │ - this, arguments │ │ │ │ - ); │ │ │ │ - │ │ │ │ - newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset); │ │ │ │ - │ │ │ │ - return newPx; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: handleResponse │ │ │ │ + * Handler for the GetFeatureInfo response. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * xy - {<OpenLayers.Pixel>} The position on the map where the mouse event │ │ │ │ + * occurred. │ │ │ │ + * request - {XMLHttpRequest} The request object. │ │ │ │ + * layer - {<OpenLayers.Layer.WMTS>} The queried layer. │ │ │ │ + */ │ │ │ │ + handleResponse: function(xy, request, layer) { │ │ │ │ + --this.pending; │ │ │ │ + if (this.pending <= 0) { │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ + this.pending = 0; │ │ │ │ + } │ │ │ │ + if (request.status && (request.status < 200 || request.status >= 300)) { │ │ │ │ + this.events.triggerEvent("exception", { │ │ │ │ + xy: xy, │ │ │ │ + request: request, │ │ │ │ + layer: layer │ │ │ │ + }); │ │ │ │ + } else { │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText; │ │ │ │ + } │ │ │ │ + var features, except; │ │ │ │ + try { │ │ │ │ + features = this.format.read(doc); │ │ │ │ + } catch (error) { │ │ │ │ + except = true; │ │ │ │ + this.events.triggerEvent("exception", { │ │ │ │ + xy: xy, │ │ │ │ + request: request, │ │ │ │ + error: error, │ │ │ │ + layer: layer │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + if (!except) { │ │ │ │ + this.events.triggerEvent("getfeatureinfo", { │ │ │ │ + text: request.responseText, │ │ │ │ + features: features, │ │ │ │ + request: request, │ │ │ │ + xy: xy, │ │ │ │ + layer: layer │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: createBlocks │ │ │ │ - */ │ │ │ │ - createBlocks: function() { │ │ │ │ - this.blocks = []; │ │ │ │ + CLASS_NAME: "OpenLayers.Control.WMTSGetFeatureInfo" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/TouchNavigation.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - //since all positions contain the same number of blocks, we can │ │ │ │ - // just pick the first position and use its blocks array to create │ │ │ │ - // our blocks array │ │ │ │ - var firstPosition = null; │ │ │ │ - for (var key in this.positionBlocks) { │ │ │ │ - firstPosition = key; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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 position = this.positionBlocks[firstPosition]; │ │ │ │ - for (var i = 0; i < position.blocks.length; i++) { │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control/DragPan.js │ │ │ │ + * @requires OpenLayers/Control/PinchZoom.js │ │ │ │ + * @requires OpenLayers/Handler/Click.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - var block = {}; │ │ │ │ - this.blocks.push(block); │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.TouchNavigation │ │ │ │ + * The navigation control handles map browsing with touch events (dragging, │ │ │ │ + * double-tapping, tap with two fingers, and pinch zoom). Create a new │ │ │ │ + * control with the <OpenLayers.Control.TouchNavigation> constructor. │ │ │ │ + * │ │ │ │ + * If you’re only targeting touch enabled devices with your mapping application, │ │ │ │ + * you can create a map with only a TouchNavigation control. The │ │ │ │ + * <OpenLayers.Control.Navigation> control is mobile ready by default, but │ │ │ │ + * you can generate a smaller build of the library by only including this │ │ │ │ + * touch navigation control if you aren't concerned about mouse interaction. │ │ │ │ + * │ │ │ │ + * Inherits: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - var divId = this.id + '_FrameDecorationDiv_' + i; │ │ │ │ - block.div = OpenLayers.Util.createDiv(divId, │ │ │ │ - null, null, null, "absolute", null, "hidden", null │ │ │ │ - ); │ │ │ │ + /** │ │ │ │ + * Property: dragPan │ │ │ │ + * {<OpenLayers.Control.DragPan>} │ │ │ │ + */ │ │ │ │ + dragPan: null, │ │ │ │ │ │ │ │ - var imgId = this.id + '_FrameDecorationImg_' + i; │ │ │ │ - var imageCreator = │ │ │ │ - (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv : │ │ │ │ - OpenLayers.Util.createImage; │ │ │ │ + /** │ │ │ │ + * APIProperty: dragPanOptions │ │ │ │ + * {Object} Options passed to the DragPan control. │ │ │ │ + */ │ │ │ │ + dragPanOptions: null, │ │ │ │ │ │ │ │ - block.image = imageCreator(imgId, │ │ │ │ - null, this.imageSize, this.imageSrc, │ │ │ │ - "absolute", null, null, null │ │ │ │ - ); │ │ │ │ + /** │ │ │ │ + * Property: pinchZoom │ │ │ │ + * {<OpenLayers.Control.PinchZoom>} │ │ │ │ + */ │ │ │ │ + pinchZoom: null, │ │ │ │ │ │ │ │ - block.div.appendChild(block.image); │ │ │ │ - this.groupDiv.appendChild(block.div); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: pinchZoomOptions │ │ │ │ + * {Object} Options passed to the PinchZoom control. │ │ │ │ + */ │ │ │ │ + pinchZoomOptions: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: updateBlocks │ │ │ │ - * Internal method, called on initialize and when the popup's relative │ │ │ │ - * position has changed. This function takes care of re-positioning │ │ │ │ - * the popup's blocks in their appropropriate places. │ │ │ │ - */ │ │ │ │ - updateBlocks: function() { │ │ │ │ - if (!this.blocks) { │ │ │ │ - this.createBlocks(); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: clickHandlerOptions │ │ │ │ + * {Object} Options passed to the Click handler. │ │ │ │ + */ │ │ │ │ + clickHandlerOptions: null, │ │ │ │ │ │ │ │ - if (this.size && this.relativePosition) { │ │ │ │ - var position = this.positionBlocks[this.relativePosition]; │ │ │ │ - for (var i = 0; i < position.blocks.length; i++) { │ │ │ │ + /** │ │ │ │ + * APIProperty: documentDrag │ │ │ │ + * {Boolean} Allow panning of the map by dragging outside map viewport. │ │ │ │ + * Default is false. │ │ │ │ + */ │ │ │ │ + documentDrag: false, │ │ │ │ │ │ │ │ - var positionBlock = position.blocks[i]; │ │ │ │ - var block = this.blocks[i]; │ │ │ │ + /** │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * true. │ │ │ │ + */ │ │ │ │ + autoActivate: true, │ │ │ │ │ │ │ │ - // adjust sizes │ │ │ │ - var l = positionBlock.anchor.left; │ │ │ │ - var b = positionBlock.anchor.bottom; │ │ │ │ - var r = positionBlock.anchor.right; │ │ │ │ - var t = positionBlock.anchor.top; │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.TouchNavigation │ │ │ │ + * Create a new navigation control │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * the control │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + this.handlers = {}; │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - //note that we use the isNaN() test here because if the │ │ │ │ - // size object is initialized with a "auto" parameter, the │ │ │ │ - // size constructor calls parseFloat() on the string, │ │ │ │ - // which will turn it into NaN │ │ │ │ - // │ │ │ │ - var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l) : │ │ │ │ - positionBlock.size.w; │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * The destroy method is used to perform any clean up before the control │ │ │ │ + * is dereferenced. Typically this is where event listeners are removed │ │ │ │ + * to prevent memory leaks. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ + if (this.dragPan) { │ │ │ │ + this.dragPan.destroy(); │ │ │ │ + } │ │ │ │ + this.dragPan = null; │ │ │ │ + if (this.pinchZoom) { │ │ │ │ + this.pinchZoom.destroy(); │ │ │ │ + delete this.pinchZoom; │ │ │ │ + } │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t) : │ │ │ │ - positionBlock.size.h; │ │ │ │ + /** │ │ │ │ + * Method: activate │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.dragPan.activate(); │ │ │ │ + this.handlers.click.activate(); │ │ │ │ + this.pinchZoom.activate(); │ │ │ │ + return true; │ │ │ │ + } │ │ │ │ + return false; │ │ │ │ + }, │ │ │ │ │ │ │ │ - block.div.style.width = (w < 0 ? 0 : w) + 'px'; │ │ │ │ - block.div.style.height = (h < 0 ? 0 : h) + 'px'; │ │ │ │ + /** │ │ │ │ + * Method: deactivate │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.dragPan.deactivate(); │ │ │ │ + this.handlers.click.deactivate(); │ │ │ │ + this.pinchZoom.deactivate(); │ │ │ │ + return true; │ │ │ │ + } │ │ │ │ + return false; │ │ │ │ + }, │ │ │ │ │ │ │ │ - block.div.style.left = (l != null) ? l + 'px' : ''; │ │ │ │ - block.div.style.bottom = (b != null) ? b + 'px' : ''; │ │ │ │ - block.div.style.right = (r != null) ? r + 'px' : ''; │ │ │ │ - block.div.style.top = (t != null) ? t + 'px' : ''; │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + var clickCallbacks = { │ │ │ │ + click: this.defaultClick, │ │ │ │ + dblclick: this.defaultDblClick │ │ │ │ + }; │ │ │ │ + var clickOptions = OpenLayers.Util.extend({ │ │ │ │ + "double": true, │ │ │ │ + stopDouble: true, │ │ │ │ + pixelTolerance: 2 │ │ │ │ + }, this.clickHandlerOptions); │ │ │ │ + this.handlers.click = new OpenLayers.Handler.Click( │ │ │ │ + this, clickCallbacks, clickOptions │ │ │ │ + ); │ │ │ │ + this.dragPan = new OpenLayers.Control.DragPan( │ │ │ │ + OpenLayers.Util.extend({ │ │ │ │ + map: this.map, │ │ │ │ + documentDrag: this.documentDrag │ │ │ │ + }, this.dragPanOptions) │ │ │ │ + ); │ │ │ │ + this.dragPan.draw(); │ │ │ │ + this.pinchZoom = new OpenLayers.Control.PinchZoom( │ │ │ │ + OpenLayers.Util.extend({ │ │ │ │ + map: this.map │ │ │ │ + }, this.pinchZoomOptions) │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ - block.image.style.left = positionBlock.position.x + 'px'; │ │ │ │ - block.image.style.top = positionBlock.position.y + 'px'; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: defaultClick │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + */ │ │ │ │ + defaultClick: function(evt) { │ │ │ │ + if (evt.lastTouches && evt.lastTouches.length == 2) { │ │ │ │ + this.map.zoomOut(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.contentDiv.style.left = this.padding.left + "px"; │ │ │ │ - this.contentDiv.style.top = this.padding.top + "px"; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: defaultDblClick │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + */ │ │ │ │ + defaultDblClick: function(evt) { │ │ │ │ + this.map.zoomTo(this.map.zoom + 1, evt.xy); │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Popup.Framed" │ │ │ │ - }); │ │ │ │ + CLASS_NAME: "OpenLayers.Control.TouchNavigation" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Popup/FramedCloud.js │ │ │ │ + OpenLayers/Control/UTFGrid.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Popup/Framed.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ - * @requires OpenLayers/BaseTypes/Bounds.js │ │ │ │ - * @requires OpenLayers/BaseTypes/Pixel.js │ │ │ │ - * @requires OpenLayers/BaseTypes/Size.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Handler/Hover.js │ │ │ │ + * @requires OpenLayers/Handler/Click.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Popup.FramedCloud │ │ │ │ + * Class: OpenLayers.Control.UTFGrid │ │ │ │ + * │ │ │ │ + * This Control provides behavior associated with UTFGrid Layers. │ │ │ │ + * These 'hit grids' provide underlying feature attributes without │ │ │ │ + * calling the server (again). This control allows Mousemove, Hovering │ │ │ │ + * and Click events to trigger callbacks that use the attributes in │ │ │ │ + * whatever way you need. │ │ │ │ + * │ │ │ │ + * The most common example may be a UTFGrid layer containing feature │ │ │ │ + * attributes that are displayed in a div as you mouseover. │ │ │ │ + * │ │ │ │ + * Example Code: │ │ │ │ + * │ │ │ │ + * (start code) │ │ │ │ + * var world_utfgrid = new OpenLayers.Layer.UTFGrid( │ │ │ │ + * 'UTFGrid Layer', │ │ │ │ + * "http://tiles/world_utfgrid/${z}/${x}/${y}.json" │ │ │ │ + * ); │ │ │ │ + * map.addLayer(world_utfgrid); │ │ │ │ * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Popup.Framed> │ │ │ │ + * var control = new OpenLayers.Control.UTFGrid({ │ │ │ │ + * layers: [world_utfgrid], │ │ │ │ + * handlerMode: 'move', │ │ │ │ + * callback: function(infoLookup) { │ │ │ │ + * // do something with returned data │ │ │ │ + * │ │ │ │ + * } │ │ │ │ + * }) │ │ │ │ + * (end code) │ │ │ │ + * │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Popup.FramedCloud = │ │ │ │ - OpenLayers.Class(OpenLayers.Popup.Framed, { │ │ │ │ +OpenLayers.Control.UTFGrid = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: contentDisplayClass │ │ │ │ - * {String} The CSS class of the popup content div. │ │ │ │ - */ │ │ │ │ - contentDisplayClass: "olFramedCloudPopupContent", │ │ │ │ + /** │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * true. │ │ │ │ + */ │ │ │ │ + autoActivate: true, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: autoSize │ │ │ │ - * {Boolean} Framed Cloud is autosizing by default. │ │ │ │ - */ │ │ │ │ - autoSize: true, │ │ │ │ + /** │ │ │ │ + * APIProperty: Layers │ │ │ │ + * List of layers to consider. Must be Layer.UTFGrids │ │ │ │ + * `null` is the default indicating all UTFGrid Layers are queried. │ │ │ │ + * {Array} <OpenLayers.Layer.UTFGrid> │ │ │ │ + */ │ │ │ │ + layers: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: panMapIfOutOfView │ │ │ │ - * {Boolean} Framed Cloud does pan into view by default. │ │ │ │ - */ │ │ │ │ - panMapIfOutOfView: true, │ │ │ │ + /* Property: defaultHandlerOptions │ │ │ │ + * The default opts passed to the handler constructors │ │ │ │ + */ │ │ │ │ + defaultHandlerOptions: { │ │ │ │ + 'delay': 300, │ │ │ │ + 'pixelTolerance': 4, │ │ │ │ + 'stopMove': false, │ │ │ │ + 'single': true, │ │ │ │ + 'double': false, │ │ │ │ + 'stopSingle': false, │ │ │ │ + 'stopDouble': false │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: imageSize │ │ │ │ - * {<OpenLayers.Size>} │ │ │ │ - */ │ │ │ │ - imageSize: new OpenLayers.Size(1276, 736), │ │ │ │ + /* APIProperty: handlerMode │ │ │ │ + * Defaults to 'click'. Can be 'hover' or 'move'. │ │ │ │ + */ │ │ │ │ + handlerMode: 'click', │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: isAlphaImage │ │ │ │ - * {Boolean} The FramedCloud does not use an alpha image (in honor of the │ │ │ │ - * good ie6 folk out there) │ │ │ │ - */ │ │ │ │ - isAlphaImage: false, │ │ │ │ + /** │ │ │ │ + * APIMethod: setHandler │ │ │ │ + * sets this.handlerMode and calls resetHandler() │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * hm - {String} Handler Mode string; 'click', 'hover' or 'move'. │ │ │ │ + */ │ │ │ │ + setHandler: function(hm) { │ │ │ │ + this.handlerMode = hm; │ │ │ │ + this.resetHandler(); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: fixedRelativePosition │ │ │ │ - * {Boolean} The Framed Cloud popup works in just one fixed position. │ │ │ │ - */ │ │ │ │ - fixedRelativePosition: false, │ │ │ │ + /** │ │ │ │ + * Method: resetHandler │ │ │ │ + * Deactivates the old hanlder and creates a new │ │ │ │ + * <OpenLayers.Handler> based on the mode specified in │ │ │ │ + * this.handlerMode │ │ │ │ + * │ │ │ │ + */ │ │ │ │ + resetHandler: function() { │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.deactivate(); │ │ │ │ + this.handler.destroy(); │ │ │ │ + this.handler = null; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: positionBlocks │ │ │ │ - * {Object} Hash of differen position blocks, keyed by relativePosition │ │ │ │ - * two-character code string (ie "tl", "tr", "bl", "br") │ │ │ │ - */ │ │ │ │ - positionBlocks: { │ │ │ │ - "tl": { │ │ │ │ - 'offset': new OpenLayers.Pixel(44, 0), │ │ │ │ - 'padding': new OpenLayers.Bounds(8, 40, 8, 9), │ │ │ │ - 'blocks': [{ // top-left │ │ │ │ - size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 51, 22, 0), │ │ │ │ - position: new OpenLayers.Pixel(0, 0) │ │ │ │ - }, { //top-right │ │ │ │ - size: new OpenLayers.Size(22, 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 50, 0, 0), │ │ │ │ - position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ - }, { //bottom-left │ │ │ │ - size: new OpenLayers.Size('auto', 19), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 32, 22, null), │ │ │ │ - position: new OpenLayers.Pixel(0, -631) │ │ │ │ - }, { //bottom-right │ │ │ │ - size: new OpenLayers.Size(22, 18), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 32, 0, null), │ │ │ │ - position: new OpenLayers.Pixel(-1238, -632) │ │ │ │ - }, { // stem │ │ │ │ - size: new OpenLayers.Size(81, 35), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 0, 0, null), │ │ │ │ - position: new OpenLayers.Pixel(0, -688) │ │ │ │ - }] │ │ │ │ - }, │ │ │ │ - "tr": { │ │ │ │ - 'offset': new OpenLayers.Pixel(-45, 0), │ │ │ │ - 'padding': new OpenLayers.Bounds(8, 40, 8, 9), │ │ │ │ - 'blocks': [{ // top-left │ │ │ │ - size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 51, 22, 0), │ │ │ │ - position: new OpenLayers.Pixel(0, 0) │ │ │ │ - }, { //top-right │ │ │ │ - size: new OpenLayers.Size(22, 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 50, 0, 0), │ │ │ │ - position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ - }, { //bottom-left │ │ │ │ - size: new OpenLayers.Size('auto', 19), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 32, 22, null), │ │ │ │ - position: new OpenLayers.Pixel(0, -631) │ │ │ │ - }, { //bottom-right │ │ │ │ - size: new OpenLayers.Size(22, 19), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 32, 0, null), │ │ │ │ - position: new OpenLayers.Pixel(-1238, -631) │ │ │ │ - }, { // stem │ │ │ │ - size: new OpenLayers.Size(81, 35), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 0, null, null), │ │ │ │ - position: new OpenLayers.Pixel(-215, -687) │ │ │ │ - }] │ │ │ │ - }, │ │ │ │ - "bl": { │ │ │ │ - 'offset': new OpenLayers.Pixel(45, 0), │ │ │ │ - 'padding': new OpenLayers.Bounds(8, 9, 8, 40), │ │ │ │ - 'blocks': [{ // top-left │ │ │ │ - size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 21, 22, 32), │ │ │ │ - position: new OpenLayers.Pixel(0, 0) │ │ │ │ - }, { //top-right │ │ │ │ - size: new OpenLayers.Size(22, 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 21, 0, 32), │ │ │ │ - position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ - }, { //bottom-left │ │ │ │ - size: new OpenLayers.Size('auto', 21), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 0, 22, null), │ │ │ │ - position: new OpenLayers.Pixel(0, -629) │ │ │ │ - }, { //bottom-right │ │ │ │ - size: new OpenLayers.Size(22, 21), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 0, 0, null), │ │ │ │ - position: new OpenLayers.Pixel(-1238, -629) │ │ │ │ - }, { // stem │ │ │ │ - size: new OpenLayers.Size(81, 33), │ │ │ │ - anchor: new OpenLayers.Bounds(null, null, 0, 0), │ │ │ │ - position: new OpenLayers.Pixel(-101, -674) │ │ │ │ - }] │ │ │ │ - }, │ │ │ │ - "br": { │ │ │ │ - 'offset': new OpenLayers.Pixel(-44, 0), │ │ │ │ - 'padding': new OpenLayers.Bounds(8, 9, 8, 40), │ │ │ │ - 'blocks': [{ // top-left │ │ │ │ - size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 21, 22, 32), │ │ │ │ - position: new OpenLayers.Pixel(0, 0) │ │ │ │ - }, { //top-right │ │ │ │ - size: new OpenLayers.Size(22, 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 21, 0, 32), │ │ │ │ - position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ - }, { //bottom-left │ │ │ │ - size: new OpenLayers.Size('auto', 21), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 0, 22, null), │ │ │ │ - position: new OpenLayers.Pixel(0, -629) │ │ │ │ - }, { //bottom-right │ │ │ │ - size: new OpenLayers.Size(22, 21), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 0, 0, null), │ │ │ │ - position: new OpenLayers.Pixel(-1238, -629) │ │ │ │ - }, { // stem │ │ │ │ - size: new OpenLayers.Size(81, 33), │ │ │ │ - anchor: new OpenLayers.Bounds(0, null, null, 0), │ │ │ │ - position: new OpenLayers.Pixel(-311, -674) │ │ │ │ - }] │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + if (this.handlerMode == 'hover') { │ │ │ │ + // Handle this event on hover │ │ │ │ + this.handler = new OpenLayers.Handler.Hover( │ │ │ │ + this, { │ │ │ │ + 'pause': this.handleEvent, │ │ │ │ + 'move': this.reset │ │ │ │ + }, │ │ │ │ + this.handlerOptions │ │ │ │ + ); │ │ │ │ + } else if (this.handlerMode == 'click') { │ │ │ │ + // Handle this event on click │ │ │ │ + this.handler = new OpenLayers.Handler.Click( │ │ │ │ + this, { │ │ │ │ + 'click': this.handleEvent │ │ │ │ + }, this.handlerOptions │ │ │ │ + ); │ │ │ │ + } else if (this.handlerMode == 'move') { │ │ │ │ + this.handler = new OpenLayers.Handler.Hover( │ │ │ │ + this, │ │ │ │ + // Handle this event while hovering OR moving │ │ │ │ + { │ │ │ │ + 'pause': this.handleEvent, │ │ │ │ + 'move': this.handleEvent │ │ │ │ + }, │ │ │ │ + this.handlerOptions │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + if (this.handler) { │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: minSize │ │ │ │ - * {<OpenLayers.Size>} │ │ │ │ - */ │ │ │ │ - minSize: new OpenLayers.Size(105, 10), │ │ │ │ + /** │ │ │ │ + * Constructor: <OpenLayers.Control.UTFGrid> │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + options.handlerOptions = options.handlerOptions || this.defaultHandlerOptions; │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + this.resetHandler(); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: maxSize │ │ │ │ - * {<OpenLayers.Size>} │ │ │ │ - */ │ │ │ │ - maxSize: new OpenLayers.Size(1200, 660), │ │ │ │ + /** │ │ │ │ + * Method: handleEvent │ │ │ │ + * Internal method called when specified event is triggered. │ │ │ │ + * │ │ │ │ + * This method does several things: │ │ │ │ + * │ │ │ │ + * Gets the lonLat of the event. │ │ │ │ + * │ │ │ │ + * Loops through the appropriate hit grid layers and gathers the attributes. │ │ │ │ + * │ │ │ │ + * Passes the attributes to the callback │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ + */ │ │ │ │ + handleEvent: function(evt) { │ │ │ │ + if (evt == null) { │ │ │ │ + this.reset(); │ │ │ │ + return; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Popup.FramedCloud │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * id - {String} │ │ │ │ - * lonlat - {<OpenLayers.LonLat>} │ │ │ │ - * contentSize - {<OpenLayers.Size>} │ │ │ │ - * contentHTML - {String} │ │ │ │ - * anchor - {Object} Object to which we'll anchor the popup. Must expose │ │ │ │ - * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) │ │ │ │ - * (Note that this is generally an <OpenLayers.Icon>). │ │ │ │ - * closeBox - {Boolean} │ │ │ │ - * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ - */ │ │ │ │ - initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, │ │ │ │ - closeBoxCallback) { │ │ │ │ + var lonLat = this.map.getLonLatFromPixel(evt.xy); │ │ │ │ + if (!lonLat) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ │ │ │ │ - this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png'); │ │ │ │ - OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments); │ │ │ │ - this.contentDiv.className = this.contentDisplayClass; │ │ │ │ - }, │ │ │ │ + var layers = this.findLayers(); │ │ │ │ + if (layers.length > 0) { │ │ │ │ + var infoLookup = {}; │ │ │ │ + var layer, idx; │ │ │ │ + for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ + layer = layers[i]; │ │ │ │ + idx = OpenLayers.Util.indexOf(this.map.layers, layer); │ │ │ │ + infoLookup[idx] = layer.getFeatureInfo(lonLat); │ │ │ │ + } │ │ │ │ + this.callback(infoLookup, lonLat, evt.xy); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Popup.FramedCloud" │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * APIMethod: callback │ │ │ │ + * Function to be called when a mouse event corresponds with a location that │ │ │ │ + * includes data in one of the configured UTFGrid layers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * infoLookup - {Object} Keys of this object are layer indexes and can be │ │ │ │ + * used to resolve a layer in the map.layers array. The structure of │ │ │ │ + * the property values depend on the data included in the underlying │ │ │ │ + * UTFGrid and may be any valid JSON type. │ │ │ │ + */ │ │ │ │ + callback: function(infoLookup) { │ │ │ │ + // to be provided in the constructor │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: reset │ │ │ │ + * Calls the callback with null. │ │ │ │ + */ │ │ │ │ + reset: function(evt) { │ │ │ │ + this.callback(null); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: findLayers │ │ │ │ + * Internal method to get the layers, independent of whether we are │ │ │ │ + * inspecting the map or using a client-provided array │ │ │ │ + * │ │ │ │ + * The default value of this.layers is null; this causes the │ │ │ │ + * findLayers method to return ALL UTFGrid layers encountered. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * None │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} Layers to handle on each event │ │ │ │ + */ │ │ │ │ + findLayers: function() { │ │ │ │ + var candidates = this.layers || this.map.layers; │ │ │ │ + var layers = []; │ │ │ │ + var layer; │ │ │ │ + for (var i = candidates.length - 1; i >= 0; --i) { │ │ │ │ + layer = candidates[i]; │ │ │ │ + if (layer instanceof OpenLayers.Layer.UTFGrid) { │ │ │ │ + layers.push(layer); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return layers; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Control.UTFGrid" │ │ │ │ +}); │ │ ├── ./usr/share/javascript/openlayers/OpenLayers.light.js │ │ │ ├── js-beautify {} │ │ │ │ @@ -5622,253 +5622,506 @@ │ │ │ │ * {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 │ │ │ │ + OpenLayers/StyleMap.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ * @requires OpenLayers/Style.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Rule │ │ │ │ - * This class represents an SLD Rule, as being used for rule-based SLD styling. │ │ │ │ + * Class: OpenLayers.StyleMap │ │ │ │ */ │ │ │ │ -OpenLayers.Rule = OpenLayers.Class({ │ │ │ │ +OpenLayers.StyleMap = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: id │ │ │ │ - * {String} A unique id for this session. │ │ │ │ + * Property: styles │ │ │ │ + * {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known │ │ │ │ + * rendering intents (e.g. "default", "temporary", "select", "delete"). │ │ │ │ */ │ │ │ │ - id: null, │ │ │ │ + styles: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: name │ │ │ │ - * {String} name of this rule │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - name: null, │ │ │ │ + extendDefault: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: title │ │ │ │ - * {String} Title of this rule (set if included in SLD) │ │ │ │ + * Constructor: OpenLayers.StyleMap │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * style - {Object} Optional. Either a style hash, or a style object, or │ │ │ │ + * a hash of style objects (style hashes) keyed by rendering │ │ │ │ + * intent. If just one style hash or style object is passed, │ │ │ │ + * this will be used for all known render intents (default, │ │ │ │ + * select, temporary) │ │ │ │ + * options - {Object} optional hash of additional options for this │ │ │ │ + * instance │ │ │ │ */ │ │ │ │ - title: null, │ │ │ │ + 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"]) │ │ │ │ + }; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: description │ │ │ │ - * {String} Description of this rule (set if abstract is included in SLD) │ │ │ │ - */ │ │ │ │ - description: null, │ │ │ │ + // take whatever the user passed as style parameter and convert it │ │ │ │ + // into parts of stylemap. │ │ │ │ + if (style instanceof OpenLayers.Style) { │ │ │ │ + // user passed a style object │ │ │ │ + this.styles["default"] = style; │ │ │ │ + this.styles["select"] = style; │ │ │ │ + this.styles["temporary"] = style; │ │ │ │ + this.styles["delete"] = style; │ │ │ │ + } else if (typeof style == "object") { │ │ │ │ + for (var key in style) { │ │ │ │ + if (style[key] instanceof OpenLayers.Style) { │ │ │ │ + // user passed a hash of style objects │ │ │ │ + this.styles[key] = style[key]; │ │ │ │ + } else if (typeof style[key] == "object") { │ │ │ │ + // user passsed a hash of style hashes │ │ │ │ + this.styles[key] = new OpenLayers.Style(style[key]); │ │ │ │ + } else { │ │ │ │ + // user passed a style hash (i.e. symbolizer) │ │ │ │ + this.styles["default"] = new OpenLayers.Style(style); │ │ │ │ + this.styles["select"] = new OpenLayers.Style(style); │ │ │ │ + this.styles["temporary"] = new OpenLayers.Style(style); │ │ │ │ + this.styles["delete"] = new OpenLayers.Style(style); │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - context: null, │ │ │ │ + destroy: function() { │ │ │ │ + for (var key in this.styles) { │ │ │ │ + this.styles[key].destroy(); │ │ │ │ + } │ │ │ │ + this.styles = null; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: filter │ │ │ │ - * {<OpenLayers.Filter>} Optional filter for the rule. │ │ │ │ + * Method: createSymbolizer │ │ │ │ + * Creates the symbolizer for a feature for a render intent. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature>} The feature to evaluate the rules │ │ │ │ + * of the intended style against. │ │ │ │ + * intent - {String} The intent determines the symbolizer that will be │ │ │ │ + * used to draw the feature. Well known intents are "default" │ │ │ │ + * (for just drawing the features), "select" (for selected │ │ │ │ + * features) and "temporary" (for drawing features). │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} symbolizer hash │ │ │ │ */ │ │ │ │ - filter: null, │ │ │ │ + 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)); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * 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 │ │ │ │ */ │ │ │ │ - elseFilter: false, │ │ │ │ + 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); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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, │ │ │ │ + CLASS_NAME: "OpenLayers.StyleMap" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Projection.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Namespace: OpenLayers.Projection │ │ │ │ + * Methods for coordinate transforms between coordinate systems. By default, │ │ │ │ + * OpenLayers ships with the ability to transform coordinates between │ │ │ │ + * geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.) │ │ │ │ + * coordinate reference systems. See the <transform> method for details │ │ │ │ + * on usage. │ │ │ │ + * │ │ │ │ + * Additional transforms may be added by using the <proj4js at http://proj4js.org/> │ │ │ │ + * library. If the proj4js library is included, the <transform> method │ │ │ │ + * will work between any two coordinate reference systems with proj4js │ │ │ │ + * definitions. │ │ │ │ + * │ │ │ │ + * If the proj4js library is not included, or if you wish to allow transforms │ │ │ │ + * between arbitrary coordinate reference systems, use the <addTransform> │ │ │ │ + * method to register a custom transform method. │ │ │ │ + */ │ │ │ │ +OpenLayers.Projection = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: 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. │ │ │ │ + * Property: proj │ │ │ │ + * {Object} Proj4js.Proj instance. │ │ │ │ */ │ │ │ │ - symbolizers: null, │ │ │ │ + proj: 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}" │ │ │ │ + * Property: projCode │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - minScaleDenominator: null, │ │ │ │ + projCode: 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}" │ │ │ │ + * Property: titleRegEx │ │ │ │ + * {RegExp} regular expression to strip the title from a proj4js definition │ │ │ │ */ │ │ │ │ - maxScaleDenominator: null, │ │ │ │ + titleRegEx: /\+title=[^\+]*/, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Rule │ │ │ │ - * Creates a Rule. │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Projection │ │ │ │ + * This class offers several methods for interacting with a wrapped │ │ │ │ + * pro4js projection object. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object with properties to set on the │ │ │ │ - * rule │ │ │ │ - * │ │ │ │ + * projCode - {String} A string identifying the Well Known Identifier for │ │ │ │ + * the projection. │ │ │ │ + * options - {Object} An optional object to set additional properties │ │ │ │ + * on the projection. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Rule>} │ │ │ │ + * {<OpenLayers.Projection>} A projection object. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - this.symbolizer = {}; │ │ │ │ + initialize: function(projCode, options) { │ │ │ │ OpenLayers.Util.extend(this, options); │ │ │ │ - if (this.symbolizers) { │ │ │ │ - delete this.symbolizer; │ │ │ │ + this.projCode = projCode; │ │ │ │ + if (typeof Proj4js == "object") { │ │ │ │ + this.proj = new Proj4js.Proj(projCode); │ │ │ │ } │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * nullify references to prevent circular references and memory leaks │ │ │ │ + /** │ │ │ │ + * APIMethod: getCode │ │ │ │ + * Get the string SRS code. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The SRS code. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - for (var i in this.symbolizer) { │ │ │ │ - this.symbolizer[i] = null; │ │ │ │ - } │ │ │ │ - this.symbolizer = null; │ │ │ │ - delete this.symbolizers; │ │ │ │ + getCode: function() { │ │ │ │ + return this.proj ? this.proj.srsCode : this.projCode; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: evaluate │ │ │ │ - * evaluates this rule for a specific feature │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature>} feature to apply the rule to. │ │ │ │ - * │ │ │ │ + * APIMethod: getUnits │ │ │ │ + * Get the units string for the projection -- returns null if │ │ │ │ + * proj4js is not available. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} true if the rule applies, false if it does not. │ │ │ │ - * This rule is the default rule and always returns true. │ │ │ │ + * {String} The units abbreviation. │ │ │ │ */ │ │ │ │ - evaluate: function(feature) { │ │ │ │ - var context = this.getContext(feature); │ │ │ │ - var applies = true; │ │ │ │ - │ │ │ │ - if (this.minScaleDenominator || this.maxScaleDenominator) { │ │ │ │ - var scale = feature.layer.map.getScale(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // check if within minScale/maxScale bounds │ │ │ │ - if (this.minScaleDenominator) { │ │ │ │ - applies = scale >= OpenLayers.Style.createLiteral( │ │ │ │ - this.minScaleDenominator, context); │ │ │ │ - } │ │ │ │ - if (applies && this.maxScaleDenominator) { │ │ │ │ - applies = scale < OpenLayers.Style.createLiteral( │ │ │ │ - this.maxScaleDenominator, context); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // check if optional filter applies │ │ │ │ - if (applies && this.filter) { │ │ │ │ - // feature id filters get the feature, others get the context │ │ │ │ - if (this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") { │ │ │ │ - applies = this.filter.evaluate(feature); │ │ │ │ - } else { │ │ │ │ - applies = this.filter.evaluate(context); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - return applies; │ │ │ │ + getUnits: function() { │ │ │ │ + return this.proj ? this.proj.units : null; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getContext │ │ │ │ - * Gets the context for evaluating this rule │ │ │ │ - * │ │ │ │ - * Paramters: │ │ │ │ - * feature - {<OpenLayers.Feature>} feature to take the context from if │ │ │ │ - * none is specified. │ │ │ │ + * Method: toString │ │ │ │ + * Convert projection to string (getCode wrapper). │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The projection code. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + toString: function() { │ │ │ │ + return this.getCode(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Clones this rule. │ │ │ │ - * │ │ │ │ + * Method: equals │ │ │ │ + * Test equality of two projection instances. Determines equality based │ │ │ │ + * soley on the projection code. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Rule>} Clone of this rule. │ │ │ │ + * {Boolean} The two projections are equivalent. │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ + equals: function(projection) { │ │ │ │ + var p = projection, │ │ │ │ + equals = false; │ │ │ │ + if (p) { │ │ │ │ + if (!(p instanceof OpenLayers.Projection)) { │ │ │ │ + p = new OpenLayers.Projection(p); │ │ │ │ } │ │ │ │ - } 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; │ │ │ │ - } │ │ │ │ + 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; │ │ │ │ } │ │ │ │ } │ │ │ │ - // 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 equals; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Rule" │ │ │ │ + /* Method: destroy │ │ │ │ + * Destroy projection object. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + delete this.proj; │ │ │ │ + delete this.projCode; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Projection" │ │ │ │ }); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Property: transforms │ │ │ │ + * {Object} Transforms is an object, with from properties, each of which may │ │ │ │ + * have a to property. This allows you to define projections without │ │ │ │ + * requiring support for proj4js to be included. │ │ │ │ + * │ │ │ │ + * This object has keys which correspond to a 'source' projection object. The │ │ │ │ + * keys should be strings, corresponding to the projection.getCode() value. │ │ │ │ + * Each source projection object should have a set of destination projection │ │ │ │ + * keys included in the object. │ │ │ │ + * │ │ │ │ + * Each value in the destination object should be a transformation function, │ │ │ │ + * where the function is expected to be passed an object with a .x and a .y │ │ │ │ + * property. The function should return the object, with the .x and .y │ │ │ │ + * transformed according to the transformation function. │ │ │ │ + * │ │ │ │ + * Note - Properties on this object should not be set directly. To add a │ │ │ │ + * transform method to this object, use the <addTransform> method. For an │ │ │ │ + * example of usage, see the OpenLayers.Layer.SphericalMercator file. │ │ │ │ + */ │ │ │ │ +OpenLayers.Projection.transforms = {}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * APIProperty: defaults │ │ │ │ + * {Object} Defaults for the SRS codes known to OpenLayers (currently │ │ │ │ + * EPSG:4326, CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857, │ │ │ │ + * EPSG:102113 and EPSG:102100). Keys are the SRS code, values are units, │ │ │ │ + * maxExtent (the validity extent for the SRS) and yx (true if this SRS is │ │ │ │ + * known to have a reverse axis order). │ │ │ │ + */ │ │ │ │ +OpenLayers.Projection.defaults = { │ │ │ │ + "EPSG:4326": { │ │ │ │ + units: "degrees", │ │ │ │ + maxExtent: [-180, -90, 180, 90], │ │ │ │ + yx: true │ │ │ │ + }, │ │ │ │ + "CRS:84": { │ │ │ │ + units: "degrees", │ │ │ │ + maxExtent: [-180, -90, 180, 90] │ │ │ │ + }, │ │ │ │ + "EPSG:900913": { │ │ │ │ + units: "m", │ │ │ │ + maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34] │ │ │ │ + } │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * APIMethod: addTransform │ │ │ │ + * Set a custom transform method between two projections. Use this method in │ │ │ │ + * cases where the proj4js lib is not available or where custom projections │ │ │ │ + * need to be handled. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * from - {String} The code for the source projection │ │ │ │ + * to - {String} the code for the destination projection │ │ │ │ + * method - {Function} A function that takes a point as an argument and │ │ │ │ + * transforms that point from the source to the destination projection │ │ │ │ + * in place. The original point should be modified. │ │ │ │ + */ │ │ │ │ +OpenLayers.Projection.addTransform = function(from, to, method) { │ │ │ │ + if (method === OpenLayers.Projection.nullTransform) { │ │ │ │ + var defaults = OpenLayers.Projection.defaults[from]; │ │ │ │ + if (defaults && !OpenLayers.Projection.defaults[to]) { │ │ │ │ + OpenLayers.Projection.defaults[to] = defaults; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (!OpenLayers.Projection.transforms[from]) { │ │ │ │ + OpenLayers.Projection.transforms[from] = {}; │ │ │ │ + } │ │ │ │ + OpenLayers.Projection.transforms[from][to] = method; │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * APIMethod: transform │ │ │ │ + * Transform a point coordinate from one projection to another. Note that │ │ │ │ + * the input point is transformed in place. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point> | Object} An object with x and y │ │ │ │ + * properties representing coordinates in those dimensions. │ │ │ │ + * source - {OpenLayers.Projection} Source map coordinate system │ │ │ │ + * dest - {OpenLayers.Projection} Destination map coordinate system │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * point - {object} A transformed coordinate. The original point is modified. │ │ │ │ + */ │ │ │ │ +OpenLayers.Projection.transform = function(point, source, dest) { │ │ │ │ + if (source && dest) { │ │ │ │ + if (!(source instanceof OpenLayers.Projection)) { │ │ │ │ + source = new OpenLayers.Projection(source); │ │ │ │ + } │ │ │ │ + if (!(dest instanceof OpenLayers.Projection)) { │ │ │ │ + dest = new OpenLayers.Projection(dest); │ │ │ │ + } │ │ │ │ + if (source.proj && dest.proj) { │ │ │ │ + point = Proj4js.transform(source.proj, dest.proj, point); │ │ │ │ + } else { │ │ │ │ + var sourceCode = source.getCode(); │ │ │ │ + var destCode = dest.getCode(); │ │ │ │ + var transforms = OpenLayers.Projection.transforms; │ │ │ │ + if (transforms[sourceCode] && transforms[sourceCode][destCode]) { │ │ │ │ + transforms[sourceCode][destCode](point); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return point; │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * APIFunction: nullTransform │ │ │ │ + * A null transformation - useful for defining projection aliases when │ │ │ │ + * proj4js is not available: │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * OpenLayers.Projection.addTransform("EPSG:3857", "EPSG:900913", │ │ │ │ + * OpenLayers.Projection.nullTransform); │ │ │ │ + * OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:3857", │ │ │ │ + * OpenLayers.Projection.nullTransform); │ │ │ │ + * (end) │ │ │ │ + */ │ │ │ │ +OpenLayers.Projection.nullTransform = function(point) { │ │ │ │ + return point; │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Note: Transforms for web mercator <-> geographic │ │ │ │ + * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100. │ │ │ │ + * OpenLayers originally started referring to EPSG:900913 as web mercator. │ │ │ │ + * The EPSG has declared EPSG:3857 to be web mercator. │ │ │ │ + * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as │ │ │ │ + * equivalent. See http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2009/11/20/ArcGIS-Online-moving-to-Google-_2F00_-Bing-tiling-scheme_3A00_-What-does-this-mean-for-you_3F00_.aspx#12084. │ │ │ │ + * For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and │ │ │ │ + * urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis │ │ │ │ + * order for EPSG:4326. │ │ │ │ + */ │ │ │ │ +(function() { │ │ │ │ + │ │ │ │ + var pole = 20037508.34; │ │ │ │ + │ │ │ │ + function inverseMercator(xy) { │ │ │ │ + xy.x = 180 * xy.x / pole; │ │ │ │ + xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2); │ │ │ │ + return xy; │ │ │ │ + } │ │ │ │ + │ │ │ │ + function forwardMercator(xy) { │ │ │ │ + xy.x = xy.x * pole / 180; │ │ │ │ + var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole; │ │ │ │ + xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34)); │ │ │ │ + return xy; │ │ │ │ + } │ │ │ │ + │ │ │ │ + function map(base, codes) { │ │ │ │ + var add = OpenLayers.Projection.addTransform; │ │ │ │ + var same = OpenLayers.Projection.nullTransform; │ │ │ │ + var i, len, code, other, j; │ │ │ │ + for (i = 0, len = codes.length; i < len; ++i) { │ │ │ │ + code = codes[i]; │ │ │ │ + add(base, code, forwardMercator); │ │ │ │ + add(code, base, inverseMercator); │ │ │ │ + for (j = i + 1; j < len; ++j) { │ │ │ │ + other = codes[j]; │ │ │ │ + add(code, other, same); │ │ │ │ + add(other, code, same); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // list of equivalent codes for web mercator │ │ │ │ + var mercator = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"], │ │ │ │ + geographic = ["CRS:84", "urn:ogc:def:crs:EPSG:6.6:4326", "EPSG:4326"], │ │ │ │ + i; │ │ │ │ + for (i = mercator.length - 1; i >= 0; --i) { │ │ │ │ + map(mercator[i], geographic); │ │ │ │ + } │ │ │ │ + for (i = geographic.length - 1; i >= 0; --i) { │ │ │ │ + map(geographic[i], mercator); │ │ │ │ + } │ │ │ │ + │ │ │ │ +})(); │ │ │ │ /* ====================================================================== │ │ │ │ OpenLayers/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 │ │ │ │ @@ -7650,342 +7903,14 @@ │ │ │ │ if ((t /= d / 2) < 1) return c / 2 * t * t + b; │ │ │ │ return -c / 2 * ((--t) * (t - 2) - 1) + b; │ │ │ │ }, │ │ │ │ │ │ │ │ CLASS_NAME: "OpenLayers.Easing.Quad" │ │ │ │ }; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Projection.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Namespace: OpenLayers.Projection │ │ │ │ - * Methods for coordinate transforms between coordinate systems. By default, │ │ │ │ - * OpenLayers ships with the ability to transform coordinates between │ │ │ │ - * geographic (EPSG:4326) and web or spherical mercator (EPSG:900913 et al.) │ │ │ │ - * coordinate reference systems. See the <transform> method for details │ │ │ │ - * on usage. │ │ │ │ - * │ │ │ │ - * Additional transforms may be added by using the <proj4js at http://proj4js.org/> │ │ │ │ - * library. If the proj4js library is included, the <transform> method │ │ │ │ - * will work between any two coordinate reference systems with proj4js │ │ │ │ - * definitions. │ │ │ │ - * │ │ │ │ - * If the proj4js library is not included, or if you wish to allow transforms │ │ │ │ - * between arbitrary coordinate reference systems, use the <addTransform> │ │ │ │ - * method to register a custom transform method. │ │ │ │ - */ │ │ │ │ -OpenLayers.Projection = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: proj │ │ │ │ - * {Object} Proj4js.Proj instance. │ │ │ │ - */ │ │ │ │ - proj: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: projCode │ │ │ │ - * {String} │ │ │ │ - */ │ │ │ │ - projCode: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: titleRegEx │ │ │ │ - * {RegExp} regular expression to strip the title from a proj4js definition │ │ │ │ - */ │ │ │ │ - titleRegEx: /\+title=[^\+]*/, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Projection │ │ │ │ - * This class offers several methods for interacting with a wrapped │ │ │ │ - * pro4js projection object. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * projCode - {String} A string identifying the Well Known Identifier for │ │ │ │ - * the projection. │ │ │ │ - * options - {Object} An optional object to set additional properties │ │ │ │ - * on the projection. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Projection>} A projection object. │ │ │ │ - */ │ │ │ │ - initialize: function(projCode, options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.projCode = projCode; │ │ │ │ - if (typeof Proj4js == "object") { │ │ │ │ - this.proj = new Proj4js.Proj(projCode); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getCode │ │ │ │ - * Get the string SRS code. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The SRS code. │ │ │ │ - */ │ │ │ │ - getCode: function() { │ │ │ │ - return this.proj ? this.proj.srsCode : this.projCode; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getUnits │ │ │ │ - * Get the units string for the projection -- returns null if │ │ │ │ - * proj4js is not available. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The units abbreviation. │ │ │ │ - */ │ │ │ │ - getUnits: function() { │ │ │ │ - return this.proj ? this.proj.units : null; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: toString │ │ │ │ - * Convert projection to string (getCode wrapper). │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The projection code. │ │ │ │ - */ │ │ │ │ - toString: function() { │ │ │ │ - return this.getCode(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: equals │ │ │ │ - * Test equality of two projection instances. Determines equality based │ │ │ │ - * soley on the projection code. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The two projections are equivalent. │ │ │ │ - */ │ │ │ │ - equals: function(projection) { │ │ │ │ - var p = projection, │ │ │ │ - equals = false; │ │ │ │ - if (p) { │ │ │ │ - if (!(p instanceof OpenLayers.Projection)) { │ │ │ │ - p = new OpenLayers.Projection(p); │ │ │ │ - } │ │ │ │ - if ((typeof Proj4js == "object") && this.proj.defData && p.proj.defData) { │ │ │ │ - equals = this.proj.defData.replace(this.titleRegEx, "") == │ │ │ │ - p.proj.defData.replace(this.titleRegEx, ""); │ │ │ │ - } else if (p.getCode) { │ │ │ │ - var source = this.getCode(), │ │ │ │ - target = p.getCode(); │ │ │ │ - equals = source == target || │ │ │ │ - !!OpenLayers.Projection.transforms[source] && │ │ │ │ - OpenLayers.Projection.transforms[source][target] === │ │ │ │ - OpenLayers.Projection.nullTransform; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return equals; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /* Method: destroy │ │ │ │ - * Destroy projection object. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - delete this.proj; │ │ │ │ - delete this.projCode; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Projection" │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Property: transforms │ │ │ │ - * {Object} Transforms is an object, with from properties, each of which may │ │ │ │ - * have a to property. This allows you to define projections without │ │ │ │ - * requiring support for proj4js to be included. │ │ │ │ - * │ │ │ │ - * This object has keys which correspond to a 'source' projection object. The │ │ │ │ - * keys should be strings, corresponding to the projection.getCode() value. │ │ │ │ - * Each source projection object should have a set of destination projection │ │ │ │ - * keys included in the object. │ │ │ │ - * │ │ │ │ - * Each value in the destination object should be a transformation function, │ │ │ │ - * where the function is expected to be passed an object with a .x and a .y │ │ │ │ - * property. The function should return the object, with the .x and .y │ │ │ │ - * transformed according to the transformation function. │ │ │ │ - * │ │ │ │ - * Note - Properties on this object should not be set directly. To add a │ │ │ │ - * transform method to this object, use the <addTransform> method. For an │ │ │ │ - * example of usage, see the OpenLayers.Layer.SphericalMercator file. │ │ │ │ - */ │ │ │ │ -OpenLayers.Projection.transforms = {}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * APIProperty: defaults │ │ │ │ - * {Object} Defaults for the SRS codes known to OpenLayers (currently │ │ │ │ - * EPSG:4326, CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, EPSG:900913, EPSG:3857, │ │ │ │ - * EPSG:102113 and EPSG:102100). Keys are the SRS code, values are units, │ │ │ │ - * maxExtent (the validity extent for the SRS) and yx (true if this SRS is │ │ │ │ - * known to have a reverse axis order). │ │ │ │ - */ │ │ │ │ -OpenLayers.Projection.defaults = { │ │ │ │ - "EPSG:4326": { │ │ │ │ - units: "degrees", │ │ │ │ - maxExtent: [-180, -90, 180, 90], │ │ │ │ - yx: true │ │ │ │ - }, │ │ │ │ - "CRS:84": { │ │ │ │ - units: "degrees", │ │ │ │ - maxExtent: [-180, -90, 180, 90] │ │ │ │ - }, │ │ │ │ - "EPSG:900913": { │ │ │ │ - units: "m", │ │ │ │ - maxExtent: [-20037508.34, -20037508.34, 20037508.34, 20037508.34] │ │ │ │ - } │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * APIMethod: addTransform │ │ │ │ - * Set a custom transform method between two projections. Use this method in │ │ │ │ - * cases where the proj4js lib is not available or where custom projections │ │ │ │ - * need to be handled. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * from - {String} The code for the source projection │ │ │ │ - * to - {String} the code for the destination projection │ │ │ │ - * method - {Function} A function that takes a point as an argument and │ │ │ │ - * transforms that point from the source to the destination projection │ │ │ │ - * in place. The original point should be modified. │ │ │ │ - */ │ │ │ │ -OpenLayers.Projection.addTransform = function(from, to, method) { │ │ │ │ - if (method === OpenLayers.Projection.nullTransform) { │ │ │ │ - var defaults = OpenLayers.Projection.defaults[from]; │ │ │ │ - if (defaults && !OpenLayers.Projection.defaults[to]) { │ │ │ │ - OpenLayers.Projection.defaults[to] = defaults; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (!OpenLayers.Projection.transforms[from]) { │ │ │ │ - OpenLayers.Projection.transforms[from] = {}; │ │ │ │ - } │ │ │ │ - OpenLayers.Projection.transforms[from][to] = method; │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * APIMethod: transform │ │ │ │ - * Transform a point coordinate from one projection to another. Note that │ │ │ │ - * the input point is transformed in place. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point> | Object} An object with x and y │ │ │ │ - * properties representing coordinates in those dimensions. │ │ │ │ - * source - {OpenLayers.Projection} Source map coordinate system │ │ │ │ - * dest - {OpenLayers.Projection} Destination map coordinate system │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * point - {object} A transformed coordinate. The original point is modified. │ │ │ │ - */ │ │ │ │ -OpenLayers.Projection.transform = function(point, source, dest) { │ │ │ │ - if (source && dest) { │ │ │ │ - if (!(source instanceof OpenLayers.Projection)) { │ │ │ │ - source = new OpenLayers.Projection(source); │ │ │ │ - } │ │ │ │ - if (!(dest instanceof OpenLayers.Projection)) { │ │ │ │ - dest = new OpenLayers.Projection(dest); │ │ │ │ - } │ │ │ │ - if (source.proj && dest.proj) { │ │ │ │ - point = Proj4js.transform(source.proj, dest.proj, point); │ │ │ │ - } else { │ │ │ │ - var sourceCode = source.getCode(); │ │ │ │ - var destCode = dest.getCode(); │ │ │ │ - var transforms = OpenLayers.Projection.transforms; │ │ │ │ - if (transforms[sourceCode] && transforms[sourceCode][destCode]) { │ │ │ │ - transforms[sourceCode][destCode](point); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return point; │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * APIFunction: nullTransform │ │ │ │ - * A null transformation - useful for defining projection aliases when │ │ │ │ - * proj4js is not available: │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * OpenLayers.Projection.addTransform("EPSG:3857", "EPSG:900913", │ │ │ │ - * OpenLayers.Projection.nullTransform); │ │ │ │ - * OpenLayers.Projection.addTransform("EPSG:900913", "EPSG:3857", │ │ │ │ - * OpenLayers.Projection.nullTransform); │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ -OpenLayers.Projection.nullTransform = function(point) { │ │ │ │ - return point; │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Note: Transforms for web mercator <-> geographic │ │ │ │ - * OpenLayers recognizes EPSG:3857, EPSG:900913, EPSG:102113 and EPSG:102100. │ │ │ │ - * OpenLayers originally started referring to EPSG:900913 as web mercator. │ │ │ │ - * The EPSG has declared EPSG:3857 to be web mercator. │ │ │ │ - * ArcGIS 10 recognizes the EPSG:3857, EPSG:102113, and EPSG:102100 as │ │ │ │ - * equivalent. See http://blogs.esri.com/Dev/blogs/arcgisserver/archive/2009/11/20/ArcGIS-Online-moving-to-Google-_2F00_-Bing-tiling-scheme_3A00_-What-does-this-mean-for-you_3F00_.aspx#12084. │ │ │ │ - * For geographic, OpenLayers recognizes EPSG:4326, CRS:84 and │ │ │ │ - * urn:ogc:def:crs:EPSG:6.6:4326. OpenLayers also knows about the reverse axis │ │ │ │ - * order for EPSG:4326. │ │ │ │ - */ │ │ │ │ -(function() { │ │ │ │ - │ │ │ │ - var pole = 20037508.34; │ │ │ │ - │ │ │ │ - function inverseMercator(xy) { │ │ │ │ - xy.x = 180 * xy.x / pole; │ │ │ │ - xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp((xy.y / pole) * Math.PI)) - Math.PI / 2); │ │ │ │ - return xy; │ │ │ │ - } │ │ │ │ - │ │ │ │ - function forwardMercator(xy) { │ │ │ │ - xy.x = xy.x * pole / 180; │ │ │ │ - var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole; │ │ │ │ - xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34)); │ │ │ │ - return xy; │ │ │ │ - } │ │ │ │ - │ │ │ │ - function map(base, codes) { │ │ │ │ - var add = OpenLayers.Projection.addTransform; │ │ │ │ - var same = OpenLayers.Projection.nullTransform; │ │ │ │ - var i, len, code, other, j; │ │ │ │ - for (i = 0, len = codes.length; i < len; ++i) { │ │ │ │ - code = codes[i]; │ │ │ │ - add(base, code, forwardMercator); │ │ │ │ - add(code, base, inverseMercator); │ │ │ │ - for (j = i + 1; j < len; ++j) { │ │ │ │ - other = codes[j]; │ │ │ │ - add(code, other, same); │ │ │ │ - add(other, code, same); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - // list of equivalent codes for web mercator │ │ │ │ - var mercator = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"], │ │ │ │ - geographic = ["CRS:84", "urn:ogc:def:crs:EPSG:6.6:4326", "EPSG:4326"], │ │ │ │ - i; │ │ │ │ - for (i = mercator.length - 1; i >= 0; --i) { │ │ │ │ - map(mercator[i], geographic); │ │ │ │ - } │ │ │ │ - for (i = geographic.length - 1; i >= 0; --i) { │ │ │ │ - map(geographic[i], mercator); │ │ │ │ - } │ │ │ │ - │ │ │ │ -})(); │ │ │ │ -/* ====================================================================== │ │ │ │ OpenLayers/Map.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ @@ -10893,179 +10818,14 @@ │ │ │ │ OpenLayers.Map.TILE_WIDTH = 256; │ │ │ │ /** │ │ │ │ * Constant: TILE_HEIGHT │ │ │ │ * {Integer} 256 Default tile height (unless otherwise specified) │ │ │ │ */ │ │ │ │ OpenLayers.Map.TILE_HEIGHT = 256; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/StyleMap.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Style.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.StyleMap │ │ │ │ - */ │ │ │ │ -OpenLayers.StyleMap = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: styles │ │ │ │ - * {Object} Hash of {<OpenLayers.Style>}, keyed by names of well known │ │ │ │ - * rendering intents (e.g. "default", "temporary", "select", "delete"). │ │ │ │ - */ │ │ │ │ - styles: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: extendDefault │ │ │ │ - * {Boolean} if true, every render intent will extend the symbolizers │ │ │ │ - * specified for the "default" intent at rendering time. Otherwise, every │ │ │ │ - * rendering intent will be treated as a completely independent style. │ │ │ │ - */ │ │ │ │ - extendDefault: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.StyleMap │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * style - {Object} Optional. Either a style hash, or a style object, or │ │ │ │ - * a hash of style objects (style hashes) keyed by rendering │ │ │ │ - * intent. If just one style hash or style object is passed, │ │ │ │ - * this will be used for all known render intents (default, │ │ │ │ - * select, temporary) │ │ │ │ - * options - {Object} optional hash of additional options for this │ │ │ │ - * instance │ │ │ │ - */ │ │ │ │ - initialize: function(style, options) { │ │ │ │ - this.styles = { │ │ │ │ - "default": new OpenLayers.Style( │ │ │ │ - OpenLayers.Feature.Vector.style["default"]), │ │ │ │ - "select": new OpenLayers.Style( │ │ │ │ - OpenLayers.Feature.Vector.style["select"]), │ │ │ │ - "temporary": new OpenLayers.Style( │ │ │ │ - OpenLayers.Feature.Vector.style["temporary"]), │ │ │ │ - "delete": new OpenLayers.Style( │ │ │ │ - OpenLayers.Feature.Vector.style["delete"]) │ │ │ │ - }; │ │ │ │ - │ │ │ │ - // take whatever the user passed as style parameter and convert it │ │ │ │ - // into parts of stylemap. │ │ │ │ - if (style instanceof OpenLayers.Style) { │ │ │ │ - // user passed a style object │ │ │ │ - this.styles["default"] = style; │ │ │ │ - this.styles["select"] = style; │ │ │ │ - this.styles["temporary"] = style; │ │ │ │ - this.styles["delete"] = style; │ │ │ │ - } else if (typeof style == "object") { │ │ │ │ - for (var key in style) { │ │ │ │ - if (style[key] instanceof OpenLayers.Style) { │ │ │ │ - // user passed a hash of style objects │ │ │ │ - this.styles[key] = style[key]; │ │ │ │ - } else if (typeof style[key] == "object") { │ │ │ │ - // user passsed a hash of style hashes │ │ │ │ - this.styles[key] = new OpenLayers.Style(style[key]); │ │ │ │ - } else { │ │ │ │ - // user passed a style hash (i.e. symbolizer) │ │ │ │ - this.styles["default"] = new OpenLayers.Style(style); │ │ │ │ - this.styles["select"] = new OpenLayers.Style(style); │ │ │ │ - this.styles["temporary"] = new OpenLayers.Style(style); │ │ │ │ - this.styles["delete"] = new OpenLayers.Style(style); │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - for (var key in this.styles) { │ │ │ │ - this.styles[key].destroy(); │ │ │ │ - } │ │ │ │ - this.styles = null; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: createSymbolizer │ │ │ │ - * Creates the symbolizer for a feature for a render intent. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature>} The feature to evaluate the rules │ │ │ │ - * of the intended style against. │ │ │ │ - * intent - {String} The intent determines the symbolizer that will be │ │ │ │ - * used to draw the feature. Well known intents are "default" │ │ │ │ - * (for just drawing the features), "select" (for selected │ │ │ │ - * features) and "temporary" (for drawing features). │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} symbolizer hash │ │ │ │ - */ │ │ │ │ - createSymbolizer: function(feature, intent) { │ │ │ │ - if (!feature) { │ │ │ │ - feature = new OpenLayers.Feature.Vector(); │ │ │ │ - } │ │ │ │ - if (!this.styles[intent]) { │ │ │ │ - intent = "default"; │ │ │ │ - } │ │ │ │ - feature.renderIntent = intent; │ │ │ │ - var defaultSymbolizer = {}; │ │ │ │ - if (this.extendDefault && intent != "default") { │ │ │ │ - defaultSymbolizer = this.styles["default"].createSymbolizer(feature); │ │ │ │ - } │ │ │ │ - return OpenLayers.Util.extend(defaultSymbolizer, │ │ │ │ - this.styles[intent].createSymbolizer(feature)); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: addUniqueValueRules │ │ │ │ - * Convenience method to create comparison rules for unique values of a │ │ │ │ - * property. The rules will be added to the style object for a specified │ │ │ │ - * rendering intent. This method is a shortcut for creating something like │ │ │ │ - * the "unique value legends" familiar from well known desktop GIS systems │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * renderIntent - {String} rendering intent to add the rules to │ │ │ │ - * property - {String} values of feature attributes to create the │ │ │ │ - * rules for │ │ │ │ - * symbolizers - {Object} Hash of symbolizers, keyed by the desired │ │ │ │ - * property values │ │ │ │ - * context - {Object} An optional object with properties that │ │ │ │ - * symbolizers' property values should be evaluated │ │ │ │ - * against. If no context is specified, feature.attributes │ │ │ │ - * will be used │ │ │ │ - */ │ │ │ │ - addUniqueValueRules: function(renderIntent, property, symbolizers, context) { │ │ │ │ - var rules = []; │ │ │ │ - for (var value in symbolizers) { │ │ │ │ - rules.push(new OpenLayers.Rule({ │ │ │ │ - symbolizer: symbolizers[value], │ │ │ │ - context: context, │ │ │ │ - filter: new OpenLayers.Filter.Comparison({ │ │ │ │ - type: OpenLayers.Filter.Comparison.EQUAL_TO, │ │ │ │ - property: property, │ │ │ │ - value: value │ │ │ │ - }) │ │ │ │ - })); │ │ │ │ - } │ │ │ │ - this.styles[renderIntent].addRules(rules); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.StyleMap" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ OpenLayers/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. */ │ │ │ │ @@ -11246,26273 +11006,26513 @@ │ │ │ │ OpenLayers.Function.bind(timerCallback, this) │ │ │ │ ); │ │ │ │ }, │ │ │ │ │ │ │ │ CLASS_NAME: "OpenLayers.Kinetic" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer.js │ │ │ │ + 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/Map.js │ │ │ │ - * @requires OpenLayers/Projection.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ + * @requires OpenLayers/Style.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer │ │ │ │ + * Class: OpenLayers.Rule │ │ │ │ + * This class represents an SLD Rule, as being used for rule-based SLD styling. │ │ │ │ */ │ │ │ │ -OpenLayers.Layer = OpenLayers.Class({ │ │ │ │ +OpenLayers.Rule = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: id │ │ │ │ - * {String} │ │ │ │ + * Property: id │ │ │ │ + * {String} A unique id for this session. │ │ │ │ */ │ │ │ │ id: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ + /** │ │ │ │ * APIProperty: name │ │ │ │ - * {String} │ │ │ │ + * {String} name of this rule │ │ │ │ */ │ │ │ │ name: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: div │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - div: null, │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIProperty: opacity │ │ │ │ - * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default │ │ │ │ - * is 1. │ │ │ │ + * Property: title │ │ │ │ + * {String} Title of this rule (set if included in SLD) │ │ │ │ */ │ │ │ │ - opacity: 1, │ │ │ │ + title: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Property: description │ │ │ │ + * {String} Description of this rule (set if abstract is included in SLD) │ │ │ │ */ │ │ │ │ - alwaysInRange: null, │ │ │ │ + description: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: RESOLUTION_PROPERTIES │ │ │ │ - * {Array} The properties that are used for calculating resolutions │ │ │ │ - * information. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - RESOLUTION_PROPERTIES: [ │ │ │ │ - 'scales', 'resolutions', │ │ │ │ - 'maxScale', 'minScale', │ │ │ │ - 'maxResolution', 'minResolution', │ │ │ │ - 'numZoomLevels', 'maxZoomLevel' │ │ │ │ - ], │ │ │ │ + context: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * layer.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Listeners will be called with a reference to an event object. The │ │ │ │ - * properties of this event depends on exactly what happened. │ │ │ │ - * │ │ │ │ - * All event objects have at least the following properties: │ │ │ │ - * object - {Object} A reference to layer.events.object. │ │ │ │ - * element - {DOMElement} A reference to layer.events.element. │ │ │ │ - * │ │ │ │ - * Supported map event types: │ │ │ │ - * loadstart - Triggered when layer loading starts. When using a Vector │ │ │ │ - * layer with a Fixed or BBOX strategy, the event object includes │ │ │ │ - * a *filter* property holding the OpenLayers.Filter used when │ │ │ │ - * calling read on the protocol. │ │ │ │ - * loadend - Triggered when layer loading ends. When using a Vector layer │ │ │ │ - * with a Fixed or BBOX strategy, the event object includes a │ │ │ │ - * *response* property holding an OpenLayers.Protocol.Response object. │ │ │ │ - * visibilitychanged - Triggered when the layer's visibility property is │ │ │ │ - * changed, e.g. by turning the layer on or off in the layer switcher. │ │ │ │ - * Note that the actual visibility of the layer can also change if it │ │ │ │ - * gets out of range (see <calculateInRange>). If you also want to catch │ │ │ │ - * these cases, register for the map's 'changelayer' event instead. │ │ │ │ - * move - Triggered when layer moves (triggered with every mousemove │ │ │ │ - * during a drag). │ │ │ │ - * moveend - Triggered when layer is done moving, object passed as │ │ │ │ - * argument has a zoomChanged boolean property which tells that the │ │ │ │ - * zoom has changed. │ │ │ │ - * added - Triggered after the layer is added to a map. Listeners will │ │ │ │ - * receive an object with a *map* property referencing the map and a │ │ │ │ - * *layer* property referencing the layer. │ │ │ │ - * removed - Triggered after the layer is removed from the map. Listeners │ │ │ │ - * will receive an object with a *map* property referencing the map and │ │ │ │ - * a *layer* property referencing the layer. │ │ │ │ + * Property: filter │ │ │ │ + * {<OpenLayers.Filter>} Optional filter for the rule. │ │ │ │ */ │ │ │ │ - events: null, │ │ │ │ + filter: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: map │ │ │ │ - * {<OpenLayers.Map>} This variable is set when the layer is added to │ │ │ │ - * the map, via the accessor function setMap(). │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - map: null, │ │ │ │ + elseFilter: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} Whether or not the layer is a base layer. This should be set │ │ │ │ - * individually by all subclasses. Default is 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. │ │ │ │ */ │ │ │ │ - isBaseLayer: false, │ │ │ │ + symbolizer: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - displayInLayerSwitcher: true, │ │ │ │ + symbolizers: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: visibility │ │ │ │ - * {Boolean} The layer should be displayed in the map. Default is true. │ │ │ │ + * 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}" │ │ │ │ */ │ │ │ │ - visibility: true, │ │ │ │ + minScaleDenominator: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: attribution │ │ │ │ - * {String} Attribution string, displayed when an │ │ │ │ - * <OpenLayers.Control.Attribution> has been added to the map. │ │ │ │ + * 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}" │ │ │ │ */ │ │ │ │ - attribution: null, │ │ │ │ + maxScaleDenominator: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: inRange │ │ │ │ - * {Boolean} The current map resolution is within the layer's min/max │ │ │ │ - * range. This is set in <OpenLayers.Map.setCenter> whenever the zoom │ │ │ │ - * changes. │ │ │ │ - */ │ │ │ │ - inRange: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Propery: imageSize │ │ │ │ - * {<OpenLayers.Size>} For layers with a gutter, the image is larger than │ │ │ │ - * the tile by twice the gutter in each dimension. │ │ │ │ + * Constructor: OpenLayers.Rule │ │ │ │ + * Creates a Rule. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object with properties to set on the │ │ │ │ + * rule │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Rule>} │ │ │ │ */ │ │ │ │ - imageSize: null, │ │ │ │ - │ │ │ │ - // OPTIONS │ │ │ │ + initialize: function(options) { │ │ │ │ + this.symbolizer = {}; │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + if (this.symbolizers) { │ │ │ │ + delete this.symbolizer; │ │ │ │ + } │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: options │ │ │ │ - * {Object} An optional object whose properties will be set on the layer. │ │ │ │ - * Any of the layer properties can be set as a property of the options │ │ │ │ - * object and sent to the constructor when the layer is created. │ │ │ │ - */ │ │ │ │ - options: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: eventListeners │ │ │ │ - * {Object} If set as an option at construction, the eventListeners │ │ │ │ - * object will be registered with <OpenLayers.Events.on>. Object │ │ │ │ - * structure must be a listeners object as shown in the example for │ │ │ │ - * the events.on method. │ │ │ │ - */ │ │ │ │ - eventListeners: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: gutter │ │ │ │ - * {Integer} Determines the width (in pixels) of the gutter around image │ │ │ │ - * tiles to ignore. By setting this property to a non-zero value, │ │ │ │ - * images will be requested that are wider and taller than the tile │ │ │ │ - * size by a value of 2 x gutter. This allows artifacts of rendering │ │ │ │ - * at tile edges to be ignored. Set a gutter value that is equal to │ │ │ │ - * half the size of the widest symbol that needs to be displayed. │ │ │ │ - * Defaults to zero. Non-tiled layers always have zero gutter. │ │ │ │ + * APIMethod: destroy │ │ │ │ + * nullify references to prevent circular references and memory leaks │ │ │ │ */ │ │ │ │ - gutter: 0, │ │ │ │ + destroy: function() { │ │ │ │ + for (var i in this.symbolizer) { │ │ │ │ + this.symbolizer[i] = null; │ │ │ │ + } │ │ │ │ + this.symbolizer = null; │ │ │ │ + delete this.symbolizers; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: projection │ │ │ │ - * {<OpenLayers.Projection>} or {<String>} Specifies the projection of the layer. │ │ │ │ - * Can be set in the layer options. If not specified in the layer options, │ │ │ │ - * it is set to the default projection specified in the map, │ │ │ │ - * when the layer is added to the map. │ │ │ │ - * Projection along with default maxExtent and resolutions │ │ │ │ - * are set automatically with commercial baselayers in EPSG:3857, │ │ │ │ - * such as Google, Bing and OpenStreetMap, and do not need to be specified. │ │ │ │ - * Otherwise, if specifying projection, also set maxExtent, │ │ │ │ - * maxResolution or resolutions as appropriate. │ │ │ │ - * When using vector layers with strategies, layer projection should be set │ │ │ │ - * to the projection of the source data if that is different from the map default. │ │ │ │ + * APIMethod: evaluate │ │ │ │ + * evaluates this rule for a specific feature │ │ │ │ * │ │ │ │ - * Can be either a string or an <OpenLayers.Projection> object; │ │ │ │ - * if a string is passed, will be converted to an object when │ │ │ │ - * the layer is added to the map. │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature>} feature to apply the rule to. │ │ │ │ * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true if the rule applies, false if it does not. │ │ │ │ + * This rule is the default rule and always returns true. │ │ │ │ */ │ │ │ │ - projection: null, │ │ │ │ + evaluate: function(feature) { │ │ │ │ + var context = this.getContext(feature); │ │ │ │ + var applies = true; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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, │ │ │ │ + if (this.minScaleDenominator || this.maxScaleDenominator) { │ │ │ │ + var scale = feature.layer.map.getScale(); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: scales │ │ │ │ - * {Array} An array of map scales in descending order. The values in the │ │ │ │ - * array correspond to the map scale denominator. Note that these │ │ │ │ - * values only make sense if the display (monitor) resolution of the │ │ │ │ - * client is correctly guessed by whomever is configuring the │ │ │ │ - * application. In addition, the units property must also be set. │ │ │ │ - * Use <resolutions> instead wherever possible. │ │ │ │ - */ │ │ │ │ - scales: null, │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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, │ │ │ │ + // 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; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: maxExtent │ │ │ │ - * {<OpenLayers.Bounds>|Array} If provided as an array, the array │ │ │ │ - * should consist of four values (left, bottom, right, top). │ │ │ │ - * The maximum extent for the layer. Defaults to null. │ │ │ │ + * Method: getContext │ │ │ │ + * Gets the context for evaluating this rule │ │ │ │ * │ │ │ │ - * The center of these bounds will not stray outside │ │ │ │ - * of the viewport extent during panning. In addition, if │ │ │ │ - * <displayOutsideMaxExtent> is set to false, data will not be │ │ │ │ - * requested that falls completely outside of these bounds. │ │ │ │ + * Paramters: │ │ │ │ + * feature - {<OpenLayers.Feature>} feature to take the context from if │ │ │ │ + * none is specified. │ │ │ │ */ │ │ │ │ - maxExtent: null, │ │ │ │ + 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; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: minExtent │ │ │ │ - * {<OpenLayers.Bounds>|Array} If provided as an array, the array │ │ │ │ - * should consist of four values (left, bottom, right, top). │ │ │ │ - * The minimum extent for the layer. Defaults to null. │ │ │ │ + * APIMethod: clone │ │ │ │ + * Clones this rule. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Rule>} Clone of this rule. │ │ │ │ */ │ │ │ │ - minExtent: 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; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + // 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); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: maxResolution │ │ │ │ - * {Float} Default max is 360 deg / 256 px, which corresponds to │ │ │ │ - * zoom level 0 on gmaps. Specify a different value in the layer │ │ │ │ - * options if you are not using the default <OpenLayers.Map.tileSize> │ │ │ │ - * and displaying the whole world. │ │ │ │ - */ │ │ │ │ - maxResolution: null, │ │ │ │ + CLASS_NAME: "OpenLayers.Rule" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Strategy.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Strategy │ │ │ │ + * Abstract vector layer strategy class. Not to be instantiated directly. Use │ │ │ │ + * one of the strategy subclasses instead. │ │ │ │ + */ │ │ │ │ +OpenLayers.Strategy = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: minResolution │ │ │ │ - * {Float} │ │ │ │ + * Property: layer │ │ │ │ + * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to. │ │ │ │ */ │ │ │ │ - minResolution: null, │ │ │ │ + layer: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: numZoomLevels │ │ │ │ - * {Integer} │ │ │ │ + * Property: options │ │ │ │ + * {Object} Any options sent to the constructor. │ │ │ │ */ │ │ │ │ - numZoomLevels: null, │ │ │ │ + options: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: minScale │ │ │ │ - * {Float} │ │ │ │ + /** │ │ │ │ + * Property: active │ │ │ │ + * {Boolean} The control is active. │ │ │ │ */ │ │ │ │ - minScale: null, │ │ │ │ + active: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: maxScale │ │ │ │ - * {Float} │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - maxScale: null, │ │ │ │ + autoActivate: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: displayOutsideMaxExtent │ │ │ │ - * {Boolean} Request map tiles that are completely outside of the max │ │ │ │ - * extent for this layer. Defaults to false. │ │ │ │ + * Property: autoDestroy │ │ │ │ + * {Boolean} The creator of the strategy can set autoDestroy to false │ │ │ │ + * to fully control when the strategy is destroyed. Defaults to │ │ │ │ + * true. │ │ │ │ */ │ │ │ │ - displayOutsideMaxExtent: false, │ │ │ │ + autoDestroy: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Constructor: OpenLayers.Strategy │ │ │ │ + * Abstract class for vector strategies. Create instances of a subclass. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ - wrapDateLine: false, │ │ │ │ + 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; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: metadata │ │ │ │ - * {Object} This object can be used to store additional information on a │ │ │ │ - * layer object. │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up the strategy. │ │ │ │ */ │ │ │ │ - metadata: null, │ │ │ │ + destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ + this.layer = null; │ │ │ │ + this.options = null; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer │ │ │ │ + * Method: setLayer │ │ │ │ + * Called to set the <layer> property. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} The layer name │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} │ │ │ │ */ │ │ │ │ - initialize: function(name, options) { │ │ │ │ - │ │ │ │ - this.metadata = {}; │ │ │ │ - │ │ │ │ - options = OpenLayers.Util.extend({}, options); │ │ │ │ - // make sure we respect alwaysInRange if set on the prototype │ │ │ │ - if (this.alwaysInRange != null) { │ │ │ │ - options.alwaysInRange = this.alwaysInRange; │ │ │ │ - } │ │ │ │ - this.addOptions(options); │ │ │ │ - │ │ │ │ - this.name = name; │ │ │ │ - │ │ │ │ - if (this.id == null) { │ │ │ │ - │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ - │ │ │ │ - this.div = OpenLayers.Util.createDiv(this.id); │ │ │ │ - this.div.style.width = "100%"; │ │ │ │ - this.div.style.height = "100%"; │ │ │ │ - this.div.dir = "ltr"; │ │ │ │ - │ │ │ │ - this.events = new OpenLayers.Events(this, this.div); │ │ │ │ - if (this.eventListeners instanceof Object) { │ │ │ │ - this.events.on(this.eventListeners); │ │ │ │ - } │ │ │ │ - │ │ │ │ - } │ │ │ │ + setLayer: function(layer) { │ │ │ │ + this.layer = layer; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: activate │ │ │ │ + * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * setNewBaseLayer - {Boolean} Set a new base layer when this layer has │ │ │ │ - * been destroyed. Default is true. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} True if the strategy was successfully activated or false if │ │ │ │ + * the strategy was already active. │ │ │ │ */ │ │ │ │ - destroy: function(setNewBaseLayer) { │ │ │ │ - if (setNewBaseLayer == null) { │ │ │ │ - setNewBaseLayer = true; │ │ │ │ - } │ │ │ │ - if (this.map != null) { │ │ │ │ - this.map.removeLayer(this, setNewBaseLayer); │ │ │ │ - } │ │ │ │ - this.projection = null; │ │ │ │ - this.map = null; │ │ │ │ - this.name = null; │ │ │ │ - this.div = null; │ │ │ │ - this.options = null; │ │ │ │ - │ │ │ │ - if (this.events) { │ │ │ │ - if (this.eventListeners) { │ │ │ │ - this.events.un(this.eventListeners); │ │ │ │ - } │ │ │ │ - this.events.destroy(); │ │ │ │ + activate: function() { │ │ │ │ + if (!this.active) { │ │ │ │ + this.active = true; │ │ │ │ + return true; │ │ │ │ } │ │ │ │ - this.eventListeners = null; │ │ │ │ - this.events = null; │ │ │ │ + return false; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clone │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {<OpenLayers.Layer>} The layer to be cloned │ │ │ │ + * Method: deactivate │ │ │ │ + * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ + * tear-down. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer> │ │ │ │ + * {Boolean} True if the strategy was successfully deactivated or false if │ │ │ │ + * the strategy was already inactive. │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer(this.name, this.getOptions()); │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + this.active = false; │ │ │ │ + return true; │ │ │ │ } │ │ │ │ + return false; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // catch any randomly tagged-on properties │ │ │ │ - OpenLayers.Util.applyDefaults(obj, this); │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Filter.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - // a cloned layer should never have its map property set │ │ │ │ - // because it has not been added to a map yet. │ │ │ │ - obj.map = 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. */ │ │ │ │ │ │ │ │ - return obj; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getOptions │ │ │ │ - * Extracts an object from the layer with the properties that were set as │ │ │ │ - * options, but updates them with the values currently set on the │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ + * @requires OpenLayers/Style.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Filter │ │ │ │ + * This class represents an OGC Filter. │ │ │ │ + */ │ │ │ │ +OpenLayers.Filter = OpenLayers.Class({ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Filter │ │ │ │ + * This class represents a generic filter. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ * instance. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} the <options> of the layer, representing the current state. │ │ │ │ + * {<OpenLayers.Filter>} │ │ │ │ */ │ │ │ │ - getOptions: function() { │ │ │ │ - var options = {}; │ │ │ │ - for (var o in this.options) { │ │ │ │ - options[o] = this[o]; │ │ │ │ - } │ │ │ │ - return options; │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, 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. │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Remove reference to anything added. │ │ │ │ */ │ │ │ │ - setName: function(newName) { │ │ │ │ - if (newName != this.name) { │ │ │ │ - this.name = newName; │ │ │ │ - if (this.map != null) { │ │ │ │ - this.map.events.triggerEvent("changelayer", { │ │ │ │ - layer: this, │ │ │ │ - property: "name" │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + destroy: function() {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: addOptions │ │ │ │ + * APIMethod: evaluate │ │ │ │ + * Evaluates this filter in a specific context. Instances or subclasses │ │ │ │ + * are supposed to override this method. │ │ │ │ * │ │ │ │ * 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. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - addOptions: function(newOptions, reinitialize) { │ │ │ │ - │ │ │ │ - if (this.options == null) { │ │ │ │ - this.options = {}; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (newOptions) { │ │ │ │ - // make sure this.projection references a projection object │ │ │ │ - if (typeof newOptions.projection == "string") { │ │ │ │ - newOptions.projection = new OpenLayers.Projection(newOptions.projection); │ │ │ │ - } │ │ │ │ - if (newOptions.projection) { │ │ │ │ - // get maxResolution, units and maxExtent from projection defaults if │ │ │ │ - // they are not defined already │ │ │ │ - OpenLayers.Util.applyDefaults(newOptions, │ │ │ │ - OpenLayers.Projection.defaults[newOptions.projection.getCode()]); │ │ │ │ - } │ │ │ │ - // allow array for extents │ │ │ │ - if (newOptions.maxExtent && !(newOptions.maxExtent instanceof OpenLayers.Bounds)) { │ │ │ │ - newOptions.maxExtent = new OpenLayers.Bounds(newOptions.maxExtent); │ │ │ │ - } │ │ │ │ - if (newOptions.minExtent && !(newOptions.minExtent instanceof OpenLayers.Bounds)) { │ │ │ │ - newOptions.minExtent = new OpenLayers.Bounds(newOptions.minExtent); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - // update our copy for clone │ │ │ │ - OpenLayers.Util.extend(this.options, newOptions); │ │ │ │ - │ │ │ │ - // add new options to this │ │ │ │ - OpenLayers.Util.extend(this, newOptions); │ │ │ │ - │ │ │ │ - // get the units from the projection, if we have a projection │ │ │ │ - // and it it has units │ │ │ │ - if (this.projection && this.projection.getUnits()) { │ │ │ │ - this.units = this.projection.getUnits(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // re-initialize resolutions if necessary, i.e. if any of the │ │ │ │ - // properties of the "properties" array defined below is set │ │ │ │ - // in the new options │ │ │ │ - if (this.map) { │ │ │ │ - // store current resolution so we can try to restore it later │ │ │ │ - var resolution = this.map.getResolution(); │ │ │ │ - var properties = this.RESOLUTION_PROPERTIES.concat( │ │ │ │ - ["projection", "units", "minExtent", "maxExtent"] │ │ │ │ - ); │ │ │ │ - for (var o in newOptions) { │ │ │ │ - if (newOptions.hasOwnProperty(o) && │ │ │ │ - OpenLayers.Util.indexOf(properties, o) >= 0) { │ │ │ │ - │ │ │ │ - this.initResolutions(); │ │ │ │ - if (reinitialize && this.map.baseLayer === this) { │ │ │ │ - // update map position, and restore previous resolution │ │ │ │ - this.map.setCenter(this.map.getCenter(), │ │ │ │ - this.map.getZoomForResolution(resolution), │ │ │ │ - false, true │ │ │ │ - ); │ │ │ │ - // trigger a changebaselayer event to make sure that │ │ │ │ - // all controls (especially │ │ │ │ - // OpenLayers.Control.PanZoomBar) get notified of the │ │ │ │ - // new options │ │ │ │ - this.map.events.triggerEvent("changebaselayer", { │ │ │ │ - layer: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + evaluate: function(context) { │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: onMapResize │ │ │ │ - * This function can be implemented by subclasses │ │ │ │ + * APIMethod: clone │ │ │ │ + * Clones this filter. Should be implemented by subclasses. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Filter>} Clone of this filter. │ │ │ │ */ │ │ │ │ - onMapResize: function() { │ │ │ │ - //this function can be implemented by subclasses │ │ │ │ + clone: function() { │ │ │ │ + return null; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: redraw │ │ │ │ - * Redraws the layer. Returns true if the layer was redrawn, false if not. │ │ │ │ + * APIMethod: toString │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The layer was redrawn. │ │ │ │ + * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL │ │ │ │ + * representation of the filter returned. Otherwise "[Object object]" │ │ │ │ + * will be returned. │ │ │ │ */ │ │ │ │ - redraw: function() { │ │ │ │ - var redrawn = false; │ │ │ │ - if (this.map) { │ │ │ │ + toString: function() { │ │ │ │ + var string; │ │ │ │ + if (OpenLayers.Format && OpenLayers.Format.CQL) { │ │ │ │ + string = OpenLayers.Format.CQL.prototype.write(this); │ │ │ │ + } else { │ │ │ │ + string = Object.prototype.toString.call(this); │ │ │ │ + } │ │ │ │ + return string; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // min/max Range may have changed │ │ │ │ - this.inRange = this.calculateInRange(); │ │ │ │ + CLASS_NAME: "OpenLayers.Filter" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Filter/Spatial.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - // map's center might not yet be set │ │ │ │ - var extent = 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. */ │ │ │ │ │ │ │ │ - if (extent && this.inRange && this.visibility) { │ │ │ │ - var zoomChanged = true; │ │ │ │ - this.moveTo(extent, zoomChanged, false); │ │ │ │ - this.events.triggerEvent("moveend", { │ │ │ │ - "zoomChanged": zoomChanged │ │ │ │ - }); │ │ │ │ - redrawn = true; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return redrawn; │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Filter.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to │ │ │ │ - * do some init work in that case. │ │ │ │ - * dragging - {Boolean} │ │ │ │ - */ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - var display = this.visibility; │ │ │ │ - if (!this.isBaseLayer) { │ │ │ │ - display = display && this.inRange; │ │ │ │ - } │ │ │ │ - this.display(display); │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Filter.Spatial │ │ │ │ + * This class represents a spatial filter. │ │ │ │ + * Currently implemented: BBOX, DWithin and Intersects │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Filter> │ │ │ │ + */ │ │ │ │ +OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveByPx │ │ │ │ - * Move the layer based on pixel vector. To be implemented by subclasses. │ │ │ │ + * APIProperty: type │ │ │ │ + * {String} Type of spatial filter. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * dx - {Number} The x coord of the displacement vector. │ │ │ │ - * dy - {Number} The y coord of the displacement vector. │ │ │ │ + * 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 │ │ │ │ */ │ │ │ │ - moveByPx: function(dx, dy) {}, │ │ │ │ + type: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the layer. This is done through an accessor │ │ │ │ - * so that subclasses can override this and take special action once │ │ │ │ - * they have their map variable set. │ │ │ │ - * │ │ │ │ - * Here we take care to bring over any of the necessary default │ │ │ │ - * properties from the map. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * APIProperty: property │ │ │ │ + * {String} Name of the context property to compare. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - if (this.map == null) { │ │ │ │ - │ │ │ │ - this.map = map; │ │ │ │ - │ │ │ │ - // grab some essential layer data from the map if it hasn't already │ │ │ │ - // been set │ │ │ │ - this.maxExtent = this.maxExtent || this.map.maxExtent; │ │ │ │ - this.minExtent = this.minExtent || this.map.minExtent; │ │ │ │ - │ │ │ │ - this.projection = this.projection || this.map.projection; │ │ │ │ - if (typeof this.projection == "string") { │ │ │ │ - this.projection = new OpenLayers.Projection(this.projection); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // Check the projection to see if we can get units -- if not, refer │ │ │ │ - // to properties. │ │ │ │ - this.units = this.projection.getUnits() || │ │ │ │ - this.units || this.map.units; │ │ │ │ - │ │ │ │ - this.initResolutions(); │ │ │ │ - │ │ │ │ - if (!this.isBaseLayer) { │ │ │ │ - this.inRange = this.calculateInRange(); │ │ │ │ - var show = ((this.visibility) && (this.inRange)); │ │ │ │ - this.div.style.display = show ? "" : "none"; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // deal with gutters │ │ │ │ - this.setTileSize(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + property: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: value │ │ │ │ + * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry │ │ │ │ + * to be used by the filter. Use bounds for BBOX filters and geometry │ │ │ │ + * for INTERSECTS or DWITHIN filters. │ │ │ │ */ │ │ │ │ - afterAdd: function() {}, │ │ │ │ + value: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: removeMap │ │ │ │ - * Just as setMap() allows each layer the possibility to take a │ │ │ │ - * personalized action on being added to the map, removeMap() allows │ │ │ │ - * each layer to take a personalized action on being removed from it. │ │ │ │ - * For now, this will be mostly unused, except for the EventPane layer, │ │ │ │ - * which needs this hook so that it can remove the special invisible │ │ │ │ - * pane. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * APIProperty: distance │ │ │ │ + * {Number} The distance to use in a DWithin spatial filter. │ │ │ │ */ │ │ │ │ - removeMap: function(map) { │ │ │ │ - //to be overridden by subclasses │ │ │ │ - }, │ │ │ │ + distance: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getImageSize │ │ │ │ + * APIProperty: distanceUnits │ │ │ │ + * {String} The units to use for the distance, e.g. 'm'. │ │ │ │ + */ │ │ │ │ + distanceUnits: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Filter.Spatial │ │ │ │ + * Creates a spatial filter. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} optional tile bounds, can be used │ │ │ │ - * by subclasses that have to deal with different tile sizes at the │ │ │ │ - * layer extent edges (e.g. Zoomify) │ │ │ │ + * options - {Object} An optional object with properties to set on the │ │ │ │ + * filter. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Size>} The size that the image should be, taking into │ │ │ │ - * account gutters. │ │ │ │ + * {<OpenLayers.Filter.Spatial>} │ │ │ │ */ │ │ │ │ - getImageSize: function(bounds) { │ │ │ │ - return (this.imageSize || this.tileSize); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setTileSize │ │ │ │ - * Set the tile size based on the map size. This also sets layer.imageSize │ │ │ │ - * or use by Tile.Image. │ │ │ │ + * Method: evaluate │ │ │ │ + * Evaluates this filter for a specific feature. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * size - {<OpenLayers.Size>} │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The feature meets filter criteria. │ │ │ │ */ │ │ │ │ - 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)); │ │ │ │ + evaluate: function(feature) { │ │ │ │ + var intersect = false; │ │ │ │ + switch (this.type) { │ │ │ │ + case OpenLayers.Filter.Spatial.BBOX: │ │ │ │ + case OpenLayers.Filter.Spatial.INTERSECTS: │ │ │ │ + if (feature.geometry) { │ │ │ │ + var geom = this.value; │ │ │ │ + if (this.value.CLASS_NAME == "OpenLayers.Bounds") { │ │ │ │ + geom = this.value.toGeometry(); │ │ │ │ + } │ │ │ │ + if (feature.geometry.intersects(geom)) { │ │ │ │ + intersect = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + throw new Error('evaluate is not implemented for this filter type.'); │ │ │ │ } │ │ │ │ + return intersect; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getVisibility │ │ │ │ + * APIMethod: clone │ │ │ │ + * Clones this filter. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The layer should be displayed (if in range). │ │ │ │ + * {<OpenLayers.Filter.Spatial>} Clone of this filter. │ │ │ │ */ │ │ │ │ - getVisibility: function() { │ │ │ │ - return this.visibility; │ │ │ │ + clone: function() { │ │ │ │ + var options = OpenLayers.Util.applyDefaults({ │ │ │ │ + value: this.value && this.value.clone && this.value.clone() │ │ │ │ + }, this); │ │ │ │ + return new OpenLayers.Filter.Spatial(options); │ │ │ │ }, │ │ │ │ + CLASS_NAME: "OpenLayers.Filter.Spatial" │ │ │ │ +}); │ │ │ │ + │ │ │ │ +OpenLayers.Filter.Spatial.BBOX = "BBOX"; │ │ │ │ +OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS"; │ │ │ │ +OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN"; │ │ │ │ +OpenLayers.Filter.Spatial.WITHIN = "WITHIN"; │ │ │ │ +OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS"; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Strategy/BBOX.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Strategy.js │ │ │ │ + * @requires OpenLayers/Filter/Spatial.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Strategy.BBOX │ │ │ │ + * A simple strategy that reads new features when the viewport invalidates │ │ │ │ + * some bounds. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Strategy> │ │ │ │ + */ │ │ │ │ +OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: bounds │ │ │ │ + * {<OpenLayers.Bounds>} The current data bounds (in the same projection │ │ │ │ + * as the layer - not always the same projection as the map). │ │ │ │ + */ │ │ │ │ + bounds: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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) │ │ │ │ + * Property: resolution │ │ │ │ + * {Float} The current data resolution. │ │ │ │ */ │ │ │ │ - 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"); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + resolution: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: ratio │ │ │ │ + * {Float} The ratio of the data bounds to the viewport bounds (in each │ │ │ │ + * dimension). Default is 2. │ │ │ │ + */ │ │ │ │ + ratio: 2, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: display │ │ │ │ - * Hide or show the Layer. This is designed to be used internally, and │ │ │ │ - * is not generally the way to enable or disable the layer. For that, │ │ │ │ - * use the setVisibility function instead.. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * display - {Boolean} │ │ │ │ + * Property: resFactor │ │ │ │ + * {Float} Optional factor used to determine when previously requested │ │ │ │ + * features are invalid. If set, the resFactor will be compared to the │ │ │ │ + * resolution of the previous request to the current map resolution. │ │ │ │ + * If resFactor > (old / new) and 1/resFactor < (old / new). If you │ │ │ │ + * set a resFactor of 1, data will be requested every time the │ │ │ │ + * resolution changes. If you set a resFactor of 3, data will be │ │ │ │ + * requested if the old resolution is 3 times the new, or if the new is │ │ │ │ + * 3 times the old. If the old bounds do not contain the new bounds │ │ │ │ + * new data will always be requested (with or without considering │ │ │ │ + * resFactor). │ │ │ │ */ │ │ │ │ - display: function(display) { │ │ │ │ - if (display != (this.div.style.display != "none")) { │ │ │ │ - this.div.style.display = (display && this.calculateInRange()) ? "block" : "none"; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + resFactor: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: calculateInRange │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The layer is displayable at the current map's current │ │ │ │ - * resolution. Note that if 'alwaysInRange' is true for the layer, │ │ │ │ - * this function will always return true. │ │ │ │ + * Property: response │ │ │ │ + * {<OpenLayers.Protocol.Response>} The protocol response object returned │ │ │ │ + * by the layer protocol. │ │ │ │ */ │ │ │ │ - calculateInRange: function() { │ │ │ │ - var inRange = false; │ │ │ │ + response: null, │ │ │ │ │ │ │ │ - if (this.alwaysInRange) { │ │ │ │ - inRange = true; │ │ │ │ - } else { │ │ │ │ - if (this.map) { │ │ │ │ - var resolution = this.map.getResolution(); │ │ │ │ - inRange = ((resolution >= this.minResolution) && │ │ │ │ - (resolution <= this.maxResolution)); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return inRange; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Strategy.BBOX │ │ │ │ + * Create a new BBOX strategy. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: setIsBaseLayer │ │ │ │ + /** │ │ │ │ + * Method: activate │ │ │ │ + * Set up strategy with regard to reading new batches of remote data. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * isBaseLayer - {Boolean} │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The strategy was successfully activated. │ │ │ │ */ │ │ │ │ - setIsBaseLayer: function(isBaseLayer) { │ │ │ │ - if (isBaseLayer != this.isBaseLayer) { │ │ │ │ - this.isBaseLayer = isBaseLayer; │ │ │ │ - if (this.map != null) { │ │ │ │ - this.map.events.triggerEvent("changebaselayer", { │ │ │ │ - layer: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + "moveend": this.update, │ │ │ │ + "refresh": this.update, │ │ │ │ + "visibilitychanged": this.update, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.update(); │ │ │ │ } │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ - /********************************************************/ │ │ │ │ - /* */ │ │ │ │ - /* 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. │ │ │ │ + /** │ │ │ │ + * Method: deactivate │ │ │ │ + * Tear down strategy with regard to reading new batches of remote data. │ │ │ │ * │ │ │ │ - * 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 │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The strategy was successfully deactivated. │ │ │ │ */ │ │ │ │ - initResolutions: function() { │ │ │ │ - │ │ │ │ - // ok we want resolutions, here's our strategy: │ │ │ │ - // │ │ │ │ - // 1. if resolutions are defined in the layer config, use them │ │ │ │ - // 2. else, if scales are defined in the layer config then derive │ │ │ │ - // resolutions from these scales │ │ │ │ - // 3. else, attempt to calculate resolutions from maxResolution, │ │ │ │ - // minResolution, numZoomLevels, maxZoomLevel set in the │ │ │ │ - // layer config │ │ │ │ - // 4. if we still don't have resolutions, and if resolutions │ │ │ │ - // are defined in the same, use them │ │ │ │ - // 5. else, if scales are defined in the map then derive │ │ │ │ - // resolutions from these scales │ │ │ │ - // 6. else, attempt to calculate resolutions from maxResolution, │ │ │ │ - // minResolution, numZoomLevels, maxZoomLevel set in the │ │ │ │ - // map │ │ │ │ - // 7. hope for the best! │ │ │ │ - │ │ │ │ - var i, len, p; │ │ │ │ - var props = {}, │ │ │ │ - alwaysInRange = true; │ │ │ │ - │ │ │ │ - // get resolution data from layer config │ │ │ │ - // (we also set alwaysInRange in the layer as appropriate) │ │ │ │ - for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) { │ │ │ │ - p = this.RESOLUTION_PROPERTIES[i]; │ │ │ │ - props[p] = this.options[p]; │ │ │ │ - if (alwaysInRange && this.options[p]) { │ │ │ │ - alwaysInRange = false; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.options.alwaysInRange == null) { │ │ │ │ - this.alwaysInRange = alwaysInRange; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // if we don't have resolutions then attempt to derive them from scales │ │ │ │ - if (props.resolutions == null) { │ │ │ │ - props.resolutions = this.resolutionsFromScales(props.scales); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // if we still don't have resolutions then attempt to calculate them │ │ │ │ - if (props.resolutions == null) { │ │ │ │ - props.resolutions = this.calculateResolutions(props); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // if we couldn't calculate resolutions then we look at we have │ │ │ │ - // in the map │ │ │ │ - if (props.resolutions == null) { │ │ │ │ - for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) { │ │ │ │ - p = this.RESOLUTION_PROPERTIES[i]; │ │ │ │ - props[p] = this.options[p] != null ? │ │ │ │ - this.options[p] : this.map[p]; │ │ │ │ - } │ │ │ │ - if (props.resolutions == null) { │ │ │ │ - props.resolutions = this.resolutionsFromScales(props.scales); │ │ │ │ - } │ │ │ │ - if (props.resolutions == null) { │ │ │ │ - props.resolutions = this.calculateResolutions(props); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - // ok, we new need to set properties in the instance │ │ │ │ - │ │ │ │ - // get maxResolution from the config if it's defined there │ │ │ │ - var maxResolution; │ │ │ │ - if (this.options.maxResolution && │ │ │ │ - this.options.maxResolution !== "auto") { │ │ │ │ - maxResolution = this.options.maxResolution; │ │ │ │ - } │ │ │ │ - if (this.options.minScale) { │ │ │ │ - maxResolution = OpenLayers.Util.getResolutionFromScale( │ │ │ │ - this.options.minScale, this.units); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // get minResolution from the config if it's defined there │ │ │ │ - var minResolution; │ │ │ │ - if (this.options.minResolution && │ │ │ │ - this.options.minResolution !== "auto") { │ │ │ │ - minResolution = this.options.minResolution; │ │ │ │ - } │ │ │ │ - if (this.options.maxScale) { │ │ │ │ - minResolution = OpenLayers.Util.getResolutionFromScale( │ │ │ │ - this.options.maxScale, this.units); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (props.resolutions) { │ │ │ │ - │ │ │ │ - //sort resolutions array descendingly │ │ │ │ - props.resolutions.sort(function(a, b) { │ │ │ │ - return (b - a); │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.layer.events.un({ │ │ │ │ + "moveend": this.update, │ │ │ │ + "refresh": this.update, │ │ │ │ + "visibilitychanged": this.update, │ │ │ │ + scope: this │ │ │ │ }); │ │ │ │ - │ │ │ │ - // if we still don't have a maxResolution get it from the │ │ │ │ - // resolutions array │ │ │ │ - if (!maxResolution) { │ │ │ │ - maxResolution = props.resolutions[0]; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // if we still don't have a minResolution get it from the │ │ │ │ - // resolutions array │ │ │ │ - if (!minResolution) { │ │ │ │ - var lastIdx = props.resolutions.length - 1; │ │ │ │ - minResolution = props.resolutions[lastIdx]; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.resolutions = props.resolutions; │ │ │ │ - if (this.resolutions) { │ │ │ │ - len = this.resolutions.length; │ │ │ │ - this.scales = new Array(len); │ │ │ │ - for (i = 0; i < len; i++) { │ │ │ │ - this.scales[i] = OpenLayers.Util.getScaleFromResolution( │ │ │ │ - this.resolutions[i], this.units); │ │ │ │ - } │ │ │ │ - this.numZoomLevels = len; │ │ │ │ - } │ │ │ │ - this.minResolution = minResolution; │ │ │ │ - if (minResolution) { │ │ │ │ - this.maxScale = OpenLayers.Util.getScaleFromResolution( │ │ │ │ - minResolution, this.units); │ │ │ │ - } │ │ │ │ - this.maxResolution = maxResolution; │ │ │ │ - if (maxResolution) { │ │ │ │ - this.minScale = OpenLayers.Util.getScaleFromResolution( │ │ │ │ - maxResolution, this.units); │ │ │ │ } │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: resolutionsFromScales │ │ │ │ - * Derive resolutions from scales. │ │ │ │ + * Method: update │ │ │ │ + * Callback function called on "moveend" or "refresh" layer events. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * scales - {Array(Number)} Scales │ │ │ │ + * options - {Object} Optional object whose properties will determine │ │ │ │ + * the behaviour of this Strategy │ │ │ │ * │ │ │ │ - * Returns │ │ │ │ - * {Array(Number)} Resolutions │ │ │ │ + * Valid options include: │ │ │ │ + * force - {Boolean} if true, new data must be unconditionally read. │ │ │ │ + * noAbort - {Boolean} if true, do not abort previous requests. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + update: function(options) { │ │ │ │ + var mapBounds = this.getMapBounds(); │ │ │ │ + if (mapBounds !== null && ((options && options.force) || │ │ │ │ + (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) { │ │ │ │ + this.calculateBounds(mapBounds); │ │ │ │ + this.resolution = this.layer.map.getResolution(); │ │ │ │ + this.triggerRead(options); │ │ │ │ } │ │ │ │ - return resolutions; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: calculateResolutions │ │ │ │ - * Calculate resolutions based on the provided properties. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * props - {Object} Properties │ │ │ │ + * Method: getMapBounds │ │ │ │ + * Get the map bounds expressed in the same projection as this layer. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array({Number})} Array of resolutions. │ │ │ │ + * {<OpenLayers.Bounds>} Map bounds in the projection of the layer. │ │ │ │ */ │ │ │ │ - calculateResolutions: function(props) { │ │ │ │ - │ │ │ │ - var viewSize, wRes, hRes; │ │ │ │ - │ │ │ │ - // determine maxResolution │ │ │ │ - var maxResolution = props.maxResolution; │ │ │ │ - if (props.minScale != null) { │ │ │ │ - maxResolution = │ │ │ │ - OpenLayers.Util.getResolutionFromScale(props.minScale, │ │ │ │ - this.units); │ │ │ │ - } else if (maxResolution == "auto" && this.maxExtent != null) { │ │ │ │ - viewSize = this.map.getSize(); │ │ │ │ - wRes = this.maxExtent.getWidth() / viewSize.w; │ │ │ │ - hRes = this.maxExtent.getHeight() / viewSize.h; │ │ │ │ - maxResolution = Math.max(wRes, hRes); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // determine minResolution │ │ │ │ - var minResolution = props.minResolution; │ │ │ │ - if (props.maxScale != null) { │ │ │ │ - minResolution = │ │ │ │ - OpenLayers.Util.getResolutionFromScale(props.maxScale, │ │ │ │ - this.units); │ │ │ │ - } else if (props.minResolution == "auto" && this.minExtent != null) { │ │ │ │ - viewSize = this.map.getSize(); │ │ │ │ - wRes = this.minExtent.getWidth() / viewSize.w; │ │ │ │ - hRes = this.minExtent.getHeight() / viewSize.h; │ │ │ │ - minResolution = Math.max(wRes, hRes); │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (typeof maxResolution !== "number" && │ │ │ │ - typeof minResolution !== "number" && │ │ │ │ - this.maxExtent != null) { │ │ │ │ - // maxResolution for default grid sets assumes that at zoom │ │ │ │ - // level zero, the whole world fits on one tile. │ │ │ │ - var tileSize = this.map.getTileSize(); │ │ │ │ - maxResolution = Math.max( │ │ │ │ - this.maxExtent.getWidth() / tileSize.w, │ │ │ │ - this.maxExtent.getHeight() / tileSize.h │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // determine numZoomLevels │ │ │ │ - var maxZoomLevel = props.maxZoomLevel; │ │ │ │ - var numZoomLevels = props.numZoomLevels; │ │ │ │ - if (typeof minResolution === "number" && │ │ │ │ - typeof maxResolution === "number" && numZoomLevels === undefined) { │ │ │ │ - var ratio = maxResolution / minResolution; │ │ │ │ - numZoomLevels = Math.floor(Math.log(ratio) / Math.log(2)) + 1; │ │ │ │ - } else if (numZoomLevels === undefined && maxZoomLevel != null) { │ │ │ │ - numZoomLevels = maxZoomLevel + 1; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // are we able to calculate resolutions? │ │ │ │ - if (typeof numZoomLevels !== "number" || numZoomLevels <= 0 || │ │ │ │ - (typeof maxResolution !== "number" && │ │ │ │ - typeof minResolution !== "number")) { │ │ │ │ - return; │ │ │ │ + getMapBounds: function() { │ │ │ │ + if (this.layer.map === null) { │ │ │ │ + return null; │ │ │ │ } │ │ │ │ - │ │ │ │ - // now we have numZoomLevels and at least one of maxResolution │ │ │ │ - // or minResolution, we can populate the resolutions array │ │ │ │ - │ │ │ │ - var resolutions = new Array(numZoomLevels); │ │ │ │ - var base = 2; │ │ │ │ - if (typeof minResolution == "number" && │ │ │ │ - typeof maxResolution == "number") { │ │ │ │ - // if maxResolution and minResolution are set, we calculate │ │ │ │ - // the base for exponential scaling that starts at │ │ │ │ - // maxResolution and ends at minResolution in numZoomLevels │ │ │ │ - // steps. │ │ │ │ - base = Math.pow( │ │ │ │ - (maxResolution / minResolution), │ │ │ │ - (1 / (numZoomLevels - 1)) │ │ │ │ + var bounds = this.layer.map.getExtent(); │ │ │ │ + if (bounds && !this.layer.projection.equals( │ │ │ │ + this.layer.map.getProjectionObject())) { │ │ │ │ + bounds = bounds.clone().transform( │ │ │ │ + this.layer.map.getProjectionObject(), this.layer.projection │ │ │ │ ); │ │ │ │ } │ │ │ │ - │ │ │ │ - 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; │ │ │ │ + return bounds; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getResolution │ │ │ │ - * │ │ │ │ + * Method: invalidBounds │ │ │ │ + * Determine whether the previously requested set of features is invalid. │ │ │ │ + * This occurs when the new map bounds do not contain the previously │ │ │ │ + * requested bounds. In addition, if <resFactor> is set, it will be │ │ │ │ + * considered. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be │ │ │ │ + * retrieved from the map object if not provided │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Float} The currently selected resolution of the map, taken from the │ │ │ │ - * resolutions array, indexed by current zoom level. │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - getResolution: function() { │ │ │ │ - var zoom = this.map.getZoom(); │ │ │ │ - return this.getResolutionForZoom(zoom); │ │ │ │ + invalidBounds: function(mapBounds) { │ │ │ │ + if (!mapBounds) { │ │ │ │ + mapBounds = this.getMapBounds(); │ │ │ │ + } │ │ │ │ + var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds); │ │ │ │ + if (!invalid && this.resFactor) { │ │ │ │ + var ratio = this.resolution / this.layer.map.getResolution(); │ │ │ │ + invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor)); │ │ │ │ + } │ │ │ │ + return invalid; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getExtent │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat │ │ │ │ - * bounds of the current viewPort. │ │ │ │ + /** │ │ │ │ + * Method: calculateBounds │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be │ │ │ │ + * retrieved from the map object if not provided │ │ │ │ */ │ │ │ │ - getExtent: function() { │ │ │ │ - // just use stock map calculateBounds function -- passing no arguments │ │ │ │ - // means it will user map's current center & resolution │ │ │ │ - // │ │ │ │ - return this.map.calculateBounds(); │ │ │ │ + calculateBounds: function(mapBounds) { │ │ │ │ + if (!mapBounds) { │ │ │ │ + mapBounds = this.getMapBounds(); │ │ │ │ + } │ │ │ │ + var center = mapBounds.getCenterLonLat(); │ │ │ │ + var dataWidth = mapBounds.getWidth() * this.ratio; │ │ │ │ + var dataHeight = mapBounds.getHeight() * this.ratio; │ │ │ │ + this.bounds = new OpenLayers.Bounds( │ │ │ │ + center.lon - (dataWidth / 2), │ │ │ │ + center.lat - (dataHeight / 2), │ │ │ │ + center.lon + (dataWidth / 2), │ │ │ │ + center.lat + (dataHeight / 2) │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getZoomForExtent │ │ │ │ - * │ │ │ │ + * Method: triggerRead │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * extent - {<OpenLayers.Bounds>} │ │ │ │ - * closest - {Boolean} Find the zoom level that most closely fits the │ │ │ │ - * specified bounds. Note that this may result in a zoom that does │ │ │ │ - * not exactly contain the entire extent. │ │ │ │ - * Default is false. │ │ │ │ + * options - {Object} Additional options for the protocol's read method │ │ │ │ + * (optional) │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Integer} The index of the zoomLevel (entry in the resolutions array) │ │ │ │ - * for the passed-in extent. We do this by calculating the ideal │ │ │ │ - * resolution for the given extent (based on the map size) and then │ │ │ │ - * calling getZoomForResolution(), passing along the 'closest' │ │ │ │ - * parameter. │ │ │ │ - */ │ │ │ │ - getZoomForExtent: function(extent, closest) { │ │ │ │ - var viewSize = this.map.getSize(); │ │ │ │ - var idealResolution = Math.max(extent.getWidth() / viewSize.w, │ │ │ │ - extent.getHeight() / viewSize.h); │ │ │ │ - │ │ │ │ - return this.getZoomForResolution(idealResolution, closest); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getDataExtent │ │ │ │ - * Calculates the max extent which includes all of the data for the layer. │ │ │ │ - * This function is to be implemented by subclasses. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} │ │ │ │ + * {<OpenLayers.Protocol.Response>} The protocol response object │ │ │ │ + * returned by the layer protocol. │ │ │ │ */ │ │ │ │ - getDataExtent: function() { │ │ │ │ - //to be implemented by subclasses │ │ │ │ + triggerRead: function(options) { │ │ │ │ + if (this.response && !(options && options.noAbort === true)) { │ │ │ │ + this.layer.protocol.abort(this.response); │ │ │ │ + this.layer.events.triggerEvent("loadend"); │ │ │ │ + } │ │ │ │ + var evt = { │ │ │ │ + filter: this.createFilter() │ │ │ │ + }; │ │ │ │ + this.layer.events.triggerEvent("loadstart", evt); │ │ │ │ + this.response = this.layer.protocol.read( │ │ │ │ + OpenLayers.Util.applyDefaults({ │ │ │ │ + filter: evt.filter, │ │ │ │ + callback: this.merge, │ │ │ │ + scope: this │ │ │ │ + }, options)); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getResolutionForZoom │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * zoom - {Float} │ │ │ │ + * Method: createFilter │ │ │ │ + * Creates a spatial BBOX filter. If the layer that this strategy belongs │ │ │ │ + * to has a filter property, this filter will be combined with the BBOX │ │ │ │ + * filter. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Float} A suitable resolution for the specified zoom. │ │ │ │ + * Returns │ │ │ │ + * {<OpenLayers.Filter>} The filter object. │ │ │ │ */ │ │ │ │ - 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)]; │ │ │ │ + createFilter: function() { │ │ │ │ + var filter = new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ + value: this.bounds, │ │ │ │ + projection: this.layer.projection │ │ │ │ + }); │ │ │ │ + if (this.layer.filter) { │ │ │ │ + filter = new OpenLayers.Filter.Logical({ │ │ │ │ + type: OpenLayers.Filter.Logical.AND, │ │ │ │ + filters: [this.layer.filter, filter] │ │ │ │ + }); │ │ │ │ } │ │ │ │ - return resolution; │ │ │ │ + return filter; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getZoomForResolution │ │ │ │ - * │ │ │ │ + * Method: merge │ │ │ │ + * Given a list of features, determine which ones to add to the layer. │ │ │ │ + * If the layer projection differs from the map projection, features │ │ │ │ + * will be transformed from the layer projection to the map projection. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * resolution - {Float} │ │ │ │ - * closest - {Boolean} Find the zoom level that corresponds to the absolute │ │ │ │ - * closest resolution, which may result in a zoom whose corresponding │ │ │ │ - * resolution is actually smaller than we would have desired (if this │ │ │ │ - * is being called from a getZoomForExtent() call, then this means that │ │ │ │ - * the returned zoom index might not actually contain the entire │ │ │ │ - * extent specified... but it'll be close). │ │ │ │ - * Default is false. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} The index of the zoomLevel (entry in the resolutions array) │ │ │ │ - * that corresponds to the best fit resolution given the passed in │ │ │ │ - * value and the 'closest' specification. │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object passed │ │ │ │ + * by the protocol. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + merge: function(resp) { │ │ │ │ + this.layer.destroyFeatures(); │ │ │ │ + if (resp.success()) { │ │ │ │ + var features = resp.features; │ │ │ │ + if (features && features.length > 0) { │ │ │ │ + var remote = this.layer.projection; │ │ │ │ + var local = this.layer.map.getProjectionObject(); │ │ │ │ + if (!local.equals(remote)) { │ │ │ │ + var geom; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + geom = features[i].geometry; │ │ │ │ + if (geom) { │ │ │ │ + geom.transform(remote, local); │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ + this.layer.addFeatures(features); │ │ │ │ } │ │ │ │ - zoom = Math.max(0, i - 1); │ │ │ │ + } else { │ │ │ │ + this.bounds = null; │ │ │ │ } │ │ │ │ - return zoom; │ │ │ │ + this.response = null; │ │ │ │ + this.layer.events.triggerEvent("loadend", { │ │ │ │ + response: resp │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.BBOX" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Strategy/Fixed.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Strategy.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Strategy.Fixed │ │ │ │ + * A simple strategy that requests features once and never requests new data. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Strategy> │ │ │ │ + */ │ │ │ │ +OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ + │ │ │ │ /** │ │ │ │ - * APIMethod: getLonLatFromViewPortPx │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or │ │ │ │ - * an object with a 'x' │ │ │ │ - * and 'y' properties. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in │ │ │ │ - * view port <OpenLayers.Pixel>, translated into lon/lat by the layer. │ │ │ │ + * APIProperty: preload │ │ │ │ + * {Boolean} Load data before layer made visible. Enabling this may result │ │ │ │ + * in considerable overhead if your application loads many data layers │ │ │ │ + * that are not visible by default. Default is false. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - }, │ │ │ │ + preload: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getViewPortPxFromLonLat │ │ │ │ - * Returns a pixel location given a map location. This method will return │ │ │ │ - * fractional pixel values. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * lonlat - {<OpenLayers.LonLat>|Object} An OpenLayers.LonLat or │ │ │ │ - * an object with a 'lon' │ │ │ │ - * and 'lat' properties. │ │ │ │ + * Constructor: OpenLayers.Strategy.Fixed │ │ │ │ + * Create a new Fixed strategy. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in │ │ │ │ - * lonlat translated into view port pixels. │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ - 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} │ │ │ │ + * Method: activate │ │ │ │ + * Activate the strategy: load data or add listener to load when visible │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} True if the strategy was successfully activated or false if │ │ │ │ + * the strategy was already active. │ │ │ │ */ │ │ │ │ - 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" │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); │ │ │ │ + if (activated) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + "refresh": this.load, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + if (this.layer.visibility == true || this.preload) { │ │ │ │ + this.load(); │ │ │ │ + } else { │ │ │ │ + this.layer.events.on({ │ │ │ │ + "visibilitychanged": this.load, │ │ │ │ + scope: this │ │ │ │ }); │ │ │ │ } │ │ │ │ } │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getZIndex │ │ │ │ + * Method: deactivate │ │ │ │ + * Deactivate the strategy. Undo what is done in <activate>. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} the z-index of this layer │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The strategy was successfully deactivated. │ │ │ │ */ │ │ │ │ - getZIndex: function() { │ │ │ │ - return this.div.style.zIndex; │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.layer.events.un({ │ │ │ │ + "refresh": this.load, │ │ │ │ + "visibilitychanged": this.load, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setZIndex │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * zIndex - {Integer} │ │ │ │ + * Method: load │ │ │ │ + * Tells protocol to load data and unhooks the visibilitychanged event │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} options to pass to protocol read. │ │ │ │ */ │ │ │ │ - setZIndex: function(zIndex) { │ │ │ │ - this.div.style.zIndex = zIndex; │ │ │ │ + load: function(options) { │ │ │ │ + var layer = this.layer; │ │ │ │ + layer.events.triggerEvent("loadstart", { │ │ │ │ + filter: layer.filter │ │ │ │ + }); │ │ │ │ + layer.protocol.read(OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: this.merge, │ │ │ │ + filter: layer.filter, │ │ │ │ + scope: this │ │ │ │ + }, options)); │ │ │ │ + layer.events.un({ │ │ │ │ + "visibilitychanged": this.load, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: 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. │ │ │ │ - * │ │ │ │ + * Method: merge │ │ │ │ + * Add all features to the layer. │ │ │ │ + * If the layer projection differs from the map projection, features │ │ │ │ + * will be transformed from the layer projection to the map projection. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object passed │ │ │ │ + * by the protocol. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - │ │ │ │ + merge: function(resp) { │ │ │ │ + var layer = this.layer; │ │ │ │ + layer.destroyFeatures(); │ │ │ │ + var features = resp.features; │ │ │ │ + if (features && features.length > 0) { │ │ │ │ + var remote = layer.projection; │ │ │ │ + var local = layer.map.getProjectionObject(); │ │ │ │ + if (!local.equals(remote)) { │ │ │ │ + var geom; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + geom = features[i].geometry; │ │ │ │ + if (geom) { │ │ │ │ + geom.transform(remote, local); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + layer.addFeatures(features); │ │ │ │ } │ │ │ │ - return bounds; │ │ │ │ + layer.events.triggerEvent("loadend", { │ │ │ │ + response: resp │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer" │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Fixed" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/HTTPRequest.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/Layer.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.HTTPRequest │ │ │ │ + * 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. │ │ │ │ * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer> │ │ │ │ + * 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.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ +OpenLayers.Renderer = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Property: container │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2, │ │ │ │ + container: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: url │ │ │ │ - * {Array(String) or String} This is either an array of url strings or │ │ │ │ - * a single url string. │ │ │ │ + /** │ │ │ │ + * Property: root │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - url: null, │ │ │ │ + root: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: params │ │ │ │ - * {Object} Hashtable of key/value parameters │ │ │ │ + * Property: extent │ │ │ │ + * {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - params: null, │ │ │ │ + extent: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: locked │ │ │ │ + * {Boolean} If the renderer is currently in a state where many things │ │ │ │ + * are changing, the 'locked' property is set to true. This means │ │ │ │ + * that renderers can expect at least one more drawFeature event to be │ │ │ │ + * called with the 'locked' property set to 'true': In some renderers, │ │ │ │ + * this might make sense to use as a 'only update local information' │ │ │ │ + * flag. │ │ │ │ + */ │ │ │ │ + locked: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: size │ │ │ │ + * {<OpenLayers.Size>} │ │ │ │ */ │ │ │ │ - reproject: false, │ │ │ │ + size: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.HTTPRequest │ │ │ │ - * │ │ │ │ + * Property: resolution │ │ │ │ + * {Float} cache of current map resolution │ │ │ │ + */ │ │ │ │ + resolution: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: map │ │ │ │ + * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap() │ │ │ │ + */ │ │ │ │ + map: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: featureDx │ │ │ │ + * {Number} Feature offset in x direction. Will be calculated for and │ │ │ │ + * applied to the current feature while rendering (see │ │ │ │ + * <calculateFeatureDx>). │ │ │ │ + */ │ │ │ │ + featureDx: 0, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Renderer │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * url - {Array(String) or String} │ │ │ │ - * params - {Object} │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * containerID - {<String>} │ │ │ │ + * options - {Object} options for this renderer. See sublcasses for │ │ │ │ + * supported options. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + initialize: function(containerID, options) { │ │ │ │ + this.container = OpenLayers.Util.getElement(containerID); │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ * APIMethod: destroy │ │ │ │ */ │ │ │ │ destroy: function() { │ │ │ │ - this.url = null; │ │ │ │ - this.params = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ + this.container = null; │ │ │ │ + this.extent = null; │ │ │ │ + this.size = null; │ │ │ │ + this.resolution = null; │ │ │ │ + this.map = null; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {Object} │ │ │ │ + * APIMethod: supported │ │ │ │ + * This should be overridden by specific subclasses │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this │ │ │ │ - * <OpenLayers.Layer.HTTPRequest> │ │ │ │ + * {Boolean} Whether or not the browser supports the renderer class │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ + supported: function() { │ │ │ │ + return false; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.HTTPRequest(this.name, │ │ │ │ - this.url, │ │ │ │ - this.params, │ │ │ │ - this.getOptions()); │ │ │ │ + /** │ │ │ │ + * Method: setExtent │ │ │ │ + * Set the visible part of the layer. │ │ │ │ + * │ │ │ │ + * Resolution has probably changed, so we nullify the resolution │ │ │ │ + * cache (this.resolution) -- this way it will be re-computed when │ │ │ │ + * next it is needed. │ │ │ │ + * We nullify the resolution cache (this.resolution) if resolutionChanged │ │ │ │ + * is set to true - this way it will be re-computed on the next │ │ │ │ + * getResolution() request. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * extent - {<OpenLayers.Bounds>} │ │ │ │ + * resolutionChanged - {Boolean} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true to notify the layer that the new extent does not exceed │ │ │ │ + * the coordinate range, and the features will not need to be redrawn. │ │ │ │ + * False otherwise. │ │ │ │ + */ │ │ │ │ + setExtent: function(extent, resolutionChanged) { │ │ │ │ + this.extent = extent.clone(); │ │ │ │ + if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) { │ │ │ │ + var ratio = extent.getWidth() / this.map.getExtent().getWidth(), │ │ │ │ + extent = extent.scale(1 / ratio); │ │ │ │ + this.extent = extent.wrapDateLine(this.map.getMaxExtent()).scale(ratio); │ │ │ │ } │ │ │ │ - │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ - │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - │ │ │ │ - return obj; │ │ │ │ + if (resolutionChanged) { │ │ │ │ + this.resolution = null; │ │ │ │ + } │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: setUrl │ │ │ │ + /** │ │ │ │ + * 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: │ │ │ │ - * newUrl - {String} │ │ │ │ + * size - {<OpenLayers.Size>} │ │ │ │ */ │ │ │ │ - setUrl: function(newUrl) { │ │ │ │ - this.url = newUrl; │ │ │ │ + setSize: function(size) { │ │ │ │ + this.size = size.clone(); │ │ │ │ + this.resolution = null; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: mergeNewParams │ │ │ │ + /** │ │ │ │ + * Method: getResolution │ │ │ │ + * Uses cached copy of resolution if available to minimize computing │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * newParams - {Object} │ │ │ │ - * │ │ │ │ * Returns: │ │ │ │ - * redrawn: {Boolean} whether the layer was actually redrawn. │ │ │ │ + * {Float} The current map's resolution │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + getResolution: function() { │ │ │ │ + this.resolution = this.resolution || this.map.getResolution(); │ │ │ │ + return this.resolution; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: redraw │ │ │ │ - * Redraws the layer. Returns true if the layer was redrawn, false if not. │ │ │ │ + * 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: │ │ │ │ - * force - {Boolean} Force redraw by adding random parameter. │ │ │ │ - * │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * style - {<Object>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The layer was redrawn. │ │ │ │ + * {Boolean} true if the feature has been drawn completely, false if not, │ │ │ │ + * undefined if the feature had no geometry │ │ │ │ */ │ │ │ │ - redraw: function(force) { │ │ │ │ - if (force) { │ │ │ │ - return this.mergeNewParams({ │ │ │ │ - "_olSalt": Math.random() │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - return OpenLayers.Layer.prototype.redraw.apply(this, []); │ │ │ │ + drawFeature: function(feature, style) { │ │ │ │ + if (style == null) { │ │ │ │ + style = feature.style; │ │ │ │ + } │ │ │ │ + if (feature.geometry) { │ │ │ │ + var bounds = feature.geometry.getBounds(); │ │ │ │ + if (bounds) { │ │ │ │ + var worldBounds; │ │ │ │ + if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) { │ │ │ │ + worldBounds = this.map.getMaxExtent(); │ │ │ │ + } │ │ │ │ + if (!bounds.intersectsBounds(this.extent, { │ │ │ │ + worldBounds: worldBounds │ │ │ │ + })) { │ │ │ │ + style = { │ │ │ │ + display: "none" │ │ │ │ + }; │ │ │ │ + } else { │ │ │ │ + this.calculateFeatureDx(bounds, worldBounds); │ │ │ │ + } │ │ │ │ + var rendered = this.drawGeometry(feature.geometry, style, feature.id); │ │ │ │ + if (style.display != "none" && style.label && rendered !== false) { │ │ │ │ + │ │ │ │ + var location = feature.geometry.getCentroid(); │ │ │ │ + if (style.labelXOffset || style.labelYOffset) { │ │ │ │ + var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset; │ │ │ │ + var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset; │ │ │ │ + var res = this.getResolution(); │ │ │ │ + location.move(xOffset * res, yOffset * res); │ │ │ │ + } │ │ │ │ + this.drawText(feature.id, style, location); │ │ │ │ + } else { │ │ │ │ + this.removeText(feature.id); │ │ │ │ + } │ │ │ │ + return rendered; │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: 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. │ │ │ │ + * 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: │ │ │ │ - * paramString - {String} │ │ │ │ - * urls - {Array(String)} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} An entry from the urls array, deterministically selected based │ │ │ │ - * on the paramString. │ │ │ │ + * bounds - {<OpenLayers.Bounds>} Bounds of the feature │ │ │ │ + * worldBounds - {<OpenLayers.Bounds>} Bounds of the world │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - return urls[Math.floor(product * urls.length)]; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getFullRequestString │ │ │ │ - * Combine url with layer's params and these newParams. │ │ │ │ - * │ │ │ │ - * does checking on the serverPath variable, allowing for cases when it │ │ │ │ - * is supplied with trailing ? or &, as well as cases where not. │ │ │ │ - * │ │ │ │ - * return in formatted string like this: │ │ │ │ - * "server?key1=value1&key2=value2&key3=value3" │ │ │ │ + * Method: drawGeometry │ │ │ │ * │ │ │ │ - * WARNING: The altUrl parameter is deprecated and will be removed in 3.0. │ │ │ │ + * Draw a geometry. This should only be called from the renderer itself. │ │ │ │ + * Use layer.drawFeature() from outside the renderer. │ │ │ │ + * virtual function │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * newParams - {Object} │ │ │ │ - * altUrl - {String} Use this as the url instead of the layer's url │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {<String>} │ │ │ │ */ │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ + drawGeometry: function(geometry, style, featureId) {}, │ │ │ │ │ │ │ │ - // if not altUrl passed in, use layer's url │ │ │ │ - var url = altUrl || this.url; │ │ │ │ + /** │ │ │ │ + * Method: drawText │ │ │ │ + * Function for drawing text labels. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * featureId - {String} │ │ │ │ + * style - │ │ │ │ + * location - {<OpenLayers.Geometry.Point>} │ │ │ │ + */ │ │ │ │ + drawText: function(featureId, style, location) {}, │ │ │ │ │ │ │ │ - // 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: removeText │ │ │ │ + * Function for removing text labels. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * featureId - {String} │ │ │ │ + */ │ │ │ │ + removeText: function(featureId) {}, │ │ │ │ │ │ │ │ - // 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: clear │ │ │ │ + * Clear all vectors from the renderer. │ │ │ │ + * virtual function. │ │ │ │ + */ │ │ │ │ + clear: function() {}, │ │ │ │ │ │ │ │ - // 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]; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: getFeatureIdFromEvent │ │ │ │ + * Returns a feature id from an event on the renderer. │ │ │ │ + * How this happens is specific to the renderer. This should be │ │ │ │ + * called from layer.getFeatureFromEvent(). │ │ │ │ + * Virtual function. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {<OpenLayers.Event>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A feature id or undefined. │ │ │ │ + */ │ │ │ │ + getFeatureIdFromEvent: function(evt) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: eraseFeatures │ │ │ │ + * This is called by the layer to erase features │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + */ │ │ │ │ + eraseFeatures: function(features) { │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ + features = [features]; │ │ │ │ } │ │ │ │ - paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + var feature = features[i]; │ │ │ │ + this.eraseGeometry(feature.geometry, feature.id); │ │ │ │ + this.removeText(feature.id); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - return OpenLayers.Util.urlAppend(url, paramsString); │ │ │ │ + /** │ │ │ │ + * Method: eraseGeometry │ │ │ │ + * Remove a geometry from the renderer (by id). │ │ │ │ + * virtual function. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * featureId - {String} │ │ │ │ + */ │ │ │ │ + eraseGeometry: function(geometry, featureId) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveRoot │ │ │ │ + * moves this renderer's root to a (different) renderer. │ │ │ │ + * To be implemented by subclasses that require a common renderer root for │ │ │ │ + * feature selection. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * renderer - {<OpenLayers.Renderer>} target renderer for the moved root │ │ │ │ + */ │ │ │ │ + moveRoot: function(renderer) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getRenderLayerId │ │ │ │ + * Gets the layer that this renderer's output appears on. If moveRoot was │ │ │ │ + * used, this will be different from the id of the layer containing the │ │ │ │ + * features rendered by this renderer. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} the id of the output layer. │ │ │ │ + */ │ │ │ │ + getRenderLayerId: function() { │ │ │ │ + return this.container.id; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.HTTPRequest" │ │ │ │ + /** │ │ │ │ + * Method: applyDefaultSymbolizer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * symbolizer - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} │ │ │ │ + */ │ │ │ │ + applyDefaultSymbolizer: function(symbolizer) { │ │ │ │ + var result = OpenLayers.Util.extend({}, │ │ │ │ + OpenLayers.Renderer.defaultSymbolizer); │ │ │ │ + if (symbolizer.stroke === false) { │ │ │ │ + delete result.strokeWidth; │ │ │ │ + delete result.strokeColor; │ │ │ │ + } │ │ │ │ + if (symbolizer.fill === false) { │ │ │ │ + delete result.fillColor; │ │ │ │ + } │ │ │ │ + OpenLayers.Util.extend(result, symbolizer); │ │ │ │ + return result; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer" │ │ │ │ }); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Renderer.defaultSymbolizer │ │ │ │ + * {Object} Properties from this symbolizer will be applied to symbolizers │ │ │ │ + * with missing properties. This can also be used to set a global │ │ │ │ + * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the │ │ │ │ + * following code before rendering any vector features: │ │ │ │ + * (code) │ │ │ │ + * OpenLayers.Renderer.defaultSymbolizer = { │ │ │ │ + * fillColor: "#808080", │ │ │ │ + * fillOpacity: 1, │ │ │ │ + * strokeColor: "#000000", │ │ │ │ + * strokeOpacity: 1, │ │ │ │ + * strokeWidth: 1, │ │ │ │ + * pointRadius: 3, │ │ │ │ + * graphicName: "square" │ │ │ │ + * }; │ │ │ │ + * (end) │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.defaultSymbolizer = { │ │ │ │ + fillColor: "#000000", │ │ │ │ + strokeColor: "#000000", │ │ │ │ + strokeWidth: 2, │ │ │ │ + fillOpacity: 1, │ │ │ │ + strokeOpacity: 1, │ │ │ │ + pointRadius: 0, │ │ │ │ + labelAlign: 'cm' │ │ │ │ +}; │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Renderer.symbol │ │ │ │ + * Coordinate arrays for well known (named) symbols. │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.symbol = { │ │ │ │ + "star": [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301, │ │ │ │ + 303, 215, 231, 161, 321, 161, 350, 75 │ │ │ │ + ], │ │ │ │ + "cross": [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4, │ │ │ │ + 4, 0 │ │ │ │ + ], │ │ │ │ + "x": [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0], │ │ │ │ + "square": [0, 0, 0, 1, 1, 1, 1, 0, 0, 0], │ │ │ │ + "triangle": [0, 10, 10, 10, 5, 0, 0, 10] │ │ │ │ +}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Tile.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/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ + * @requires OpenLayers/Renderer.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Tile │ │ │ │ - * This is a class designed to designate a single tile, however │ │ │ │ - * it is explicitly designed to do relatively little. Tiles store │ │ │ │ - * information about themselves -- such as the URL that they are related │ │ │ │ - * to, and their size - but do not add themselves to the layer div │ │ │ │ - * automatically, for example. Create a new tile with the │ │ │ │ - * <OpenLayers.Tile> constructor, or a subclass. │ │ │ │ - * │ │ │ │ - * TBD 3.0 - remove reference to url in above paragraph │ │ │ │ - * │ │ │ │ + * 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.Tile = OpenLayers.Class({ │ │ │ │ +OpenLayers.ElementsIndexer = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} An events object that handles all │ │ │ │ - * events on the tile. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * tile.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types: │ │ │ │ - * beforedraw - Triggered before the tile is drawn. Used to defer │ │ │ │ - * drawing to an animation queue. To defer drawing, listeners need │ │ │ │ - * to return false, which will abort drawing. The queue handler needs │ │ │ │ - * to call <draw>(true) to actually draw the tile. │ │ │ │ - * loadstart - Triggered when tile loading starts. │ │ │ │ - * loadend - Triggered when tile loading ends. │ │ │ │ - * loaderror - Triggered before the loadend event (i.e. when the tile is │ │ │ │ - * still hidden) if the tile could not be loaded. │ │ │ │ - * reload - Triggered when an already loading tile is reloaded. │ │ │ │ - * unload - Triggered before a tile is unloaded. │ │ │ │ + * Property: maxZIndex │ │ │ │ + * {Integer} This is the largest-most z-index value for a node │ │ │ │ + * contained within the indexer. │ │ │ │ */ │ │ │ │ - events: null, │ │ │ │ + maxZIndex: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: eventListeners │ │ │ │ - * {Object} If set as an option at construction, the eventListeners │ │ │ │ - * object will be registered with <OpenLayers.Events.on>. Object │ │ │ │ - * structure must be a listeners object as shown in the example for │ │ │ │ - * the events.on method. │ │ │ │ - * │ │ │ │ - * This options can be set in the ``tileOptions`` option from │ │ │ │ - * <OpenLayers.Layer.Grid>. For example, to be notified of the │ │ │ │ - * ``loadend`` event of each tiles: │ │ │ │ - * (code) │ │ │ │ - * new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', { │ │ │ │ - * tileOptions: { │ │ │ │ - * eventListeners: { │ │ │ │ - * 'loadend': function(evt) { │ │ │ │ - * // do something on loadend │ │ │ │ - * } │ │ │ │ - * } │ │ │ │ - * } │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ + * Property: order │ │ │ │ + * {Array<String>} This is an array of node id's stored in the │ │ │ │ + * order that they should show up on screen. Id's higher up in the │ │ │ │ + * array (higher array index) represent nodes with higher z-indeces. │ │ │ │ */ │ │ │ │ - eventListeners: null, │ │ │ │ + order: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: id │ │ │ │ - * {String} null │ │ │ │ - */ │ │ │ │ - id: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: layer │ │ │ │ - * {<OpenLayers.Layer>} layer the tile is attached to │ │ │ │ + * 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). │ │ │ │ */ │ │ │ │ - layer: null, │ │ │ │ + indices: 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. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - url: null, │ │ │ │ + compare: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: bounds │ │ │ │ - * {<OpenLayers.Bounds>} null │ │ │ │ + /** │ │ │ │ + * APIMethod: initialize │ │ │ │ + * Create a new indexer with │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * yOrdering - {Boolean} Whether to use y-ordering. │ │ │ │ */ │ │ │ │ - bounds: null, │ │ │ │ + initialize: function(yOrdering) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: size │ │ │ │ - * {<OpenLayers.Size>} null │ │ │ │ - */ │ │ │ │ - size: null, │ │ │ │ + this.compare = yOrdering ? │ │ │ │ + OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : │ │ │ │ + OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: position │ │ │ │ - * {<OpenLayers.Pixel>} Top Left pixel of the tile │ │ │ │ - */ │ │ │ │ - position: null, │ │ │ │ + this.clear(); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: isLoading │ │ │ │ - * {Boolean} Is the tile loading? │ │ │ │ - */ │ │ │ │ - isLoading: false, │ │ │ │ - │ │ │ │ - /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor. │ │ │ │ - * there is no need for the base tile class to have a url. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Tile │ │ │ │ - * Constructor for a new <OpenLayers.Tile> instance. │ │ │ │ + * 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: │ │ │ │ - * layer - {<OpenLayers.Layer>} layer that the tile will go in. │ │ │ │ - * position - {<OpenLayers.Pixel>} │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * url - {<String>} │ │ │ │ - * size - {<OpenLayers.Size>} │ │ │ │ - * options - {Object} │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ + 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); │ │ │ │ } │ │ │ │ │ │ │ │ - //give the tile a unique id based on its BBOX. │ │ │ │ - this.id = OpenLayers.Util.createUniqueID("Tile_"); │ │ │ │ + var nodeId = newNode.id; │ │ │ │ │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ + this.determineZIndex(newNode); │ │ │ │ │ │ │ │ - this.events = new OpenLayers.Events(this); │ │ │ │ - if (this.eventListeners instanceof Object) { │ │ │ │ - this.events.on(this.eventListeners); │ │ │ │ + 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); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIMethod: remove │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} The node to be removed. │ │ │ │ */ │ │ │ │ - unload: function() { │ │ │ │ - if (this.isLoading) { │ │ │ │ - this.isLoading = false; │ │ │ │ - this.events.triggerEvent("unload"); │ │ │ │ + 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: destroy │ │ │ │ - * Nullify references to prevent circular references and memory leaks. │ │ │ │ + /** │ │ │ │ + * APIMethod: clear │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.layer = null; │ │ │ │ - this.bounds = null; │ │ │ │ - this.size = null; │ │ │ │ - this.position = null; │ │ │ │ - │ │ │ │ - if (this.eventListeners) { │ │ │ │ - this.events.un(this.eventListeners); │ │ │ │ - } │ │ │ │ - this.events.destroy(); │ │ │ │ - this.eventListeners = null; │ │ │ │ - this.events = null; │ │ │ │ + clear: function() { │ │ │ │ + this.order = []; │ │ │ │ + this.indices = {}; │ │ │ │ + this.maxZIndex = 0; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ - * Clear whatever is currently in the tile, then return whether or not │ │ │ │ - * it should actually be re-drawn. This is an example implementation │ │ │ │ - * that can be overridden by subclasses. The minimum thing to do here │ │ │ │ - * is to call <clear> and return the result from <shouldDraw>. │ │ │ │ + * APIMethod: exists │ │ │ │ * │ │ │ │ * 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. │ │ │ │ + * node - {DOMElement} The node to test for existence. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Whether or not the node exists in the indexer? │ │ │ │ + */ │ │ │ │ + exists: function(node) { │ │ │ │ + return (this.indices[node.id] != null); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getZIndex │ │ │ │ + * Get the z-index value for the current node from the node data itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} The node whose z-index to get. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Whether or not the tile should actually be drawn. Returns null │ │ │ │ - * if a beforedraw listener returned false. │ │ │ │ + * {Integer} The z-index value for the specified node (from the node │ │ │ │ + * data itself). │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - return draw; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: shouldDraw │ │ │ │ - * Return whether or not the tile should actually be (re-)drawn. The only │ │ │ │ - * case where we *wouldn't* want to draw the tile is if the tile is outside │ │ │ │ - * its layer's maxExtent │ │ │ │ + * APIMethod: getNextElement │ │ │ │ + * Get the next element in the order stack. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * index - {Integer} The index of the current node in this.order. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Whether or not the tile should actually be drawn. │ │ │ │ + * {DOMElement} the node following the index passed in, or │ │ │ │ + * null. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - │ │ │ │ - return withinMaxExtent || this.layer.displayOutsideMaxExtent; │ │ │ │ }, │ │ │ │ │ │ │ │ + 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: setBounds │ │ │ │ - * Sets the bounds on this instance │ │ │ │ - * │ │ │ │ + * 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: │ │ │ │ - * bounds {<OpenLayers.Bounds>} │ │ │ │ + * indexer - {<OpenLayers.ElementsIndexer>} │ │ │ │ + * newNode - {DOMElement} │ │ │ │ + * nextNode - {DOMElement} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} │ │ │ │ */ │ │ │ │ - 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 │ │ │ │ - }); │ │ │ │ + Z_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ + var newZIndex = indexer.getZIndex(newNode); │ │ │ │ + │ │ │ │ + var returnVal = 0; │ │ │ │ + if (nextNode) { │ │ │ │ + var nextZIndex = indexer.getZIndex(nextNode); │ │ │ │ + returnVal = newZIndex - nextZIndex; │ │ │ │ } │ │ │ │ - this.bounds = bounds; │ │ │ │ + │ │ │ │ + return returnVal; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * Reposition the tile. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * 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: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * position - {<OpenLayers.Pixel>} │ │ │ │ - * redraw - {Boolean} Call draw method on tile after moving. │ │ │ │ - * Default is true │ │ │ │ + * indexer - {<OpenLayers.ElementsIndexer>} │ │ │ │ + * newNode - {DOMElement} │ │ │ │ + * nextNode - {DOMElement} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} │ │ │ │ */ │ │ │ │ - moveTo: function(bounds, position, redraw) { │ │ │ │ - if (redraw == null) { │ │ │ │ - redraw = true; │ │ │ │ - } │ │ │ │ + Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ + var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER( │ │ │ │ + indexer, │ │ │ │ + newNode, │ │ │ │ + nextNode │ │ │ │ + ); │ │ │ │ │ │ │ │ - this.setBounds(bounds); │ │ │ │ - this.position = position.clone(); │ │ │ │ - if (redraw) { │ │ │ │ - this.draw(); │ │ │ │ + // 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; │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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 │ │ │ │ + return returnVal; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Tile" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Tile/Image.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + /** │ │ │ │ + * APIMethod: Z_ORDER_Y_ORDER │ │ │ │ + * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it │ │ │ │ + * best describes which ordering methods have precedence (though, the │ │ │ │ + * name would be too long). This method orders nodes by their z-index, │ │ │ │ + * but does so in a way that, if there are other nodes with the same │ │ │ │ + * z-index, the nodes with the lower y position will be "closer" than │ │ │ │ + * those with a higher y position. If two nodes have the exact same y │ │ │ │ + * position, however, then this method will revert to using drawing │ │ │ │ + * order to decide placement. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * indexer - {<OpenLayers.ElementsIndexer>} │ │ │ │ + * newNode - {DOMElement} │ │ │ │ + * nextNode - {DOMElement} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} │ │ │ │ + */ │ │ │ │ + Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ + var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER( │ │ │ │ + indexer, │ │ │ │ + newNode, │ │ │ │ + nextNode │ │ │ │ + ); │ │ │ │ │ │ │ │ + if (nextNode && returnVal === 0) { │ │ │ │ + var result = nextNode._boundsBottom - newNode._boundsBottom; │ │ │ │ + returnVal = (result === 0) ? 1 : result; │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Tile.js │ │ │ │ - * @requires OpenLayers/Animation.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ - */ │ │ │ │ + return returnVal; │ │ │ │ + } │ │ │ │ +}; │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Tile.Image │ │ │ │ - * Instances of OpenLayers.Tile.Image are used to manage the image tiles │ │ │ │ - * used by various layers. Create a new image tile with the │ │ │ │ - * <OpenLayers.Tile.Image> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Tile> │ │ │ │ + * Class: OpenLayers.Renderer.Elements │ │ │ │ + * This is another virtual class in that it should never be instantiated by │ │ │ │ + * itself as a Renderer. It exists because there is *tons* of shared │ │ │ │ + * functionality between different vector libraries which use nodes/elements │ │ │ │ + * as a base for rendering vectors. │ │ │ │ + * │ │ │ │ + * The highlevel bits of code that are implemented here are the adding and │ │ │ │ + * removing of geometries, which is essentially the same for any │ │ │ │ + * element-based renderer. The details of creating each node and drawing the │ │ │ │ + * paths are of course different, but the machinery is the same. │ │ │ │ + * │ │ │ │ + * Inherits: │ │ │ │ + * - <OpenLayers.Renderer> │ │ │ │ */ │ │ │ │ -OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { │ │ │ │ +OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} An events object that handles all │ │ │ │ - * events on the tile. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * tile.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types (in addition to the <OpenLayers.Tile> events): │ │ │ │ - * beforeload - Triggered before an image is prepared for loading, when the │ │ │ │ - * url for the image is known already. Listeners may call <setImage> on │ │ │ │ - * the tile instance. If they do so, that image will be used and no new │ │ │ │ - * one will be created. │ │ │ │ + * Property: rendererRoot │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ + rendererRoot: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: url │ │ │ │ - * {String} The URL of the image being requested. No default. Filled in by │ │ │ │ - * layer.getURL() function. May be modified by loadstart listeners. │ │ │ │ + /** │ │ │ │ + * Property: root │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - url: null, │ │ │ │ + root: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: imgDiv │ │ │ │ - * {HTMLImageElement} The image for this tile. │ │ │ │ + /** │ │ │ │ + * Property: vectorRoot │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - imgDiv: null, │ │ │ │ + vectorRoot: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: frame │ │ │ │ - * {DOMElement} The image element is appended to the frame. Any gutter on │ │ │ │ - * the image will be hidden behind the frame. If no gutter is set, │ │ │ │ - * this will be null. │ │ │ │ + * Property: textRoot │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - frame: null, │ │ │ │ + textRoot: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: imageReloadAttempts │ │ │ │ - * {Integer} Attempts to load the image. │ │ │ │ + /** │ │ │ │ + * Property: xmlns │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - imageReloadAttempts: null, │ │ │ │ + xmlns: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: layerAlphaHack │ │ │ │ - * {Boolean} True if the png alpha hack needs to be applied on the layer's div. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - layerAlphaHack: null, │ │ │ │ + xOffset: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Property: rightOfDateLine │ │ │ │ + * {Boolean} Keeps track of the location of the map extent relative to the │ │ │ │ + * date line. The <setExtent> method compares this value (which is the one │ │ │ │ + * from the previous <setExtent> call) with the current position of the map │ │ │ │ + * extent relative to the date line and updates the xOffset when the extent │ │ │ │ + * has moved from one side of the date line to the other. │ │ │ │ */ │ │ │ │ - asyncRequestId: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: maxGetUrlLength │ │ │ │ - * {Number} If set, requests that would result in GET urls with more │ │ │ │ - * characters than the number provided will be made using form-encoded │ │ │ │ - * HTTP POST. It is good practice to avoid urls that are longer than 2048 │ │ │ │ - * characters. │ │ │ │ - * │ │ │ │ - * Caution: │ │ │ │ - * Older versions of Gecko based browsers (e.g. Firefox < 3.5) and most │ │ │ │ - * Opera versions do not fully support this option. On all browsers, │ │ │ │ - * transition effects are not supported if POST requests are used. │ │ │ │ + * Property: Indexer │ │ │ │ + * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer │ │ │ │ + * created upon initialization if the zIndexing or yOrdering options │ │ │ │ + * passed to this renderer's constructor are set to true. │ │ │ │ */ │ │ │ │ - maxGetUrlLength: null, │ │ │ │ + indexer: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: canvasContext │ │ │ │ - * {CanvasRenderingContext2D} A canvas context associated with │ │ │ │ - * the tile image. │ │ │ │ + * Constant: BACKGROUND_ID_SUFFIX │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - canvasContext: null, │ │ │ │ + BACKGROUND_ID_SUFFIX: "_background", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: crossOriginKeyword │ │ │ │ - * The value of the crossorigin keyword to use when loading images. This is │ │ │ │ - * only relevant when using <getCanvasContext> for tiles from remote │ │ │ │ - * origins and should be set to either 'anonymous' or 'use-credentials' │ │ │ │ - * for servers that send Access-Control-Allow-Origin headers with their │ │ │ │ - * tiles. │ │ │ │ + * Constant: LABEL_ID_SUFFIX │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - crossOriginKeyword: null, │ │ │ │ + LABEL_ID_SUFFIX: "_label", │ │ │ │ │ │ │ │ - /** 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. │ │ │ │ + /** │ │ │ │ + * Constant: LABEL_OUTLINE_SUFFIX │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ + LABEL_OUTLINE_SUFFIX: "_outline", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Tile.Image │ │ │ │ - * Constructor for a new <OpenLayers.Tile.Image> instance. │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Renderer.Elements │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer>} layer that the tile will go in. │ │ │ │ - * position - {<OpenLayers.Pixel>} │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * url - {<String>} Deprecated. Remove me in 3.0. │ │ │ │ - * size - {<OpenLayers.Size>} │ │ │ │ - * options - {Object} │ │ │ │ + * containerID - {String} │ │ │ │ + * options - {Object} options for this renderer. │ │ │ │ + * │ │ │ │ + * Supported options are: │ │ │ │ + * yOrdering - {Boolean} Whether to use y-ordering │ │ │ │ + * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored │ │ │ │ + * if yOrdering is set to true. │ │ │ │ */ │ │ │ │ - initialize: function(layer, position, bounds, url, size, options) { │ │ │ │ - OpenLayers.Tile.prototype.initialize.apply(this, arguments); │ │ │ │ + initialize: function(containerID, options) { │ │ │ │ + OpenLayers.Renderer.prototype.initialize.apply(this, arguments); │ │ │ │ │ │ │ │ - this.url = url; //deprecated remove me │ │ │ │ + this.rendererRoot = this.createRenderRoot(); │ │ │ │ + this.root = this.createRoot("_root"); │ │ │ │ + this.vectorRoot = this.createRoot("_vroot"); │ │ │ │ + this.textRoot = this.createRoot("_troot"); │ │ │ │ │ │ │ │ - this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack(); │ │ │ │ + this.root.appendChild(this.vectorRoot); │ │ │ │ + this.root.appendChild(this.textRoot); │ │ │ │ │ │ │ │ - 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); │ │ │ │ + this.rendererRoot.appendChild(this.root); │ │ │ │ + this.container.appendChild(this.rendererRoot); │ │ │ │ + │ │ │ │ + if (options && (options.zIndexing || options.yOrdering)) { │ │ │ │ + this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * nullify references to prevent circular references and memory leaks │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ 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); │ │ │ │ + │ │ │ │ + this.clear(); │ │ │ │ + │ │ │ │ + this.rendererRoot = null; │ │ │ │ + this.root = null; │ │ │ │ + this.xmlns = null; │ │ │ │ + │ │ │ │ + OpenLayers.Renderer.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ - * Check that a tile should be drawn, and draw it. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned │ │ │ │ - * false. │ │ │ │ + * Method: clear │ │ │ │ + * Remove all the elements from the root │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + clear: function() { │ │ │ │ + var child; │ │ │ │ + var root = this.vectorRoot; │ │ │ │ + if (root) { │ │ │ │ + while (child = root.firstChild) { │ │ │ │ + root.removeChild(child); │ │ │ │ } │ │ │ │ - if (this.isLoading) { │ │ │ │ - //if we're already loading, send 'reload' instead of 'loadstart'. │ │ │ │ - this._loadEvent = "reload"; │ │ │ │ - } else { │ │ │ │ - this.isLoading = true; │ │ │ │ - this._loadEvent = "loadstart"; │ │ │ │ + } │ │ │ │ + root = this.textRoot; │ │ │ │ + if (root) { │ │ │ │ + while (child = root.firstChild) { │ │ │ │ + root.removeChild(child); │ │ │ │ } │ │ │ │ - this.renderTile(); │ │ │ │ - this.positionTile(); │ │ │ │ - } else if (shouldDraw === false) { │ │ │ │ - this.unload(); │ │ │ │ } │ │ │ │ - return shouldDraw; │ │ │ │ + if (this.indexer) { │ │ │ │ + this.indexer.clear(); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: renderTile │ │ │ │ - * Internal function to actually initialize the image tile, │ │ │ │ - * position it correctly, and set its url. │ │ │ │ + * Method: setExtent │ │ │ │ + * Set the visible part of the layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * extent - {<OpenLayers.Bounds>} │ │ │ │ + * resolutionChanged - {Boolean} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true to notify the layer that the new extent does not exceed │ │ │ │ + * the coordinate range, and the features will not need to be redrawn. │ │ │ │ + * False otherwise. │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ + setExtent: function(extent, resolutionChanged) { │ │ │ │ + var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) { │ │ │ │ + var rightOfDateLine, │ │ │ │ + ratio = extent.getWidth() / this.map.getExtent().getWidth(), │ │ │ │ + extent = extent.scale(1 / ratio), │ │ │ │ + world = this.map.getMaxExtent(); │ │ │ │ + if (world.right > extent.left && world.right < extent.right) { │ │ │ │ + rightOfDateLine = true; │ │ │ │ + } else if (world.left > extent.left && world.left < extent.right) { │ │ │ │ + rightOfDateLine = false; │ │ │ │ + } │ │ │ │ + if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) { │ │ │ │ + coordSysUnchanged = false; │ │ │ │ + this.xOffset = rightOfDateLine === true ? │ │ │ │ + world.getWidth() / resolution : 0; │ │ │ │ + } │ │ │ │ + this.rightOfDateLine = rightOfDateLine; │ │ │ │ } │ │ │ │ + return coordSysUnchanged; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: 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. │ │ │ │ + /** │ │ │ │ + * Method: getNodeType │ │ │ │ + * This function is in charge of asking the specific renderer which type │ │ │ │ + * of node to create for the given geometry and style. All geometries │ │ │ │ + * in an Elements-based renderer consist of one node and some │ │ │ │ + * attributes. We have the nodeFactory() function which creates a node │ │ │ │ + * for us, but it takes a 'type' as input, and that is precisely what │ │ │ │ + * this function tells us. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The corresponding node type for the specified geometry │ │ │ │ */ │ │ │ │ - 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"; │ │ │ │ - }, │ │ │ │ + getNodeType: function(geometry, style) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: drawGeometry │ │ │ │ + * Draw the geometry, creating new nodes, setting paths, setting style, │ │ │ │ + * setting featureId on the node. This method should only be called │ │ │ │ + * by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {String} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true if the geometry has been drawn completely; null if │ │ │ │ + * incomplete; false otherwise │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - this.setImgSrc(); │ │ │ │ - if (this.layerAlphaHack === true) { │ │ │ │ - img.style.filter = ""; │ │ │ │ + return rendered; │ │ │ │ + } │ │ │ │ + │ │ │ │ + rendered = false; │ │ │ │ + var removeBackground = false; │ │ │ │ + if (style.display != "none") { │ │ │ │ + if (style.backgroundGraphic) { │ │ │ │ + this.redrawBackgroundNode(geometry.id, geometry, style, │ │ │ │ + featureId); │ │ │ │ + } else { │ │ │ │ + removeBackground = true; │ │ │ │ } │ │ │ │ - OpenLayers.Element.removeClass(img, "olImageLoadError"); │ │ │ │ + rendered = this.redrawNode(geometry.id, geometry, style, │ │ │ │ + featureId); │ │ │ │ } │ │ │ │ - this.canvasContext = null; │ │ │ │ + if (rendered == false) { │ │ │ │ + var node = document.getElementById(geometry.id); │ │ │ │ + if (node) { │ │ │ │ + if (node._style.backgroundGraphic) { │ │ │ │ + removeBackground = true; │ │ │ │ + } │ │ │ │ + node.parentNode.removeChild(node); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (removeBackground) { │ │ │ │ + var node = document.getElementById( │ │ │ │ + geometry.id + this.BACKGROUND_ID_SUFFIX); │ │ │ │ + if (node) { │ │ │ │ + node.parentNode.removeChild(node); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return rendered; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getImage │ │ │ │ - * Returns or creates and returns the tile image. │ │ │ │ + * Method: redrawNode │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * id - {String} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {String} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true if the complete geometry could be drawn, null if parts of │ │ │ │ + * the geometry could not be drawn, false otherwise │ │ │ │ */ │ │ │ │ - getImage: function() { │ │ │ │ - if (!this.imgDiv) { │ │ │ │ - this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false); │ │ │ │ + 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)); │ │ │ │ │ │ │ │ - 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%"; │ │ │ │ + // 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); │ │ │ │ } │ │ │ │ - if (this.frame) { │ │ │ │ - this.frame.appendChild(this.imgDiv); │ │ │ │ + } 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); │ │ │ │ } │ │ │ │ } │ │ │ │ │ │ │ │ - return this.imgDiv; │ │ │ │ + this.postDraw(node); │ │ │ │ + │ │ │ │ + return drawResult.complete; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: redrawBackgroundNode │ │ │ │ + * Redraws the node using special 'background' style properties. Basically │ │ │ │ + * just calls redrawNode(), but instead of directly using the │ │ │ │ + * 'externalGraphic', 'graphicXOffset', 'graphicYOffset', and │ │ │ │ + * 'graphicZIndex' properties directly from the specified 'style' │ │ │ │ + * parameter, we create a new style object and set those properties │ │ │ │ + * from the corresponding 'background'-prefixed properties from │ │ │ │ + * specified 'style' parameter. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * id - {String} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * featureId - {String} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true if the complete geometry could be drawn, null if parts of │ │ │ │ + * the geometry could not be drawn, false otherwise │ │ │ │ */ │ │ │ │ - setImage: function(img) { │ │ │ │ - this.imgDiv = img; │ │ │ │ + redrawBackgroundNode: function(id, geometry, style, featureId) { │ │ │ │ + var backgroundStyle = OpenLayers.Util.extend({}, style); │ │ │ │ + │ │ │ │ + // Set regular style attributes to apply to the background styles. │ │ │ │ + backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic; │ │ │ │ + backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset; │ │ │ │ + backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset; │ │ │ │ + backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex; │ │ │ │ + backgroundStyle.graphicWidth = backgroundStyle.backgroundWidth || backgroundStyle.graphicWidth; │ │ │ │ + backgroundStyle.graphicHeight = backgroundStyle.backgroundHeight || backgroundStyle.graphicHeight; │ │ │ │ + │ │ │ │ + // Erase background styles. │ │ │ │ + backgroundStyle.backgroundGraphic = null; │ │ │ │ + backgroundStyle.backgroundXOffset = null; │ │ │ │ + backgroundStyle.backgroundYOffset = null; │ │ │ │ + backgroundStyle.backgroundGraphicZIndex = null; │ │ │ │ + │ │ │ │ + return this.redrawNode( │ │ │ │ + id + this.BACKGROUND_ID_SUFFIX, │ │ │ │ + geometry, │ │ │ │ + backgroundStyle, │ │ │ │ + null │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: initImage │ │ │ │ - * Creates the content for the frame on the tile. │ │ │ │ + * Method: drawGeometryNode │ │ │ │ + * Given a node, draw a geometry on the specified layer. │ │ │ │ + * node and geometry are required arguments, style is optional. │ │ │ │ + * This method is only called by the render itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} a hash with properties "node" (the drawn node) and "complete" │ │ │ │ + * (null if parts of the geometry could not be drawn, false if nothing │ │ │ │ + * could be drawn) │ │ │ │ */ │ │ │ │ - initImage: function() { │ │ │ │ - if (!this.url && !this.imgDiv) { │ │ │ │ - // fast path out - if there is no tile url and no previous image │ │ │ │ - this.isLoading = false; │ │ │ │ - return; │ │ │ │ + drawGeometryNode: function(node, geometry, style) { │ │ │ │ + style = style || node._style; │ │ │ │ + │ │ │ │ + var options = { │ │ │ │ + 'isFilled': style.fill === undefined ? │ │ │ │ + true : style.fill, │ │ │ │ + 'isStroked': style.stroke === undefined ? │ │ │ │ + !!style.strokeWidth : style.stroke │ │ │ │ + }; │ │ │ │ + var drawn; │ │ │ │ + switch (geometry.CLASS_NAME) { │ │ │ │ + case "OpenLayers.Geometry.Point": │ │ │ │ + if (style.graphic === false) { │ │ │ │ + options.isFilled = false; │ │ │ │ + options.isStroked = false; │ │ │ │ + } │ │ │ │ + drawn = this.drawPoint(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LineString": │ │ │ │ + options.isFilled = false; │ │ │ │ + drawn = this.drawLineString(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LinearRing": │ │ │ │ + drawn = this.drawLinearRing(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Polygon": │ │ │ │ + drawn = this.drawPolygon(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Rectangle": │ │ │ │ + drawn = this.drawRectangle(node, geometry); │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + break; │ │ │ │ } │ │ │ │ - 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 │ │ │ │ - ); │ │ │ │ + │ │ │ │ + node._options = options; │ │ │ │ + │ │ │ │ + //set style │ │ │ │ + //TBD simplify this │ │ │ │ + if (drawn != false) { │ │ │ │ + return { │ │ │ │ + node: this.setStyle(node, style, options, geometry), │ │ │ │ + complete: drawn │ │ │ │ + }; │ │ │ │ } 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); │ │ │ │ + return false; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setImgSrc │ │ │ │ - * Sets the source for the tile image │ │ │ │ - * │ │ │ │ + * Method: postDraw │ │ │ │ + * Things that have do be done after the geometry node is appended │ │ │ │ + * to its parent node. To be overridden by subclasses. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * url - {String} or undefined to hide the image │ │ │ │ + * node - {DOMElement} │ │ │ │ */ │ │ │ │ - setImgSrc: function(url) { │ │ │ │ - var img = this.imgDiv; │ │ │ │ - if (url) { │ │ │ │ - img.style.visibility = 'hidden'; │ │ │ │ - img.style.opacity = 0; │ │ │ │ - // don't set crossOrigin if the url is a data URL │ │ │ │ - if (this.crossOriginKeyword) { │ │ │ │ - if (url.substr(0, 5) !== 'data:') { │ │ │ │ - img.setAttribute("crossorigin", this.crossOriginKeyword); │ │ │ │ - } else { │ │ │ │ - img.removeAttribute("crossorigin"); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - img.src = url; │ │ │ │ - } else { │ │ │ │ - // Remove reference to the image, and leave it to the browser's │ │ │ │ - // caching and garbage collection. │ │ │ │ - this.stopLoading(); │ │ │ │ - this.imgDiv = null; │ │ │ │ - if (img.parentNode) { │ │ │ │ - img.parentNode.removeChild(img); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + postDraw: function(node) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getTile │ │ │ │ - * Get the tile's markup. │ │ │ │ - * │ │ │ │ + * Method: drawPoint │ │ │ │ + * Virtual function for drawing Point Geometry. │ │ │ │ + * Should be implemented by subclasses. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} The tile's markup │ │ │ │ + * {DOMElement} or false if the renderer could not draw the point │ │ │ │ */ │ │ │ │ - getTile: function() { │ │ │ │ - return this.frame ? this.frame : this.getImage(); │ │ │ │ - }, │ │ │ │ + drawPoint: function(node, geometry) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ - * │ │ │ │ + * Method: drawLineString │ │ │ │ + * Virtual function for drawing LineString Geometry. │ │ │ │ + * Should be implemented by subclasses. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} The markup, or undefined if the tile has no image │ │ │ │ - * or if it's currently loading. │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components of │ │ │ │ + * the linestring, or false if nothing could be drawn │ │ │ │ */ │ │ │ │ - createBackBuffer: function() { │ │ │ │ - if (!this.imgDiv || this.isLoading) { │ │ │ │ - return; │ │ │ │ + drawLineString: function(node, geometry) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: drawLinearRing │ │ │ │ + * Virtual function for drawing LinearRing Geometry. │ │ │ │ + * Should be implemented by subclasses. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components │ │ │ │ + * of the linear ring, or false if nothing could be drawn │ │ │ │ + */ │ │ │ │ + drawLinearRing: function(node, geometry) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: drawPolygon │ │ │ │ + * Virtual function for drawing Polygon Geometry. │ │ │ │ + * Should be implemented by subclasses. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components │ │ │ │ + * of the polygon, or false if nothing could be drawn │ │ │ │ + */ │ │ │ │ + drawPolygon: function(node, geometry) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: drawRectangle │ │ │ │ + * Virtual function for drawing Rectangle Geometry. │ │ │ │ + * Should be implemented by subclasses. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the renderer could not draw the rectangle │ │ │ │ + */ │ │ │ │ + drawRectangle: function(node, geometry) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: drawCircle │ │ │ │ + * Virtual function for drawing Circle Geometry. │ │ │ │ + * Should be implemented by subclasses. │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the renderer could not draw the circle │ │ │ │ + */ │ │ │ │ + drawCircle: function(node, geometry) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: removeText │ │ │ │ + * Removes a label │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * featureId - {String} │ │ │ │ + */ │ │ │ │ + removeText: function(featureId) { │ │ │ │ + var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX); │ │ │ │ + if (label) { │ │ │ │ + this.textRoot.removeChild(label); │ │ │ │ } │ │ │ │ - var backBuffer; │ │ │ │ - if (this.frame) { │ │ │ │ - backBuffer = this.frame.cloneNode(false); │ │ │ │ - backBuffer.appendChild(this.imgDiv); │ │ │ │ - } else { │ │ │ │ - backBuffer = this.imgDiv; │ │ │ │ + var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX); │ │ │ │ + if (outline) { │ │ │ │ + this.textRoot.removeChild(outline); │ │ │ │ } │ │ │ │ - this.imgDiv = null; │ │ │ │ - return backBuffer; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onImageLoad │ │ │ │ - * Handler for the image onload event │ │ │ │ + * Method: getFeatureIdFromEvent │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} An <OpenLayers.Event> object │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A feature id or undefined. │ │ │ │ */ │ │ │ │ - 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"); │ │ │ │ + getFeatureIdFromEvent: function(evt) { │ │ │ │ + var target = evt.target; │ │ │ │ + var useElement = target && target.correspondingUseElement; │ │ │ │ + var node = useElement ? useElement : (target || evt.srcElement); │ │ │ │ + return node._featureId; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.layerAlphaHack === true) { │ │ │ │ - img.style.filter = │ │ │ │ - "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + │ │ │ │ - img.src + "', sizingMethod='scale')"; │ │ │ │ + /** │ │ │ │ + * Method: eraseGeometry │ │ │ │ + * Erase a geometry from the renderer. In the case of a multi-geometry, │ │ │ │ + * we cycle through and recurse on ourselves. Otherwise, we look for a │ │ │ │ + * node with the geometry.id, destroy its geometry, and remove it from │ │ │ │ + * the DOM. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * featureId - {String} │ │ │ │ + */ │ │ │ │ + eraseGeometry: function(geometry, featureId) { │ │ │ │ + if ((geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPoint") || │ │ │ │ + (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiLineString") || │ │ │ │ + (geometry.CLASS_NAME == "OpenLayers.Geometry.MultiPolygon") || │ │ │ │ + (geometry.CLASS_NAME == "OpenLayers.Geometry.Collection")) { │ │ │ │ + for (var i = 0, len = geometry.components.length; i < len; i++) { │ │ │ │ + this.eraseGeometry(geometry.components[i], featureId); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + var element = OpenLayers.Util.getElement(geometry.id); │ │ │ │ + if (element && element.parentNode) { │ │ │ │ + if (element.geometry) { │ │ │ │ + element.geometry.destroy(); │ │ │ │ + element.geometry = null; │ │ │ │ + } │ │ │ │ + element.parentNode.removeChild(element); │ │ │ │ + │ │ │ │ + if (this.indexer) { │ │ │ │ + this.indexer.remove(element); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (element._style.backgroundGraphic) { │ │ │ │ + var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX; │ │ │ │ + var bElem = OpenLayers.Util.getElement(backgroundId); │ │ │ │ + if (bElem && bElem.parentNode) { │ │ │ │ + // No need to destroy the geometry since the element and the background │ │ │ │ + // node share the same geometry. │ │ │ │ + bElem.parentNode.removeChild(bElem); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: onImageError │ │ │ │ - * Handler for the image onerror event │ │ │ │ + /** │ │ │ │ + * Method: nodeFactory │ │ │ │ + * Create new node of the specified type, with the (optional) specified id. │ │ │ │ + * │ │ │ │ + * If node already exists with same ID and a different type, we remove it │ │ │ │ + * and then call ourselves again to recreate it. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * id - {String} │ │ │ │ + * type - {String} type Kind of node to draw. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A new node of the given type and id. │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ + nodeFactory: function(id, type) { │ │ │ │ + var node = OpenLayers.Util.getElement(id); │ │ │ │ + if (node) { │ │ │ │ + if (!this.nodeTypeCompare(node, type)) { │ │ │ │ + node.parentNode.removeChild(node); │ │ │ │ + node = this.nodeFactory(id, type); │ │ │ │ } │ │ │ │ + } else { │ │ │ │ + node = this.createNode(type, id); │ │ │ │ } │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Method: nodeTypeCompare │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * type - {String} Kind of node │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ + * This function must be overridden by subclasses. │ │ │ │ + */ │ │ │ │ + nodeTypeCompare: function(node, type) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: createNode │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * type - {String} Kind of node to draw. │ │ │ │ + * id - {String} Id for node. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A new node of the given type and id. │ │ │ │ + * This function must be overridden by subclasses. │ │ │ │ + */ │ │ │ │ + createNode: function(type, id) {}, │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: stopLoading │ │ │ │ - * Stops a loading sequence so <onImageLoad> won't be executed. │ │ │ │ + * Method: moveRoot │ │ │ │ + * moves this renderer's root to a different renderer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * renderer - {<OpenLayers.Renderer>} target renderer for the moved root │ │ │ │ */ │ │ │ │ - stopLoading: function() { │ │ │ │ - OpenLayers.Event.stopObservingElement(this.imgDiv); │ │ │ │ - window.clearTimeout(this._loadTimeout); │ │ │ │ - delete this._loadTimeout; │ │ │ │ + moveRoot: function(renderer) { │ │ │ │ + var root = this.root; │ │ │ │ + if (renderer.root.parentNode == this.rendererRoot) { │ │ │ │ + root = renderer.root; │ │ │ │ + } │ │ │ │ + root.parentNode.removeChild(root); │ │ │ │ + renderer.rendererRoot.appendChild(root); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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) │ │ │ │ - * │ │ │ │ + * 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: │ │ │ │ - * {Boolean} │ │ │ │ + * {String} the id of the output layer. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ + getRenderLayerId: function() { │ │ │ │ + return this.root.parentNode.parentNode.id; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Tile.Image" │ │ │ │ + /** │ │ │ │ + * Method: isComplexSymbol │ │ │ │ + * Determines if a symbol cannot be rendered using drawCircle │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * graphicName - {String} │ │ │ │ + * │ │ │ │ + * Returns │ │ │ │ + * {Boolean} true if the symbol is complex, false if not │ │ │ │ + */ │ │ │ │ + isComplexSymbol: function(graphicName) { │ │ │ │ + return (graphicName != "circle") && !!graphicName; │ │ │ │ + }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.Elements" │ │ │ │ }); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Tile.Image.IMAGE │ │ │ │ - * {HTMLImageElement} The image for a tile. │ │ │ │ - */ │ │ │ │ -OpenLayers.Tile.Image.IMAGE = (function() { │ │ │ │ - var img = new Image(); │ │ │ │ - img.className = "olTileImage"; │ │ │ │ - // avoid image gallery menu in IE6 │ │ │ │ - img.galleryImg = "no"; │ │ │ │ - return img; │ │ │ │ -}()); │ │ │ │ - │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/Grid.js │ │ │ │ + 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/Layer/HTTPRequest.js │ │ │ │ - * @requires OpenLayers/Tile/Image.js │ │ │ │ + * @requires OpenLayers/Renderer/Elements.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.Grid │ │ │ │ - * Base class for layers that use a lattice of tiles. Create a new grid │ │ │ │ - * layer with the <OpenLayers.Layer.Grid> constructor. │ │ │ │ + * Class: OpenLayers.Renderer.VML │ │ │ │ + * Render vector features in browsers with VML capability. Construct a new │ │ │ │ + * VML renderer with the <OpenLayers.Renderer.VML> constructor. │ │ │ │ + * │ │ │ │ + * Note that for all calculations in this class, we use (num | 0) to truncate a │ │ │ │ + * float value to an integer. This is done because it seems that VML doesn't │ │ │ │ + * support float values. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.HTTPRequest> │ │ │ │ + * - <OpenLayers.Renderer.Elements> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { │ │ │ │ +OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: tileSize │ │ │ │ - * {<OpenLayers.Size>} │ │ │ │ + * Property: xmlns │ │ │ │ + * {String} XML Namespace URN │ │ │ │ */ │ │ │ │ - tileSize: null, │ │ │ │ + xmlns: "urn:schemas-microsoft-com:vml", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: tileOriginCorner │ │ │ │ - * {String} If the <tileOrigin> property is not provided, the tile origin │ │ │ │ - * will be derived from the layer's <maxExtent>. The corner of the │ │ │ │ - * <maxExtent> used is determined by this property. Acceptable values │ │ │ │ - * are "tl" (top left), "tr" (top right), "bl" (bottom left), and "br" │ │ │ │ - * (bottom right). Default is "bl". │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - tileOriginCorner: "bl", │ │ │ │ + symbolCache: {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: tileOrigin │ │ │ │ - * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles. │ │ │ │ - * If provided, requests for tiles at all resolutions will be aligned │ │ │ │ - * with this location (no tiles shall overlap this location). If │ │ │ │ - * not provided, the grid of tiles will be aligned with the layer's │ │ │ │ - * <maxExtent>. Default is ``null``. │ │ │ │ - */ │ │ │ │ - tileOrigin: null, │ │ │ │ - │ │ │ │ - /** APIProperty: tileOptions │ │ │ │ - * {Object} optional configuration options for <OpenLayers.Tile> instances │ │ │ │ - * created by this Layer, if supported by the tile class. │ │ │ │ + * Property: offset │ │ │ │ + * {Object} Hash with "x" and "y" properties │ │ │ │ */ │ │ │ │ - tileOptions: null, │ │ │ │ + offset: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: tileClass │ │ │ │ - * {<OpenLayers.Tile>} The tile class to use for this layer. │ │ │ │ - * Defaults is OpenLayers.Tile.Image. │ │ │ │ + * Constructor: OpenLayers.Renderer.VML │ │ │ │ + * Create a new VML renderer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * containerID - {String} The id for the element that contains the renderer │ │ │ │ */ │ │ │ │ - tileClass: OpenLayers.Tile.Image, │ │ │ │ + initialize: function(containerID) { │ │ │ │ + if (!this.supported()) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (!document.namespaces.olv) { │ │ │ │ + document.namespaces.add("olv", this.xmlns); │ │ │ │ + var style = document.createStyleSheet(); │ │ │ │ + var shapes = ['shape', 'rect', 'oval', 'fill', 'stroke', 'imagedata', 'group', 'textbox']; │ │ │ │ + for (var i = 0, len = shapes.length; i < len; i++) { │ │ │ │ + │ │ │ │ + style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " + │ │ │ │ + "position: absolute; display: inline-block;"); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + OpenLayers.Renderer.Elements.prototype.initialize.apply(this, │ │ │ │ + arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: grid │ │ │ │ - * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is │ │ │ │ - * an array of tiles. │ │ │ │ + * APIMethod: supported │ │ │ │ + * Determine whether a browser supports this renderer. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The browser supports the VML renderer │ │ │ │ */ │ │ │ │ - grid: null, │ │ │ │ + supported: function() { │ │ │ │ + return !!(document.namespaces); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: setExtent │ │ │ │ + * Set the renderer's extent │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * extent - {<OpenLayers.Bounds>} │ │ │ │ + * resolutionChanged - {Boolean} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true to notify the layer that the new extent does not exceed │ │ │ │ + * the coordinate range, and the features will not need to be redrawn. │ │ │ │ */ │ │ │ │ - singleTile: false, │ │ │ │ + setExtent: function(extent, resolutionChanged) { │ │ │ │ + var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + │ │ │ │ + var left = (extent.left / resolution) | 0; │ │ │ │ + var top = (extent.top / resolution - this.size.h) | 0; │ │ │ │ + if (resolutionChanged || !this.offset) { │ │ │ │ + this.offset = { │ │ │ │ + x: left, │ │ │ │ + y: top │ │ │ │ + }; │ │ │ │ + left = 0; │ │ │ │ + top = 0; │ │ │ │ + } else { │ │ │ │ + left = left - this.offset.x; │ │ │ │ + top = top - this.offset.y; │ │ │ │ + } │ │ │ │ + │ │ │ │ + │ │ │ │ + var org = (left - this.xOffset) + " " + top; │ │ │ │ + this.root.coordorigin = org; │ │ │ │ + var roots = [this.root, this.vectorRoot, this.textRoot]; │ │ │ │ + var root; │ │ │ │ + for (var i = 0, len = roots.length; i < len; ++i) { │ │ │ │ + root = roots[i]; │ │ │ │ + │ │ │ │ + var size = this.size.w + " " + this.size.h; │ │ │ │ + root.coordsize = size; │ │ │ │ + │ │ │ │ + } │ │ │ │ + // flip the VML display Y axis upside down so it │ │ │ │ + // matches the display Y axis of the map │ │ │ │ + this.root.style.flip = "y"; │ │ │ │ + │ │ │ │ + return coordSysUnchanged; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** 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. │ │ │ │ + * Method: setSize │ │ │ │ + * Set the size of the drawing surface │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * size - {<OpenLayers.Size>} the size of the drawing surface │ │ │ │ */ │ │ │ │ - buffer: 0, │ │ │ │ + setSize: function(size) { │ │ │ │ + OpenLayers.Renderer.prototype.setSize.apply(this, arguments); │ │ │ │ + │ │ │ │ + // setting width and height on all roots to avoid flicker which we │ │ │ │ + // would get with 100% width and height on child roots │ │ │ │ + var roots = [ │ │ │ │ + this.rendererRoot, │ │ │ │ + this.root, │ │ │ │ + this.vectorRoot, │ │ │ │ + this.textRoot │ │ │ │ + ]; │ │ │ │ + var w = this.size.w + "px"; │ │ │ │ + var h = this.size.h + "px"; │ │ │ │ + var root; │ │ │ │ + for (var i = 0, len = roots.length; i < len; ++i) { │ │ │ │ + root = roots[i]; │ │ │ │ + root.style.width = w; │ │ │ │ + root.style.height = h; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: transitionEffect │ │ │ │ - * {String} The transition effect to use when the map is zoomed. │ │ │ │ - * Two posible values: │ │ │ │ + * Method: getNodeType │ │ │ │ + * Get the node type for a geometry and style │ │ │ │ * │ │ │ │ - * "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. │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ * │ │ │ │ - * Using "resize" on non-opaque layers can cause undesired visual │ │ │ │ - * effects. Set transitionEffect to null in this case. │ │ │ │ + * Returns: │ │ │ │ + * {String} The corresponding node type for the specified geometry │ │ │ │ */ │ │ │ │ - transitionEffect: "resize", │ │ │ │ + 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: numLoadingTiles │ │ │ │ - * {Integer} How many tiles are still loading? │ │ │ │ + * Method: setStyle │ │ │ │ + * Use to set all the style attributes to a VML node. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} An VML element to decorate │ │ │ │ + * style - {Object} │ │ │ │ + * options - {Object} Currently supported options include │ │ │ │ + * 'isFilled' {Boolean} and │ │ │ │ + * 'isStroked' {Boolean} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ */ │ │ │ │ - numLoadingTiles: 0, │ │ │ │ + setStyle: function(node, style, options, geometry) { │ │ │ │ + style = style || node._style; │ │ │ │ + options = options || node._options; │ │ │ │ + var fillColor = style.fillColor; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: serverResolutions │ │ │ │ - * {Array(Number}} This property is documented in subclasses as │ │ │ │ - * an API property. │ │ │ │ - */ │ │ │ │ - serverResolutions: null, │ │ │ │ + var title = style.title || style.graphicTitle; │ │ │ │ + if (title) { │ │ │ │ + node.title = title; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: loading │ │ │ │ - * {Boolean} Indicates if tiles are being loaded. │ │ │ │ - */ │ │ │ │ - loading: false, │ │ │ │ + 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; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: backBuffer │ │ │ │ - * {DOMElement} The back buffer. │ │ │ │ - */ │ │ │ │ - backBuffer: null, │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var xOffset = (style.graphicXOffset != undefined) ? │ │ │ │ + style.graphicXOffset : -(0.5 * width); │ │ │ │ + var yOffset = (style.graphicYOffset != undefined) ? │ │ │ │ + style.graphicYOffset : -(0.5 * height); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: gridResolution │ │ │ │ - * {Number} The resolution of the current grid. Used for backbuffer and │ │ │ │ - * client zoom. This property is updated every time the grid is │ │ │ │ - * initialized. │ │ │ │ - */ │ │ │ │ - gridResolution: null, │ │ │ │ + node.style.left = ((((geometry.x - this.featureDx) / resolution - this.offset.x) + xOffset) | 0) + "px"; │ │ │ │ + node.style.top = (((geometry.y / resolution - this.offset.y) - (yOffset + height)) | 0) + "px"; │ │ │ │ + node.style.width = width + "px"; │ │ │ │ + node.style.height = height + "px"; │ │ │ │ + node.style.flip = "y"; │ │ │ │ + │ │ │ │ + // modify fillColor and options for stroke styling below │ │ │ │ + fillColor = "none"; │ │ │ │ + options.isStroked = false; │ │ │ │ + } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ + var cache = this.importSymbol(style.graphicName); │ │ │ │ + node.path = cache.path; │ │ │ │ + node.coordorigin = cache.left + "," + cache.bottom; │ │ │ │ + var size = cache.size; │ │ │ │ + node.coordsize = size + "," + size; │ │ │ │ + this.drawCircle(node, geometry, style.pointRadius); │ │ │ │ + node.style.flip = "y"; │ │ │ │ + } else { │ │ │ │ + this.drawCircle(node, geometry, style.pointRadius); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // fill │ │ │ │ + if (options.isFilled) { │ │ │ │ + node.fillcolor = fillColor; │ │ │ │ + } else { │ │ │ │ + node.filled = "false"; │ │ │ │ + } │ │ │ │ + var fills = node.getElementsByTagName("fill"); │ │ │ │ + var fill = (fills.length == 0) ? null : fills[0]; │ │ │ │ + if (!options.isFilled) { │ │ │ │ + if (fill) { │ │ │ │ + node.removeChild(fill); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + if (!fill) { │ │ │ │ + fill = this.createNode('olv:fill', node.id + "_fill"); │ │ │ │ + } │ │ │ │ + fill.opacity = style.fillOpacity; │ │ │ │ + │ │ │ │ + if (node._geometryClass === "OpenLayers.Geometry.Point" && │ │ │ │ + style.externalGraphic) { │ │ │ │ + │ │ │ │ + // override fillOpacity │ │ │ │ + if (style.graphicOpacity) { │ │ │ │ + fill.opacity = style.graphicOpacity; │ │ │ │ + } │ │ │ │ + │ │ │ │ + fill.src = style.externalGraphic; │ │ │ │ + fill.type = "frame"; │ │ │ │ + │ │ │ │ + if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ + fill.aspect = "atmost"; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (fill.parentNode != node) { │ │ │ │ + node.appendChild(fill); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // additional rendering for rotated graphics or symbols │ │ │ │ + var rotation = style.rotation; │ │ │ │ + if ((rotation !== undefined || node._rotation !== undefined)) { │ │ │ │ + node._rotation = rotation; │ │ │ │ + if (style.externalGraphic) { │ │ │ │ + this.graphicRotate(node, xOffset, yOffset, style); │ │ │ │ + // make the fill fully transparent, because we now have │ │ │ │ + // the graphic as imagedata element. We cannot just remove │ │ │ │ + // the fill, because this is part of the hack described │ │ │ │ + // in graphicRotate │ │ │ │ + fill.opacity = 0; │ │ │ │ + } else if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ + node.style.rotation = rotation || 0; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // stroke │ │ │ │ + var strokes = node.getElementsByTagName("stroke"); │ │ │ │ + var stroke = (strokes.length == 0) ? null : strokes[0]; │ │ │ │ + if (!options.isStroked) { │ │ │ │ + node.stroked = false; │ │ │ │ + if (stroke) { │ │ │ │ + stroke.on = false; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + if (!stroke) { │ │ │ │ + stroke = this.createNode('olv:stroke', node.id + "_stroke"); │ │ │ │ + node.appendChild(stroke); │ │ │ │ + } │ │ │ │ + stroke.on = true; │ │ │ │ + stroke.color = style.strokeColor; │ │ │ │ + stroke.weight = style.strokeWidth + "px"; │ │ │ │ + stroke.opacity = style.strokeOpacity; │ │ │ │ + stroke.endcap = style.strokeLinecap == 'butt' ? 'flat' : │ │ │ │ + (style.strokeLinecap || 'round'); │ │ │ │ + if (style.strokeDashstyle) { │ │ │ │ + stroke.dashstyle = this.dashStyle(style); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ + node.style.cursor = style.cursor; │ │ │ │ + } │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: backBufferResolution │ │ │ │ - * {Number} The resolution of the current back buffer. This property is │ │ │ │ - * updated each time a back buffer is created. │ │ │ │ + * Method: graphicRotate │ │ │ │ + * If a point is to be styled with externalGraphic and rotation, VML fills │ │ │ │ + * cannot be used to display the graphic, because rotation of graphic │ │ │ │ + * fills is not supported by the VML implementation of Internet Explorer. │ │ │ │ + * This method creates a olv:imagedata element inside the VML node, │ │ │ │ + * DXImageTransform.Matrix and BasicImage filters for rotation and │ │ │ │ + * opacity, and a 3-step hack to remove rendering artefacts from the │ │ │ │ + * graphic and preserve the ability of graphics to trigger events. │ │ │ │ + * Finally, OpenLayers methods are used to determine the correct │ │ │ │ + * insertion point of the rotated image, because DXImageTransform.Matrix │ │ │ │ + * does the rotation without the ability to specify a rotation center │ │ │ │ + * point. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * xOffset - {Number} rotation center relative to image, x coordinate │ │ │ │ + * yOffset - {Number} rotation center relative to image, y coordinate │ │ │ │ + * style - {Object} │ │ │ │ */ │ │ │ │ - backBufferResolution: null, │ │ │ │ + graphicRotate: function(node, xOffset, yOffset, style) { │ │ │ │ + var style = style || node._style; │ │ │ │ + var rotation = style.rotation || 0; │ │ │ │ + │ │ │ │ + var aspectRatio, size; │ │ │ │ + if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ + // load the image to determine its size │ │ │ │ + var img = new Image(); │ │ │ │ + img.onreadystatechange = OpenLayers.Function.bind(function() { │ │ │ │ + if (img.readyState == "complete" || │ │ │ │ + img.readyState == "interactive") { │ │ │ │ + aspectRatio = img.width / img.height; │ │ │ │ + size = Math.max(style.pointRadius * 2, │ │ │ │ + style.graphicWidth || 0, │ │ │ │ + style.graphicHeight || 0); │ │ │ │ + xOffset = xOffset * aspectRatio; │ │ │ │ + style.graphicWidth = size * aspectRatio; │ │ │ │ + style.graphicHeight = size; │ │ │ │ + this.graphicRotate(node, xOffset, yOffset, style); │ │ │ │ + } │ │ │ │ + }, this); │ │ │ │ + img.src = style.externalGraphic; │ │ │ │ + │ │ │ │ + // will be called again by the onreadystate handler │ │ │ │ + return; │ │ │ │ + } else { │ │ │ │ + size = Math.max(style.graphicWidth, style.graphicHeight); │ │ │ │ + aspectRatio = style.graphicWidth / style.graphicHeight; │ │ │ │ + } │ │ │ │ + │ │ │ │ + var width = Math.round(style.graphicWidth || size * aspectRatio); │ │ │ │ + var height = Math.round(style.graphicHeight || size); │ │ │ │ + node.style.width = width + "px"; │ │ │ │ + node.style.height = height + "px"; │ │ │ │ + │ │ │ │ + // Three steps are required to remove artefacts for images with │ │ │ │ + // transparent backgrounds (resulting from using DXImageTransform │ │ │ │ + // filters on svg objects), while preserving awareness for browser │ │ │ │ + // events on images: │ │ │ │ + // - Use the fill as usual (like for unrotated images) to handle │ │ │ │ + // events │ │ │ │ + // - specify an imagedata element with the same src as the fill │ │ │ │ + // - style the imagedata element with an AlphaImageLoader filter │ │ │ │ + // with empty src │ │ │ │ + var image = document.getElementById(node.id + "_image"); │ │ │ │ + if (!image) { │ │ │ │ + image = this.createNode("olv:imagedata", node.id + "_image"); │ │ │ │ + node.appendChild(image); │ │ │ │ + } │ │ │ │ + image.style.width = width + "px"; │ │ │ │ + image.style.height = height + "px"; │ │ │ │ + image.src = style.externalGraphic; │ │ │ │ + image.style.filter = │ │ │ │ + "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + │ │ │ │ + "src='', sizingMethod='scale')"; │ │ │ │ + │ │ │ │ + var rot = rotation * Math.PI / 180; │ │ │ │ + var sintheta = Math.sin(rot); │ │ │ │ + var costheta = Math.cos(rot); │ │ │ │ + │ │ │ │ + // do the rotation on the image │ │ │ │ + var filter = │ │ │ │ + "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta + │ │ │ │ + ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta + │ │ │ │ + ",SizingMethod='auto expand')\n"; │ │ │ │ + │ │ │ │ + // set the opacity (needed for the imagedata) │ │ │ │ + var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ + if (opacity && opacity != 1) { │ │ │ │ + filter += │ │ │ │ + "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + │ │ │ │ + opacity + ")\n"; │ │ │ │ + } │ │ │ │ + node.style.filter = filter; │ │ │ │ + │ │ │ │ + // do the rotation again on a box, so we know the insertion point │ │ │ │ + var centerPoint = new OpenLayers.Geometry.Point(-xOffset, -yOffset); │ │ │ │ + var imgBox = new OpenLayers.Bounds(0, 0, width, height).toGeometry(); │ │ │ │ + imgBox.rotate(style.rotation, centerPoint); │ │ │ │ + var imgBounds = imgBox.getBounds(); │ │ │ │ + │ │ │ │ + node.style.left = Math.round( │ │ │ │ + parseInt(node.style.left) + imgBounds.left) + "px"; │ │ │ │ + node.style.top = Math.round( │ │ │ │ + parseInt(node.style.top) - imgBounds.bottom) + "px"; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: postDraw │ │ │ │ + * Does some node postprocessing to work around browser issues: │ │ │ │ + * - Some versions of Internet Explorer seem to be unable to set fillcolor │ │ │ │ + * and strokecolor to "none" correctly before the fill node is appended │ │ │ │ + * to a visible vml node. This method takes care of that and sets │ │ │ │ + * fillcolor and strokecolor again if needed. │ │ │ │ + * - In some cases, a node won't become visible after being drawn. Setting │ │ │ │ + * style.visibility to "visible" works around that. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ */ │ │ │ │ - backBufferLonLat: null, │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: setNodeDimension │ │ │ │ + * Get the geometry's bounds, convert it to our vml coordinate system, │ │ │ │ + * then set the node's position, size, and local coordinate system. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ */ │ │ │ │ - backBufferTimerId: null, │ │ │ │ + setNodeDimension: function(node, geometry) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: removeBackBufferDelay │ │ │ │ - * {Number} Delay for removing the backbuffer when all tiles have finished │ │ │ │ - * loading. Can be set to 0 when no css opacity transitions for the │ │ │ │ - * olTileImage class are used. Default is 0 for <singleTile> layers, │ │ │ │ - * 2500 for tiled layers. See <className> for more information on │ │ │ │ - * tile animation. │ │ │ │ + var bbox = geometry.getBounds(); │ │ │ │ + if (bbox) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + │ │ │ │ + var scaledBox = │ │ │ │ + new OpenLayers.Bounds(((bbox.left - this.featureDx) / resolution - this.offset.x) | 0, │ │ │ │ + (bbox.bottom / resolution - this.offset.y) | 0, │ │ │ │ + ((bbox.right - this.featureDx) / resolution - this.offset.x) | 0, │ │ │ │ + (bbox.top / resolution - this.offset.y) | 0); │ │ │ │ + │ │ │ │ + // Set the internal coordinate system to draw the path │ │ │ │ + node.style.left = scaledBox.left + "px"; │ │ │ │ + node.style.top = scaledBox.top + "px"; │ │ │ │ + node.style.width = scaledBox.getWidth() + "px"; │ │ │ │ + node.style.height = scaledBox.getHeight() + "px"; │ │ │ │ + │ │ │ │ + node.coordorigin = scaledBox.left + " " + scaledBox.top; │ │ │ │ + node.coordsize = scaledBox.getWidth() + " " + scaledBox.getHeight(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: dashStyle │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * style - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A VML compliant 'stroke-dasharray' value │ │ │ │ */ │ │ │ │ - removeBackBufferDelay: null, │ │ │ │ + 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"; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: className │ │ │ │ - * {String} Name of the class added to the layer div. If not set in the │ │ │ │ - * options passed to the constructor then className defaults to │ │ │ │ - * "olLayerGridSingleTile" for single tile layers (see <singleTile>), │ │ │ │ - * and "olLayerGrid" for non single tile layers. │ │ │ │ + * Method: createNode │ │ │ │ + * Create a new node │ │ │ │ * │ │ │ │ - * Note: │ │ │ │ + * Parameters: │ │ │ │ + * type - {String} Kind of node to draw │ │ │ │ + * id - {String} Id for node │ │ │ │ * │ │ │ │ - * The displaying of tiles is not animated by default for single tile │ │ │ │ - * layers - OpenLayers' default theme (style.css) includes this: │ │ │ │ - * (code) │ │ │ │ - * .olLayerGrid .olTileImage { │ │ │ │ - * -webkit-transition: opacity 0.2s linear; │ │ │ │ - * -moz-transition: opacity 0.2s linear; │ │ │ │ - * -o-transition: opacity 0.2s linear; │ │ │ │ - * transition: opacity 0.2s linear; │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ - * To animate tile displaying for any grid layer the following │ │ │ │ - * CSS rule can be used: │ │ │ │ - * (code) │ │ │ │ - * .olTileImage { │ │ │ │ - * -webkit-transition: opacity 0.2s linear; │ │ │ │ - * -moz-transition: opacity 0.2s linear; │ │ │ │ - * -o-transition: opacity 0.2s linear; │ │ │ │ - * transition: opacity 0.2s linear; │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ - * In that case, to avoid flash effects, <removeBackBufferDelay> │ │ │ │ - * should not be zero. │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A new node of the given type and id │ │ │ │ */ │ │ │ │ - className: null, │ │ │ │ + createNode: function(type, id) { │ │ │ │ + var node = document.createElement(type); │ │ │ │ + if (id) { │ │ │ │ + node.id = id; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // IE hack to make elements unselectable, to prevent 'blue flash' │ │ │ │ + // while dragging vectors; #1410 │ │ │ │ + node.unselectable = 'on'; │ │ │ │ + node.onselectstart = OpenLayers.Function.False; │ │ │ │ + │ │ │ │ + return node; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: nodeTypeCompare │ │ │ │ + * Determine whether a node is of a given type │ │ │ │ * │ │ │ │ - * 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: │ │ │ │ + * node - {DOMElement} An VML element │ │ │ │ + * type - {String} Kind of node │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ */ │ │ │ │ + nodeTypeCompare: function(node, type) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: gridLayout │ │ │ │ - * {Object} Object containing properties tilelon, tilelat, startcol, │ │ │ │ - * startrow │ │ │ │ - */ │ │ │ │ - gridLayout: null, │ │ │ │ + //split type │ │ │ │ + var subType = type; │ │ │ │ + var splitIndex = subType.indexOf(":"); │ │ │ │ + if (splitIndex != -1) { │ │ │ │ + subType = subType.substr(splitIndex + 1); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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, │ │ │ │ + //split nodeName │ │ │ │ + var nodeName = node.nodeName; │ │ │ │ + splitIndex = nodeName.indexOf(":"); │ │ │ │ + if (splitIndex != -1) { │ │ │ │ + nodeName = nodeName.substr(splitIndex + 1); │ │ │ │ + } │ │ │ │ + │ │ │ │ + return (subType == nodeName); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: transitionendEvents │ │ │ │ - * {Array} Event names for transitionend │ │ │ │ + * Method: createRenderRoot │ │ │ │ + * Create the renderer root │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The specific render engine's root element │ │ │ │ */ │ │ │ │ - transitionendEvents: [ │ │ │ │ - 'transitionend', 'webkitTransitionEnd', 'otransitionend', │ │ │ │ - 'oTransitionEnd' │ │ │ │ - ], │ │ │ │ + createRenderRoot: function() { │ │ │ │ + return this.nodeFactory(this.container.id + "_vmlRoot", "div"); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.Grid │ │ │ │ - * Create a new grid layer │ │ │ │ - * │ │ │ │ + * Method: createRoot │ │ │ │ + * Create the main root element │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * url - {String} │ │ │ │ - * params - {Object} │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * suffix - {String} suffix to append to the id │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, │ │ │ │ - arguments); │ │ │ │ - this.grid = []; │ │ │ │ - this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this); │ │ │ │ + createRoot: function(suffix) { │ │ │ │ + return this.nodeFactory(this.container.id + suffix, "olv:group"); │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.initProperties(); │ │ │ │ + /************************************** │ │ │ │ + * * │ │ │ │ + * GEOMETRY DRAWING FUNCTIONS * │ │ │ │ + * * │ │ │ │ + **************************************/ │ │ │ │ │ │ │ │ - this.rowSign = this.tileOriginCorner.substr(0, 1) === "t" ? 1 : -1; │ │ │ │ + /** │ │ │ │ + * Method: drawPoint │ │ │ │ + * Render a point │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the point could not be drawn │ │ │ │ + */ │ │ │ │ + drawPoint: function(node, geometry) { │ │ │ │ + return this.drawCircle(node, geometry, 1); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: initProperties │ │ │ │ - * Set any properties that depend on the value of singleTile. │ │ │ │ - * Currently sets removeBackBufferDelay and className │ │ │ │ + * Method: drawCircle │ │ │ │ + * Render a circle. │ │ │ │ + * Size and Center a circle given geometry (x,y center) and radius │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * radius - {float} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the circle could not ne drawn │ │ │ │ */ │ │ │ │ - initProperties: function() { │ │ │ │ - if (this.options.removeBackBufferDelay === undefined) { │ │ │ │ - this.removeBackBufferDelay = this.singleTile ? 0 : 2500; │ │ │ │ - } │ │ │ │ + drawCircle: function(node, geometry, radius) { │ │ │ │ + if (!isNaN(geometry.x) && !isNaN(geometry.y)) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ │ │ │ │ - if (this.options.className === undefined) { │ │ │ │ - this.className = this.singleTile ? 'olLayerGridSingleTile' : │ │ │ │ - 'olLayerGrid'; │ │ │ │ + node.style.left = ((((geometry.x - this.featureDx) / resolution - this.offset.x) | 0) - radius) + "px"; │ │ │ │ + node.style.top = (((geometry.y / resolution - this.offset.y) | 0) - radius) + "px"; │ │ │ │ + │ │ │ │ + var diameter = radius * 2; │ │ │ │ + │ │ │ │ + node.style.width = diameter + "px"; │ │ │ │ + node.style.height = diameter + "px"; │ │ │ │ + return node; │ │ │ │ } │ │ │ │ + return false; │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: setMap │ │ │ │ - * │ │ │ │ + * Method: drawLineString │ │ │ │ + * Render a linestring. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} The map. │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map); │ │ │ │ - OpenLayers.Element.addClass(this.div, this.className); │ │ │ │ + drawLineString: function(node, geometry) { │ │ │ │ + return this.drawLine(node, geometry, false); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: removeMap │ │ │ │ - * Called when the layer is removed from the map. │ │ │ │ - * │ │ │ │ + * Method: drawLinearRing │ │ │ │ + * Render a linearring │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} The map. │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - removeMap: function(map) { │ │ │ │ - this.removeBackBuffer(); │ │ │ │ + drawLinearRing: function(node, geometry) { │ │ │ │ + return this.drawLine(node, geometry, true); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Deconstruct the layer and clear the grid. │ │ │ │ + * Method: DrawLine │ │ │ │ + * Render a line. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * closeLine - {Boolean} Close the line? (make it a ring?) │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.removeBackBuffer(); │ │ │ │ - this.clearGrid(); │ │ │ │ + drawLine: function(node, geometry, closeLine) { │ │ │ │ │ │ │ │ - this.grid = null; │ │ │ │ - this.tileSize = null; │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); │ │ │ │ + this.setNodeDimension(node, geometry); │ │ │ │ + │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var numComponents = geometry.components.length; │ │ │ │ + var parts = new Array(numComponents); │ │ │ │ + │ │ │ │ + var comp, x, y; │ │ │ │ + for (var i = 0; i < numComponents; i++) { │ │ │ │ + comp = geometry.components[i]; │ │ │ │ + x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0; │ │ │ │ + y = (comp.y / resolution - this.offset.y) | 0; │ │ │ │ + parts[i] = " " + x + "," + y + " l "; │ │ │ │ + } │ │ │ │ + var end = (closeLine) ? " x e" : " e"; │ │ │ │ + node.path = "m" + parts.join("") + end; │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: drawPolygon │ │ │ │ + * Render a polygon │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * newParams - {Object} │ │ │ │ - * │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * redrawn: {Boolean} whether the layer was actually redrawn. │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ + drawPolygon: function(node, geometry) { │ │ │ │ + this.setNodeDimension(node, geometry); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + │ │ │ │ + var path = []; │ │ │ │ + var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y; │ │ │ │ + for (j = 0, jj = geometry.components.length; j < jj; j++) { │ │ │ │ + path.push("m"); │ │ │ │ + points = geometry.components[j].components; │ │ │ │ + // we only close paths of interior rings with area │ │ │ │ + area = (j === 0); │ │ │ │ + first = null; │ │ │ │ + second = null; │ │ │ │ + for (i = 0, ii = points.length; i < ii; i++) { │ │ │ │ + comp = points[i]; │ │ │ │ + x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0; │ │ │ │ + y = (comp.y / resolution - this.offset.y) | 0; │ │ │ │ + pathComp = " " + x + "," + y; │ │ │ │ + path.push(pathComp); │ │ │ │ + if (i == 0) { │ │ │ │ + path.push(" l"); │ │ │ │ + } │ │ │ │ + if (!area) { │ │ │ │ + // IE improperly renders sub-paths that have no area. │ │ │ │ + // Instead of checking the area of every ring, we confirm │ │ │ │ + // the ring has at least three distinct points. This does │ │ │ │ + // not catch all non-zero area cases, but it greatly improves │ │ │ │ + // interior ring digitizing and is a minor performance hit │ │ │ │ + // when rendering rings with many points. │ │ │ │ + if (!first) { │ │ │ │ + first = pathComp; │ │ │ │ + } else if (first != pathComp) { │ │ │ │ + if (!second) { │ │ │ │ + second = pathComp; │ │ │ │ + } else if (second != pathComp) { │ │ │ │ + // stop looking │ │ │ │ + area = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ - this.grid = []; │ │ │ │ - this.gridResolution = null; │ │ │ │ - this.gridLayout = null; │ │ │ │ + path.push(area ? " x " : " "); │ │ │ │ } │ │ │ │ + path.push("e"); │ │ │ │ + node.path = path.join(""); │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: addOptions │ │ │ │ + * Method: drawRectangle │ │ │ │ + * Render a rectangle │ │ │ │ * │ │ │ │ * 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. │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + drawRectangle: function(node, geometry) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + │ │ │ │ + node.style.left = (((geometry.x - this.featureDx) / resolution - this.offset.x) | 0) + "px"; │ │ │ │ + node.style.top = ((geometry.y / resolution - this.offset.y) | 0) + "px"; │ │ │ │ + node.style.width = ((geometry.width / resolution) | 0) + "px"; │ │ │ │ + node.style.height = ((geometry.height / resolution) | 0) + "px"; │ │ │ │ + │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {Object} Is this ever used? │ │ │ │ + * Method: drawText │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid │ │ │ │ + * Parameters: │ │ │ │ + * featureId - {String} │ │ │ │ + * style - │ │ │ │ + * location - {<OpenLayers.Geometry.Point>} │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ + 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"); │ │ │ │ │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Grid(this.name, │ │ │ │ - this.url, │ │ │ │ - this.params, │ │ │ │ - this.getOptions()); │ │ │ │ - } │ │ │ │ + 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"; │ │ │ │ │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]); │ │ │ │ + textbox.innerText = style.label; │ │ │ │ │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - if (this.tileSize != null) { │ │ │ │ - obj.tileSize = this.tileSize.clone(); │ │ │ │ + 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"; │ │ │ │ │ │ │ │ - // 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; │ │ │ │ + if (!label.parentNode) { │ │ │ │ + label.appendChild(textbox); │ │ │ │ + this.textRoot.appendChild(label); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var align = style.labelAlign || "cm"; │ │ │ │ + if (align.length == 1) { │ │ │ │ + align += "m"; │ │ │ │ + } │ │ │ │ + var xshift = textbox.clientWidth * │ │ │ │ + (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(0, 1)]); │ │ │ │ + var yshift = textbox.clientHeight * │ │ │ │ + (OpenLayers.Renderer.VML.LABEL_SHIFT[align.substr(1, 1)]); │ │ │ │ + label.style.left = parseInt(label.style.left) - xshift - 1 + "px"; │ │ │ │ + label.style.top = parseInt(label.style.top) + yshift + "px"; │ │ │ │ │ │ │ │ - return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveTo │ │ │ │ - * This function is called whenever the map is moved. All the moving │ │ │ │ - * of actual 'tiles' is done by the map, but moveTo's role is to accept │ │ │ │ - * a bounds and make sure the data that that bounds requires is pre-loaded. │ │ │ │ - * │ │ │ │ + * Method: moveRoot │ │ │ │ + * moves this renderer's root to a different renderer. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * zoomChanged - {Boolean} │ │ │ │ - * dragging - {Boolean} │ │ │ │ + * renderer - {<OpenLayers.Renderer>} target renderer for the moved root │ │ │ │ + * root - {DOMElement} optional root node. To be used when this renderer │ │ │ │ + * holds roots from multiple layers to tell this method which one to │ │ │ │ + * detach │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true if successful, false otherwise │ │ │ │ */ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + moveRoot: function(renderer) { │ │ │ │ + var layer = this.map.getLayer(renderer.container.id); │ │ │ │ + if (layer instanceof OpenLayers.Layer.Vector.RootContainer) { │ │ │ │ + layer = this.map.getLayer(this.container.id); │ │ │ │ + } │ │ │ │ + layer && layer.renderer.clear(); │ │ │ │ + OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments); │ │ │ │ + layer && layer.redraw(); │ │ │ │ + }, │ │ │ │ │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments); │ │ │ │ + /** │ │ │ │ + * Method: importSymbol │ │ │ │ + * add a new symbol definition from the rendererer's symbol hash │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * graphicName - {String} name of the symbol to import │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} - hash of {DOMElement} "symbol" and {Number} "size" │ │ │ │ + */ │ │ │ │ + importSymbol: function(graphicName) { │ │ │ │ + var id = this.container.id + "-" + graphicName; │ │ │ │ │ │ │ │ - bounds = bounds || this.map.getExtent(); │ │ │ │ + // check if symbol already exists in the cache │ │ │ │ + var cache = this.symbolCache[id]; │ │ │ │ + if (cache) { │ │ │ │ + return cache; │ │ │ │ + } │ │ │ │ │ │ │ │ - if (bounds != null) { │ │ │ │ + var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ + if (!symbol) { │ │ │ │ + throw new Error(graphicName + ' is not a valid symbol name'); │ │ │ │ + } │ │ │ │ │ │ │ │ - // if grid is empty or zoom has changed, we *must* re-tile │ │ │ │ - var forceReTile = !this.grid.length || zoomChanged; │ │ │ │ + var symbolExtent = new OpenLayers.Bounds( │ │ │ │ + Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ │ │ │ │ - // total bounds of the tiles │ │ │ │ - var tilesBounds = this.getTilesBounds(); │ │ │ │ + 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); │ │ │ │ │ │ │ │ - // the new map resolution │ │ │ │ - var resolution = this.map.getResolution(); │ │ │ │ + pathitems.push(x); │ │ │ │ + pathitems.push(y); │ │ │ │ + if (i == 0) { │ │ │ │ + pathitems.push("l"); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + pathitems.push("x e"); │ │ │ │ + var path = pathitems.join(" "); │ │ │ │ │ │ │ │ - // the server-supported resolution for the new map resolution │ │ │ │ - var serverResolution = this.getServerResolution(resolution); │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ │ │ │ │ - if (this.singleTile) { │ │ │ │ + cache = { │ │ │ │ + path: path, │ │ │ │ + size: symbolExtent.getWidth(), // equals getHeight() now │ │ │ │ + left: symbolExtent.left, │ │ │ │ + bottom: symbolExtent.bottom │ │ │ │ + }; │ │ │ │ + this.symbolCache[id] = cache; │ │ │ │ │ │ │ │ - // 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) │ │ │ │ + return cache; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (forceReTile || │ │ │ │ - (!dragging && !tilesBounds.containsBounds(bounds))) { │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.VML" │ │ │ │ +}); │ │ │ │ │ │ │ │ - // 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. │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Renderer.VML.LABEL_SHIFT │ │ │ │ + * {Object} │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.VML.LABEL_SHIFT = { │ │ │ │ + "l": 0, │ │ │ │ + "c": .5, │ │ │ │ + "r": 1, │ │ │ │ + "t": 0, │ │ │ │ + "m": .5, │ │ │ │ + "b": 1 │ │ │ │ +}; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Renderer/SVG.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - if (zoomChanged && this.transitionEffect !== 'resize') { │ │ │ │ - this.removeBackBuffer(); │ │ │ │ - } │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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 || this.transitionEffect === 'resize') { │ │ │ │ - this.applyBackBuffer(resolution); │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Renderer/Elements.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - this.initSingleTile(bounds); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Renderer.SVG │ │ │ │ + * │ │ │ │ + * Inherits: │ │ │ │ + * - <OpenLayers.Renderer.Elements> │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ │ │ │ │ - // 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() │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * Property: xmlns │ │ │ │ + * {String} │ │ │ │ + */ │ │ │ │ + xmlns: "http://www.w3.org/2000/svg", │ │ │ │ │ │ │ │ - if (forceReTile) { │ │ │ │ - if (zoomChanged && (this.transitionEffect === 'resize' || │ │ │ │ - this.gridResolution === resolution)) { │ │ │ │ - this.applyBackBuffer(resolution); │ │ │ │ - } │ │ │ │ - this.initGriddedTiles(bounds); │ │ │ │ - } else { │ │ │ │ - this.moveGriddedTiles(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: xlinkns │ │ │ │ + * {String} │ │ │ │ + */ │ │ │ │ + xlinkns: "http://www.w3.org/1999/xlink", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getTileData │ │ │ │ - * Given a map location, retrieve a tile and the pixel offset within that │ │ │ │ - * tile corresponding to the location. If there is not an existing │ │ │ │ - * tile in the grid that covers the given location, null will be │ │ │ │ - * returned. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * loc - {<OpenLayers.LonLat>} map location │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} Object with the following properties: tile ({<OpenLayers.Tile>}), │ │ │ │ - * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel │ │ │ │ - * offset from top left). │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - getTileData: function(loc) { │ │ │ │ - var data = null, │ │ │ │ - x = loc.lon, │ │ │ │ - y = loc.lat, │ │ │ │ - numRows = this.grid.length; │ │ │ │ + MAX_PIXEL: 15000, │ │ │ │ │ │ │ │ - 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; │ │ │ │ + /** │ │ │ │ + * Property: translationParameters │ │ │ │ + * {Object} Hash with "x" and "y" properties │ │ │ │ + */ │ │ │ │ + translationParameters: null, │ │ │ │ │ │ │ │ - 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; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: symbolMetrics │ │ │ │ + * {Object} Cache for symbol metrics according to their svg coordinate │ │ │ │ + * space. This is an object keyed by the symbol's id, and values are │ │ │ │ + * an array of [width, centerX, centerY]. │ │ │ │ + */ │ │ │ │ + symbolMetrics: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroyTile │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Renderer.SVG │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * tile - {<OpenLayers.Tile>} │ │ │ │ + * containerID - {String} │ │ │ │ */ │ │ │ │ - destroyTile: function(tile) { │ │ │ │ - this.removeTileMonitoringHooks(tile); │ │ │ │ - tile.destroy(); │ │ │ │ + initialize: function(containerID) { │ │ │ │ + if (!this.supported()) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + OpenLayers.Renderer.Elements.prototype.initialize.apply(this, │ │ │ │ + arguments); │ │ │ │ + this.translationParameters = { │ │ │ │ + x: 0, │ │ │ │ + y: 0 │ │ │ │ + }; │ │ │ │ + │ │ │ │ + this.symbolMetrics = {}; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getServerResolution │ │ │ │ - * Return the closest server-supported resolution. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * resolution - {Number} The base resolution. If undefined the │ │ │ │ - * map resolution is used. │ │ │ │ - * │ │ │ │ + * APIMethod: supported │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Number} The closest server resolution value. │ │ │ │ + * {Boolean} Whether or not the browser supports the SVG renderer │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + 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: getServerZoom │ │ │ │ - * Return the zoom value corresponding to the best matching server │ │ │ │ - * resolution, taking into account <serverResolutions> and <zoomOffset>. │ │ │ │ + * Method: inValidRange │ │ │ │ + * See #669 for more information │ │ │ │ * │ │ │ │ + * Parameters: │ │ │ │ + * x - {Integer} │ │ │ │ + * y - {Integer} │ │ │ │ + * xyOnly - {Boolean} whether or not to just check for x and y, which means │ │ │ │ + * to not take the current translation parameters into account if true. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Number} The closest server supported zoom. This is not the map zoom │ │ │ │ - * level, but an index of the server's resolutions array. │ │ │ │ + * {Boolean} Whether or not the 'x' and 'y' coordinates are in the │ │ │ │ + * valid range. │ │ │ │ */ │ │ │ │ - getServerZoom: function() { │ │ │ │ - var resolution = this.getServerResolution(); │ │ │ │ - return this.serverResolutions ? │ │ │ │ - OpenLayers.Util.indexOf(this.serverResolutions, resolution) : │ │ │ │ - this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0); │ │ │ │ + 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: applyBackBuffer │ │ │ │ - * Create, insert, scale and position a back buffer for the layer. │ │ │ │ - * │ │ │ │ + * Method: setExtent │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * resolution - {Number} The resolution to transition to. │ │ │ │ + * extent - {<OpenLayers.Bounds>} │ │ │ │ + * resolutionChanged - {Boolean} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true to notify the layer that the new extent does not exceed │ │ │ │ + * the coordinate range, and the features will not need to be redrawn. │ │ │ │ + * False otherwise. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + setExtent: function(extent, resolutionChanged) { │ │ │ │ + var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); │ │ │ │ │ │ │ │ - // 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 resolution = this.getResolution(), │ │ │ │ + left = -extent.left / resolution, │ │ │ │ + top = extent.top / resolution; │ │ │ │ │ │ │ │ - var ratio = this.backBufferResolution / 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; │ │ │ │ │ │ │ │ - // 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'; │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - │ │ │ │ - // 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: translate │ │ │ │ + * Transforms the SVG coordinate system │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * x - {Float} │ │ │ │ + * y - {Float} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} The DOM element for the back buffer, undefined if the │ │ │ │ - * grid isn't initialized yet. │ │ │ │ + * {Boolean} true if the translation parameters are in the valid coordinates │ │ │ │ + * range, false otherwise. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - return backBuffer; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: removeBackBuffer │ │ │ │ - * Remove back buffer from DOM. │ │ │ │ + * Method: setSize │ │ │ │ + * Sets the size of the drawing surface. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * size - {<OpenLayers.Size>} The size of the drawing surface │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + 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: moveByPx │ │ │ │ - * Move the layer based on pixel vector. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: getNodeType │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * dx - {Number} │ │ │ │ - * dy - {Number} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * style - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The corresponding node type for the specified geometry │ │ │ │ */ │ │ │ │ - moveByPx: function(dx, dy) { │ │ │ │ - if (!this.singleTile) { │ │ │ │ - this.moveGriddedTiles(); │ │ │ │ + getNodeType: function(geometry, style) { │ │ │ │ + var nodeType = null; │ │ │ │ + switch (geometry.CLASS_NAME) { │ │ │ │ + case "OpenLayers.Geometry.Point": │ │ │ │ + if (style.externalGraphic) { │ │ │ │ + nodeType = "image"; │ │ │ │ + } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ + nodeType = "svg"; │ │ │ │ + } else { │ │ │ │ + nodeType = "circle"; │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Rectangle": │ │ │ │ + nodeType = "rect"; │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LineString": │ │ │ │ + nodeType = "polyline"; │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LinearRing": │ │ │ │ + nodeType = "polygon"; │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Polygon": │ │ │ │ + case "OpenLayers.Geometry.Curve": │ │ │ │ + nodeType = "path"; │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + break; │ │ │ │ } │ │ │ │ + return nodeType; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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). │ │ │ │ + /** │ │ │ │ + * 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: │ │ │ │ - * size - {<OpenLayers.Size>} │ │ │ │ + * node - {SVGDomElement} An SVG element to decorate │ │ │ │ + * style - {Object} │ │ │ │ + * options - {Object} Currently supported options include │ │ │ │ + * 'isFilled' {Boolean} and │ │ │ │ + * 'isStroked' {Boolean} │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ } │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getTilesBounds │ │ │ │ - * Return the bounds of the tile grid. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the │ │ │ │ - * currently loaded tiles (including those partially or not at all seen │ │ │ │ - * onscreen). │ │ │ │ - */ │ │ │ │ - getTilesBounds: function() { │ │ │ │ - var bounds = null; │ │ │ │ + var r = parseFloat(node.getAttributeNS(null, "r")); │ │ │ │ + var widthFactor = 1; │ │ │ │ + var pos; │ │ │ │ + if (node._geometryClass == "OpenLayers.Geometry.Point" && r) { │ │ │ │ + node.style.visibility = ""; │ │ │ │ + if (style.graphic === false) { │ │ │ │ + node.style.visibility = "hidden"; │ │ │ │ + } else if (style.externalGraphic) { │ │ │ │ + pos = this.getPosition(node); │ │ │ │ + if (style.graphicWidth && style.graphicHeight) { │ │ │ │ + node.setAttributeNS(null, "preserveAspectRatio", "none"); │ │ │ │ + } │ │ │ │ + var width = style.graphicWidth || style.graphicHeight; │ │ │ │ + var height = style.graphicHeight || style.graphicWidth; │ │ │ │ + width = width ? width : style.pointRadius * 2; │ │ │ │ + height = height ? height : style.pointRadius * 2; │ │ │ │ + var xOffset = (style.graphicXOffset != undefined) ? │ │ │ │ + style.graphicXOffset : -(0.5 * width); │ │ │ │ + var yOffset = (style.graphicYOffset != undefined) ? │ │ │ │ + style.graphicYOffset : -(0.5 * height); │ │ │ │ │ │ │ │ - var 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(); │ │ │ │ + var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ │ │ │ │ - bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, │ │ │ │ - bottomLeftTileBounds.bottom, │ │ │ │ - bottomLeftTileBounds.left + width, │ │ │ │ - bottomLeftTileBounds.bottom + height); │ │ │ │ - } │ │ │ │ - return bounds; │ │ │ │ - }, │ │ │ │ + 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; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: initSingleTile │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - */ │ │ │ │ - initSingleTile: function(bounds) { │ │ │ │ - this.events.triggerEvent("retile"); │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ │ │ │ │ - //determine new tile bounds │ │ │ │ - var center = bounds.getCenterLonLat(); │ │ │ │ - var tileWidth = bounds.getWidth() * this.ratio; │ │ │ │ - var tileHeight = bounds.getHeight() * this.ratio; │ │ │ │ + // 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")); │ │ │ │ │ │ │ │ - var tileBounds = │ │ │ │ - new OpenLayers.Bounds(center.lon - (tileWidth / 2), │ │ │ │ - center.lat - (tileHeight / 2), │ │ │ │ - center.lon + (tileWidth / 2), │ │ │ │ - center.lat + (tileHeight / 2)); │ │ │ │ + node.setAttributeNS(null, "width", size); │ │ │ │ + node.setAttributeNS(null, "height", size); │ │ │ │ + node.setAttributeNS(null, "x", pos.x - offset); │ │ │ │ + node.setAttributeNS(null, "y", pos.y - offset); │ │ │ │ │ │ │ │ - var px = this.map.getLayerPxFromLonLat({ │ │ │ │ - lon: tileBounds.left, │ │ │ │ - lat: tileBounds.top │ │ │ │ - }); │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ │ │ │ │ - if (!this.grid.length) { │ │ │ │ - this.grid[0] = []; │ │ │ │ + var rotation = style.rotation; │ │ │ │ + │ │ │ │ + if ((rotation !== undefined || node._rotation !== undefined) && pos) { │ │ │ │ + node._rotation = rotation; │ │ │ │ + rotation |= 0; │ │ │ │ + if (node.nodeName !== "svg") { │ │ │ │ + node.setAttributeNS(null, "transform", │ │ │ │ + "rotate(" + rotation + " " + pos.x + " " + │ │ │ │ + pos.y + ")"); │ │ │ │ + } else { │ │ │ │ + var metrics = this.symbolMetrics[src.id]; │ │ │ │ + node.firstChild.setAttributeNS(null, "transform", "rotate(" + │ │ │ │ + rotation + " " + │ │ │ │ + metrics[1] + " " + │ │ │ │ + metrics[2] + ")"); │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ │ │ │ │ - var tile = this.grid[0][0]; │ │ │ │ - if (!tile) { │ │ │ │ - tile = this.addTile(tileBounds, px); │ │ │ │ + if (options.isFilled) { │ │ │ │ + node.setAttributeNS(null, "fill", style.fillColor); │ │ │ │ + node.setAttributeNS(null, "fill-opacity", style.fillOpacity); │ │ │ │ + } else { │ │ │ │ + node.setAttributeNS(null, "fill", "none"); │ │ │ │ + } │ │ │ │ │ │ │ │ - this.addTileMonitoringHooks(tile); │ │ │ │ - tile.draw(); │ │ │ │ - this.grid[0][0] = tile; │ │ │ │ + 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 { │ │ │ │ - tile.moveTo(tileBounds, px); │ │ │ │ + node.setAttributeNS(null, "stroke", "none"); │ │ │ │ } │ │ │ │ │ │ │ │ - //remove all but our single tile │ │ │ │ - this.removeExcessTiles(1, 1); │ │ │ │ + if (style.pointerEvents) { │ │ │ │ + node.setAttributeNS(null, "pointer-events", style.pointerEvents); │ │ │ │ + } │ │ │ │ │ │ │ │ - // store the resolution of the grid │ │ │ │ - this.gridResolution = this.getServerResolution(); │ │ │ │ + if (style.cursor != null) { │ │ │ │ + node.setAttributeNS(null, "cursor", style.cursor); │ │ │ │ + } │ │ │ │ + │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: calculateGridLayout │ │ │ │ - * Generate parameters for the grid layout. │ │ │ │ - * │ │ │ │ + * Method: dashStyle │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bound>|Object} OpenLayers.Bounds or an │ │ │ │ - * object with a 'left' and 'top' properties. │ │ │ │ - * origin - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an │ │ │ │ - * object with a 'lon' and 'lat' properties. │ │ │ │ - * resolution - {Number} │ │ │ │ - * │ │ │ │ + * style - {Object} │ │ │ │ + * widthFactor - {Number} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Object} Object containing properties tilelon, tilelat, startcol, │ │ │ │ - * startrow │ │ │ │ + * {String} A SVG compliant 'stroke-dasharray' value │ │ │ │ */ │ │ │ │ - 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 │ │ │ │ - }; │ │ │ │ - │ │ │ │ + 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: getTileOrigin │ │ │ │ - * Determine the origin for aligning the grid of tiles. If a <tileOrigin> │ │ │ │ - * property is supplied, that will be returned. Otherwise, the origin │ │ │ │ - * will be derived from the layer's <maxExtent> property. In this case, │ │ │ │ - * the tile origin will be the corner of the <maxExtent> given by the │ │ │ │ - * <tileOriginCorner> property. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: createNode │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * type - {String} Kind of node to draw │ │ │ │ + * id - {String} Id for node │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} The tile origin. │ │ │ │ + * {DOMElement} A new node of the given type and id │ │ │ │ */ │ │ │ │ - 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]]); │ │ │ │ + createNode: function(type, id) { │ │ │ │ + var node = document.createElementNS(this.xmlns, type); │ │ │ │ + if (id) { │ │ │ │ + node.setAttributeNS(null, "id", id); │ │ │ │ } │ │ │ │ - return origin; │ │ │ │ + return node; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getTileBoundsForGridIndex │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: nodeTypeCompare │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * row - {Number} The row of the grid │ │ │ │ - * col - {Number} The column of the grid │ │ │ │ - * │ │ │ │ + * node - {SVGDomElement} An SVG element │ │ │ │ + * type - {String} Kind of node │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} The bounds for the tile at (row, col) │ │ │ │ + * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ */ │ │ │ │ - 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 │ │ │ │ - ); │ │ │ │ + nodeTypeCompare: function(node, type) { │ │ │ │ + return (type == node.nodeName); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: initGriddedTiles │ │ │ │ + * Method: createRenderRoot │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The specific render engine's root element │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + createRenderRoot: function() { │ │ │ │ + var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg"); │ │ │ │ + svg.style.display = "block"; │ │ │ │ + return svg; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var tileData = [], │ │ │ │ - center = this.map.getCenter(); │ │ │ │ + /** │ │ │ │ + * Method: createRoot │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * suffix - {String} suffix to append to the id │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + createRoot: function(suffix) { │ │ │ │ + return this.nodeFactory(this.container.id + suffix, "g"); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var rowidx = 0; │ │ │ │ - do { │ │ │ │ - var row = this.grid[rowidx]; │ │ │ │ - if (!row) { │ │ │ │ - row = []; │ │ │ │ - this.grid.push(row); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: createDefs │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The element to which we'll add the symbol definitions │ │ │ │ + */ │ │ │ │ + createDefs: function() { │ │ │ │ + var defs = this.nodeFactory(this.container.id + "_defs", "defs"); │ │ │ │ + this.rendererRoot.appendChild(defs); │ │ │ │ + return defs; │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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) │ │ │ │ - }); │ │ │ │ + /************************************** │ │ │ │ + * * │ │ │ │ + * GEOMETRY DRAWING FUNCTIONS * │ │ │ │ + * * │ │ │ │ + **************************************/ │ │ │ │ │ │ │ │ - colidx += 1; │ │ │ │ - } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) || │ │ │ │ - colidx < minCols); │ │ │ │ + /** │ │ │ │ + * Method: drawPoint │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the renderer could not draw the point │ │ │ │ + */ │ │ │ │ + drawPoint: function(node, geometry) { │ │ │ │ + return this.drawCircle(node, geometry, 1); │ │ │ │ + }, │ │ │ │ │ │ │ │ - rowidx += 1; │ │ │ │ - } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) || │ │ │ │ - rowidx < minRows); │ │ │ │ + /** │ │ │ │ + * Method: drawCircle │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * radius - {Float} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the renderer could not draw the circle │ │ │ │ + */ │ │ │ │ + drawCircle: function(node, geometry, radius) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var x = ((geometry.x - this.featureDx) / resolution + this.left); │ │ │ │ + var y = (this.top - geometry.y / resolution); │ │ │ │ │ │ │ │ - //shave off exceess rows and colums │ │ │ │ - this.removeExcessTiles(rowidx, colidx); │ │ │ │ + if (this.inValidRange(x, y)) { │ │ │ │ + node.setAttributeNS(null, "cx", x); │ │ │ │ + node.setAttributeNS(null, "cy", y); │ │ │ │ + node.setAttributeNS(null, "r", radius); │ │ │ │ + return node; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ │ │ │ │ - var resolution = this.getServerResolution(); │ │ │ │ - // store the resolution of the grid │ │ │ │ - this.gridResolution = resolution; │ │ │ │ + }, │ │ │ │ │ │ │ │ - //now actually draw the tiles │ │ │ │ - tileData.sort(function(a, b) { │ │ │ │ - return a.distance - b.distance; │ │ │ │ - }); │ │ │ │ - for (var i = 0, ii = tileData.length; i < ii; ++i) { │ │ │ │ - tileData[i].tile.draw(); │ │ │ │ + /** │ │ │ │ + * Method: drawLineString │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components of │ │ │ │ + * the linestring, or false if nothing could be drawn │ │ │ │ + */ │ │ │ │ + drawLineString: function(node, geometry) { │ │ │ │ + var componentsResult = this.getComponentsString(geometry.components); │ │ │ │ + if (componentsResult.path) { │ │ │ │ + node.setAttributeNS(null, "points", componentsResult.path); │ │ │ │ + return (componentsResult.complete ? node : null); │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getMaxExtent │ │ │ │ - * Get this layer's maximum extent. (Implemented as a getter for │ │ │ │ - * potential specific implementations in sub-classes.) │ │ │ │ - * │ │ │ │ + * Method: drawLinearRing │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components │ │ │ │ + * of the linear ring, or false if nothing could be drawn │ │ │ │ */ │ │ │ │ - getMaxExtent: function() { │ │ │ │ - return this.maxExtent; │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: addTile │ │ │ │ - * Create a tile, initialize it, and add it to the layer div. │ │ │ │ - * │ │ │ │ - * Parameters │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * position - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ + * Method: drawPolygon │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Tile>} The added OpenLayers.Tile │ │ │ │ + * {DOMElement} or null if the renderer could not draw all components │ │ │ │ + * of the polygon, or false if nothing could be drawn │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + drawPolygon: function(node, geometry) { │ │ │ │ + var d = ""; │ │ │ │ + var draw = true; │ │ │ │ + var complete = true; │ │ │ │ + var linearRingResult, path; │ │ │ │ + for (var j = 0, len = geometry.components.length; j < len; j++) { │ │ │ │ + d += " M"; │ │ │ │ + linearRingResult = this.getComponentsString( │ │ │ │ + geometry.components[j].components, " "); │ │ │ │ + path = linearRingResult.path; │ │ │ │ + if (path) { │ │ │ │ + d += " " + path; │ │ │ │ + complete = linearRingResult.complete && complete; │ │ │ │ + } else { │ │ │ │ + draw = false; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + d += " z"; │ │ │ │ + if (draw) { │ │ │ │ + node.setAttributeNS(null, "d", d); │ │ │ │ + node.setAttributeNS(null, "fill-rule", "evenodd"); │ │ │ │ + return complete ? node : null; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: 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. │ │ │ │ + /** │ │ │ │ + * Method: drawRectangle │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * tile - {<OpenLayers.Tile>} │ │ │ │ + * node - {DOMElement} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} or false if the renderer could not draw the rectangle │ │ │ │ */ │ │ │ │ - addTileMonitoringHooks: function(tile) { │ │ │ │ + drawRectangle: function(node, geometry) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var x = ((geometry.x - this.featureDx) / resolution + this.left); │ │ │ │ + var y = (this.top - geometry.y / resolution); │ │ │ │ │ │ │ │ - var replacingCls = 'olTileReplacing'; │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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); │ │ │ │ + /** │ │ │ │ + * Method: drawText │ │ │ │ + * This method is only called by the renderer itself. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * featureId - {String} │ │ │ │ + * style - │ │ │ │ + * location - {<OpenLayers.Geometry.Point>} │ │ │ │ + */ │ │ │ │ + drawText: function(featureId, style, location) { │ │ │ │ + var drawOutline = (!!style.labelOutlineWidth); │ │ │ │ + // First draw text in halo color and size and overlay the │ │ │ │ + // normal text afterwards │ │ │ │ + if (drawOutline) { │ │ │ │ + var outlineStyle = OpenLayers.Util.extend({}, style); │ │ │ │ + outlineStyle.fontColor = outlineStyle.labelOutlineColor; │ │ │ │ + outlineStyle.fontStrokeColor = outlineStyle.labelOutlineColor; │ │ │ │ + outlineStyle.fontStrokeWidth = style.labelOutlineWidth; │ │ │ │ + if (style.labelOutlineOpacity) { │ │ │ │ + outlineStyle.fontOpacity = style.labelOutlineOpacity; │ │ │ │ } │ │ │ │ - }; │ │ │ │ + delete outlineStyle.labelOutlineWidth; │ │ │ │ + this.drawText(featureId, outlineStyle, location); │ │ │ │ + } │ │ │ │ │ │ │ │ - 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); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + │ │ │ │ + var x = ((location.x - this.featureDx) / resolution + this.left); │ │ │ │ + var y = (location.y / resolution - this.top); │ │ │ │ + │ │ │ │ + var suffix = (drawOutline) ? this.LABEL_OUTLINE_SUFFIX : this.LABEL_ID_SUFFIX; │ │ │ │ + var label = this.nodeFactory(featureId + suffix, "text"); │ │ │ │ + │ │ │ │ + label.setAttributeNS(null, "x", x); │ │ │ │ + label.setAttributeNS(null, "y", -y); │ │ │ │ + │ │ │ │ + if (style.fontColor) { │ │ │ │ + label.setAttributeNS(null, "fill", style.fontColor); │ │ │ │ + } │ │ │ │ + if (style.fontStrokeColor) { │ │ │ │ + label.setAttributeNS(null, "stroke", style.fontStrokeColor); │ │ │ │ + } │ │ │ │ + if (style.fontStrokeWidth) { │ │ │ │ + label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth); │ │ │ │ + } │ │ │ │ + if (style.fontOpacity) { │ │ │ │ + label.setAttributeNS(null, "opacity", style.fontOpacity); │ │ │ │ + } │ │ │ │ + if (style.fontFamily) { │ │ │ │ + label.setAttributeNS(null, "font-family", style.fontFamily); │ │ │ │ + } │ │ │ │ + if (style.fontSize) { │ │ │ │ + label.setAttributeNS(null, "font-size", style.fontSize); │ │ │ │ + } │ │ │ │ + if (style.fontWeight) { │ │ │ │ + label.setAttributeNS(null, "font-weight", style.fontWeight); │ │ │ │ + } │ │ │ │ + if (style.fontStyle) { │ │ │ │ + label.setAttributeNS(null, "font-style", style.fontStyle); │ │ │ │ + } │ │ │ │ + if (style.labelSelect === true) { │ │ │ │ + label.setAttributeNS(null, "pointer-events", "visible"); │ │ │ │ + label._featureId = featureId; │ │ │ │ + } else { │ │ │ │ + label.setAttributeNS(null, "pointer-events", "none"); │ │ │ │ + } │ │ │ │ + var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign; │ │ │ │ + label.setAttributeNS(null, "text-anchor", │ │ │ │ + OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle"); │ │ │ │ + │ │ │ │ + if (OpenLayers.IS_GECKO === true) { │ │ │ │ + label.setAttributeNS(null, "dominant-baseline", │ │ │ │ + OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central"); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var labelRows = style.label.split('\n'); │ │ │ │ + var numRows = labelRows.length; │ │ │ │ + while (label.childNodes.length > numRows) { │ │ │ │ + label.removeChild(label.lastChild); │ │ │ │ + } │ │ │ │ + for (var i = 0; i < numRows; i++) { │ │ │ │ + var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan"); │ │ │ │ + if (style.labelSelect === true) { │ │ │ │ + tspan._featureId = featureId; │ │ │ │ + tspan._geometry = location; │ │ │ │ + tspan._geometryClass = location.CLASS_NAME; │ │ │ │ } │ │ │ │ - //if 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 │ │ │ │ - ); │ │ │ │ - } │ │ │ │ + if (OpenLayers.IS_GECKO === false) { │ │ │ │ + tspan.setAttributeNS(null, "baseline-shift", │ │ │ │ + OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%"); │ │ │ │ + } │ │ │ │ + tspan.setAttribute("x", x); │ │ │ │ + if (i == 0) { │ │ │ │ + var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]]; │ │ │ │ + if (vfactor == null) { │ │ │ │ + vfactor = -.5; │ │ │ │ } │ │ │ │ - this.loading = false; │ │ │ │ - this.events.triggerEvent("loadend"); │ │ │ │ + tspan.setAttribute("dy", (vfactor * (numRows - 1)) + "em"); │ │ │ │ + } else { │ │ │ │ + tspan.setAttribute("dy", "1em"); │ │ │ │ } │ │ │ │ - }; │ │ │ │ - │ │ │ │ - tile.onLoadError = function() { │ │ │ │ - this.events.triggerEvent("tileerror", { │ │ │ │ - tile: tile │ │ │ │ - }); │ │ │ │ - }; │ │ │ │ + tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i]; │ │ │ │ + if (!tspan.parentNode) { │ │ │ │ + label.appendChild(tspan); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - tile.events.on({ │ │ │ │ - "loadstart": tile.onLoadStart, │ │ │ │ - "loadend": tile.onLoadEnd, │ │ │ │ - "unload": tile.onLoadEnd, │ │ │ │ - "loaderror": tile.onLoadError, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + if (!label.parentNode) { │ │ │ │ + this.textRoot.appendChild(label); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: removeTileMonitoringHooks │ │ │ │ - * This function takes a tile as input and removes the tile hooks │ │ │ │ - * that were added in addTileMonitoringHooks() │ │ │ │ + * Method: getComponentString │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * tile - {<OpenLayers.Tile>} │ │ │ │ + * Parameters: │ │ │ │ + * components - {Array(<OpenLayers.Geometry.Point>)} Array of points │ │ │ │ + * separator - {String} character between coordinate pairs. Defaults to "," │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} hash with properties "path" (the string created from the │ │ │ │ + * components and "complete" (false if the renderer was unable to │ │ │ │ + * draw all components) │ │ │ │ */ │ │ │ │ - removeTileMonitoringHooks: function(tile) { │ │ │ │ - tile.unload(); │ │ │ │ - tile.events.un({ │ │ │ │ - "loadstart": tile.onLoadStart, │ │ │ │ - "loadend": tile.onLoadEnd, │ │ │ │ - "unload": tile.onLoadEnd, │ │ │ │ - "loaderror": tile.onLoadError, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + getComponentsString: function(components, separator) { │ │ │ │ + var renderCmp = []; │ │ │ │ + var complete = true; │ │ │ │ + var len = components.length; │ │ │ │ + var strings = []; │ │ │ │ + var str, component; │ │ │ │ + for (var i = 0; i < len; i++) { │ │ │ │ + component = components[i]; │ │ │ │ + renderCmp.push(component); │ │ │ │ + str = this.getShortString(component); │ │ │ │ + if (str) { │ │ │ │ + strings.push(str); │ │ │ │ + } else { │ │ │ │ + // The current component is outside the valid range. Let's │ │ │ │ + // see if the previous or next component is inside the range. │ │ │ │ + // If so, add the coordinate of the intersection with the │ │ │ │ + // valid range bounds. │ │ │ │ + if (i > 0) { │ │ │ │ + if (this.getShortString(components[i - 1])) { │ │ │ │ + strings.push(this.clipLine(components[i], │ │ │ │ + components[i - 1])); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (i < len - 1) { │ │ │ │ + if (this.getShortString(components[i + 1])) { │ │ │ │ + strings.push(this.clipLine(components[i], │ │ │ │ + components[i + 1])); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + complete = false; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + return { │ │ │ │ + path: strings.join(separator || ","), │ │ │ │ + complete: complete │ │ │ │ + }; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveGriddedTiles │ │ │ │ + * Method: clipLine │ │ │ │ + * Given two points (one inside the valid range, and one outside), │ │ │ │ + * clips the line betweeen the two points so that the new points are both │ │ │ │ + * inside the valid range. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * badComponent - {<OpenLayers.Geometry.Point>} original geometry of the │ │ │ │ + * invalid point │ │ │ │ + * goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the │ │ │ │ + * valid point │ │ │ │ + * Returns │ │ │ │ + * {String} the SVG coordinate pair of the clipped point (like │ │ │ │ + * getShortString), or an empty string if both passed componets are at │ │ │ │ + * the same point. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ + clipLine: function(badComponent, goodComponent) { │ │ │ │ + if (goodComponent.equals(badComponent)) { │ │ │ │ + return ""; │ │ │ │ + } │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var maxX = this.MAX_PIXEL - this.translationParameters.x; │ │ │ │ + var maxY = this.MAX_PIXEL - this.translationParameters.y; │ │ │ │ + var x1 = (goodComponent.x - this.featureDx) / resolution + this.left; │ │ │ │ + var y1 = this.top - goodComponent.y / resolution; │ │ │ │ + var x2 = (badComponent.x - this.featureDx) / resolution + this.left; │ │ │ │ + var y2 = this.top - badComponent.y / resolution; │ │ │ │ + var k; │ │ │ │ + if (x2 < -maxX || x2 > maxX) { │ │ │ │ + k = (y2 - y1) / (x2 - x1); │ │ │ │ + x2 = x2 < 0 ? -maxX : maxX; │ │ │ │ + y2 = y1 + (x2 - x1) * k; │ │ │ │ } │ │ │ │ + if (y2 < -maxY || y2 > maxY) { │ │ │ │ + k = (x2 - x1) / (y2 - y1); │ │ │ │ + y2 = y2 < 0 ? -maxY : maxY; │ │ │ │ + x2 = x1 + (y2 - y1) * k; │ │ │ │ + } │ │ │ │ + return x2 + "," + y2; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: shiftRow │ │ │ │ - * Shifty grid work │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: getShortString │ │ │ │ + * │ │ │ │ * 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 │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} or false if point is outside the valid range │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + getShortString: function(point) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var x = ((point.x - this.featureDx) / resolution + this.left); │ │ │ │ + var y = (this.top - point.y / resolution); │ │ │ │ │ │ │ │ - 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); │ │ │ │ + if (this.inValidRange(x, y)) { │ │ │ │ + return x + "," + y; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ } │ │ │ │ - grid[prepend ? 'unshift' : 'push'](row); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: shiftColumn │ │ │ │ - * Shift grid work in the other dimension │ │ │ │ - * │ │ │ │ + * Method: getPosition │ │ │ │ + * Finds the position of an svg node. │ │ │ │ + * │ │ │ │ * 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 │ │ │ │ + * node - {DOMElement} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} hash with x and y properties, representing the coordinates │ │ │ │ + * within the svg coordinate system │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + getPosition: function(node) { │ │ │ │ + return ({ │ │ │ │ + x: parseFloat(node.getAttributeNS(null, "cx")), │ │ │ │ + y: parseFloat(node.getAttributeNS(null, "cy")) │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: removeExcessTiles │ │ │ │ - * When the size of the map or the buffer changes, we may need to │ │ │ │ - * remove some excess rows and columns. │ │ │ │ + * Method: importSymbol │ │ │ │ + * add a new symbol definition from the rendererer's symbol hash │ │ │ │ * │ │ │ │ * 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. │ │ │ │ + * graphicName - {String} name of the symbol to import │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} - the imported symbol │ │ │ │ */ │ │ │ │ - removeExcessTiles: function(rows, columns) { │ │ │ │ - var i, l; │ │ │ │ + importSymbol: function(graphicName) { │ │ │ │ + if (!this.defs) { │ │ │ │ + // create svg defs tag │ │ │ │ + this.defs = this.createDefs(); │ │ │ │ + } │ │ │ │ + var id = this.container.id + "-" + graphicName; │ │ │ │ │ │ │ │ - // 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); │ │ │ │ - } │ │ │ │ + // check if symbol already exists in the defs │ │ │ │ + var existing = document.getElementById(id); │ │ │ │ + if (existing != null) { │ │ │ │ + return existing; │ │ │ │ } │ │ │ │ │ │ │ │ - // 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); │ │ │ │ - } │ │ │ │ + var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ + if (!symbol) { │ │ │ │ + throw new Error(graphicName + ' is not a valid symbol name'); │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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(); │ │ │ │ + var symbolNode = this.nodeFactory(id, "symbol"); │ │ │ │ + var node = this.nodeFactory(null, "polygon"); │ │ │ │ + symbolNode.appendChild(node); │ │ │ │ + var symbolExtent = new OpenLayers.Bounds( │ │ │ │ + Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ + │ │ │ │ + var points = []; │ │ │ │ + var x, y; │ │ │ │ + for (var i = 0; i < symbol.length; i = i + 2) { │ │ │ │ + x = symbol[i]; │ │ │ │ + y = symbol[i + 1]; │ │ │ │ + symbolExtent.left = Math.min(symbolExtent.left, x); │ │ │ │ + symbolExtent.bottom = Math.min(symbolExtent.bottom, y); │ │ │ │ + symbolExtent.right = Math.max(symbolExtent.right, x); │ │ │ │ + symbolExtent.top = Math.max(symbolExtent.top, y); │ │ │ │ + points.push(x, ",", y); │ │ │ │ } │ │ │ │ + │ │ │ │ + node.setAttributeNS(null, "points", points.join(" ")); │ │ │ │ + │ │ │ │ + var width = symbolExtent.getWidth(); │ │ │ │ + var height = symbolExtent.getHeight(); │ │ │ │ + // create a viewBox three times as large as the symbol itself, │ │ │ │ + // to allow for strokeWidth being displayed correctly at the corners. │ │ │ │ + var viewBox = [symbolExtent.left - width, │ │ │ │ + symbolExtent.bottom - height, width * 3, height * 3 │ │ │ │ + ]; │ │ │ │ + symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" ")); │ │ │ │ + this.symbolMetrics[id] = [ │ │ │ │ + Math.max(width, height), │ │ │ │ + symbolExtent.getCenterLonLat().lon, │ │ │ │ + symbolExtent.getCenterLonLat().lat │ │ │ │ + ]; │ │ │ │ + │ │ │ │ + this.defs.appendChild(symbolNode); │ │ │ │ + return symbolNode; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getTileBounds │ │ │ │ - * Returns The tile bounds for a layer given a pixel location. │ │ │ │ - * │ │ │ │ + * Method: getFeatureIdFromEvent │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport. │ │ │ │ + * evt - {Object} An <OpenLayers.Event> object │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location. │ │ │ │ + * {String} A feature id or undefined. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + getFeatureIdFromEvent: function(evt) { │ │ │ │ + var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments); │ │ │ │ + if (!featureId) { │ │ │ │ + var target = evt.target; │ │ │ │ + featureId = target.parentNode && target != this.rendererRoot ? │ │ │ │ + target.parentNode._featureId : undefined; │ │ │ │ + } │ │ │ │ + return featureId; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Grid" │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.SVG" │ │ │ │ }); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN │ │ │ │ + * {Object} │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.SVG.LABEL_ALIGN = { │ │ │ │ + "l": "start", │ │ │ │ + "r": "end", │ │ │ │ + "b": "bottom", │ │ │ │ + "t": "hanging" │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT │ │ │ │ + * {Object} │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.SVG.LABEL_VSHIFT = { │ │ │ │ + // according to │ │ │ │ + // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html │ │ │ │ + // a baseline-shift of -70% shifts the text exactly from the │ │ │ │ + // bottom to the top of the baseline, so -35% moves the text to │ │ │ │ + // the center of the baseline. │ │ │ │ + "t": "-70%", │ │ │ │ + "b": "0" │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR │ │ │ │ + * {Object} │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.SVG.LABEL_VFACTOR = { │ │ │ │ + "t": 0, │ │ │ │ + "b": -1 │ │ │ │ +}; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Function: OpenLayers.Renderer.SVG.preventDefault │ │ │ │ + * *Deprecated*. Use <OpenLayers.Event.preventDefault> method instead. │ │ │ │ + * Used to prevent default events (especially opening images in a new tab on │ │ │ │ + * ctrl-click) from being executed for externalGraphic symbols │ │ │ │ + */ │ │ │ │ +OpenLayers.Renderer.SVG.preventDefault = function(e) { │ │ │ │ + OpenLayers.Event.preventDefault(e); │ │ │ │ +}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/XYZ.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/Layer/Grid.js │ │ │ │ + * @requires OpenLayers/Filter.js │ │ │ │ */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.XYZ │ │ │ │ - * The XYZ class is designed to make it easier for people who have tiles │ │ │ │ - * arranged by a standard XYZ grid. │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Filter.Comparison │ │ │ │ + * This class represents a comparison filter. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ + * - <OpenLayers.Filter> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ +OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * Default is true, as this is designed to be a base tile source. │ │ │ │ + * 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"; │ │ │ │ */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + type: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: sphericalMercator │ │ │ │ - * Whether the tile extents should be set to the defaults for │ │ │ │ - * spherical mercator. Useful for things like OpenStreetMap. │ │ │ │ - * Default is false, except for the OSM subclass. │ │ │ │ + * APIProperty: property │ │ │ │ + * {String} │ │ │ │ + * name of the context property to compare │ │ │ │ */ │ │ │ │ - sphericalMercator: false, │ │ │ │ + property: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: zoomOffset │ │ │ │ - * {Number} If your cache has more zoom levels than you want to provide │ │ │ │ - * access to with this layer, supply a zoomOffset. This zoom offset │ │ │ │ - * is added to the current map zoom level to determine the level │ │ │ │ - * for a requested tile. For example, if you supply a zoomOffset │ │ │ │ - * of 3, when the map is at the zoom 0, tiles will be requested from │ │ │ │ - * level 3 of your cache. Default is 0 (assumes cache level and map │ │ │ │ - * zoom are equivalent). Using <zoomOffset> is an alternative to │ │ │ │ - * setting <serverResolutions> if you only want to expose a subset │ │ │ │ - * of the server resolutions. │ │ │ │ + * 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}" │ │ │ │ */ │ │ │ │ - zoomOffset: 0, │ │ │ │ + value: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: serverResolutions │ │ │ │ - * {Array} A list of all resolutions available on the server. Only set this │ │ │ │ - * property if the map resolutions differ from the server. This │ │ │ │ - * property serves two purposes. (a) <serverResolutions> can include │ │ │ │ - * resolutions that the server supports and that you don't want to │ │ │ │ - * provide with this layer; you can also look at <zoomOffset>, which is │ │ │ │ - * an alternative to <serverResolutions> for that specific purpose. │ │ │ │ - * (b) The map can work with resolutions that aren't supported by │ │ │ │ - * the server, i.e. that aren't in <serverResolutions>. When the │ │ │ │ - * map is displayed in such a resolution data for the closest │ │ │ │ - * server-supported resolution is loaded and the layer div is │ │ │ │ - * stretched as necessary. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - serverResolutions: null, │ │ │ │ + matchCase: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.XYZ │ │ │ │ + * APIProperty: lowerBoundary │ │ │ │ + * {Number} or {String} │ │ │ │ + * lower boundary for between comparisons. In the case of a String, this │ │ │ │ + * can be a combination of text and propertyNames in the form │ │ │ │ + * "literal ${propertyName}" │ │ │ │ + */ │ │ │ │ + lowerBoundary: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: upperBoundary │ │ │ │ + * {Number} or {String} │ │ │ │ + * upper boundary for between comparisons. In the case of a String, this │ │ │ │ + * can be a combination of text and propertyNames in the form │ │ │ │ + * "literal ${propertyName}" │ │ │ │ + */ │ │ │ │ + upperBoundary: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Filter.Comparison │ │ │ │ + * Creates a comparison rule. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * url - {String} │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * options - {Object} An optional object with properties to set on the │ │ │ │ + * rule │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Filter.Comparison>} │ │ │ │ */ │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - if (options && options.sphericalMercator || this.sphericalMercator) { │ │ │ │ - options = OpenLayers.Util.extend({ │ │ │ │ - projection: "EPSG:900913", │ │ │ │ - numZoomLevels: 19 │ │ │ │ - }, options); │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, [ │ │ │ │ - name || this.name, url || this.url, {}, │ │ │ │ - options │ │ │ │ - ]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ - * │ │ │ │ + * APIMethod: evaluate │ │ │ │ + * Evaluates this filter in a specific context. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * obj - {Object} Is this ever used? │ │ │ │ + * context - {Object} Context to use in evaluating the filter. If a vector │ │ │ │ + * feature is provided, the feature.attributes will be used as context. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ │ │ │ │ + * {Boolean} The filter applies. │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.XYZ(this.name, │ │ │ │ - this.url, │ │ │ │ - this.getOptions()); │ │ │ │ + evaluate: function(context) { │ │ │ │ + if (context instanceof OpenLayers.Feature.Vector) { │ │ │ │ + context = context.attributes; │ │ │ │ } │ │ │ │ - │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - │ │ │ │ - return obj; │ │ │ │ + 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; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ - * │ │ │ │ + * 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: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * │ │ │ │ + * 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} A string with the layer's url and parameters and also the │ │ │ │ - * passed-in bounds and appropriate tile size specified as │ │ │ │ - * parameters │ │ │ │ + * {String} regular expression string │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - var xyz = this.getXYZ(bounds); │ │ │ │ - var url = this.url; │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - var s = '' + xyz.x + xyz.y + xyz.z; │ │ │ │ - url = this.selectUrl(s, url); │ │ │ │ + value2regex: function(wildCard, singleChar, escapeChar) { │ │ │ │ + if (wildCard == ".") { │ │ │ │ + throw new Error("'.' is an unsupported wildCard character for " + │ │ │ │ + "OpenLayers.Filter.Comparison"); │ │ │ │ } │ │ │ │ │ │ │ │ - return OpenLayers.String.format(url, xyz); │ │ │ │ + │ │ │ │ + // 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: getXYZ │ │ │ │ - * Calculates x, y and z for the given bounds. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * │ │ │ │ + * Method: regex2value │ │ │ │ + * Convert the value of this rule from a regular expression string into an │ │ │ │ + * ogc literal string using a wildCard of *, a singleChar of ., and an │ │ │ │ + * escape of !. Leaves the <value> property unmodified. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Object} - an object with x, y and z properties. │ │ │ │ + * {String} A string value. │ │ │ │ */ │ │ │ │ - getXYZ: function(bounds) { │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var x = Math.round((bounds.left - this.maxExtent.left) / │ │ │ │ - (res * this.tileSize.w)); │ │ │ │ - var y = Math.round((this.maxExtent.top - bounds.top) / │ │ │ │ - (res * this.tileSize.h)); │ │ │ │ - var z = this.getServerZoom(); │ │ │ │ + regex2value: function() { │ │ │ │ │ │ │ │ - if (this.wrapDateLine) { │ │ │ │ - var limit = Math.pow(2, z); │ │ │ │ - x = ((x % limit) + limit) % limit; │ │ │ │ - } │ │ │ │ + var value = this.value; │ │ │ │ │ │ │ │ - return { │ │ │ │ - 'x': x, │ │ │ │ - 'y': y, │ │ │ │ - 'z': z │ │ │ │ - }; │ │ │ │ + // replace ! with !! │ │ │ │ + value = value.replace(/!/g, "!!"); │ │ │ │ + │ │ │ │ + // replace \. with !. (watching out for \\.) │ │ │ │ + value = value.replace(/(\\)?\\\./g, function($0, $1) { │ │ │ │ + return $1 ? $0 : "!."; │ │ │ │ + }); │ │ │ │ + │ │ │ │ + // replace \* with #* (watching out for \\*) │ │ │ │ + value = value.replace(/(\\)?\\\*/g, function($0, $1) { │ │ │ │ + return $1 ? $0 : "!*"; │ │ │ │ + }); │ │ │ │ + │ │ │ │ + // replace \\ with \ │ │ │ │ + value = value.replace(/\\\\/g, "\\"); │ │ │ │ + │ │ │ │ + // convert .* to * (the sequence #.* is not allowed) │ │ │ │ + value = value.replace(/\.\*/g, "*"); │ │ │ │ + │ │ │ │ + return value; │ │ │ │ }, │ │ │ │ │ │ │ │ - /* APIMethod: setMap │ │ │ │ - * When the layer is added to a map, then we can fetch our origin │ │ │ │ - * (if we don't have one.) │ │ │ │ + /** │ │ │ │ + * APIMethod: clone │ │ │ │ + * Clones this filter. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Filter.Comparison>} Clone of this filter. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ - if (!this.tileOrigin) { │ │ │ │ - this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, │ │ │ │ - this.maxExtent.bottom); │ │ │ │ - } │ │ │ │ + clone: function() { │ │ │ │ + return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.XYZ" │ │ │ │ + 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/Layer/Bing.js │ │ │ │ + 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/Layer/XYZ.js │ │ │ │ + * @requires OpenLayers/Filter.js │ │ │ │ */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.Bing │ │ │ │ - * Bing layer using direct tile access as provided by Bing Maps REST Services. │ │ │ │ - * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more │ │ │ │ - * information. Note: Terms of Service compliant use requires the map to be │ │ │ │ - * configured with an <OpenLayers.Control.Attribution> control and the │ │ │ │ - * attribution placed on or near the map. │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Filter.Logical │ │ │ │ + * This class represents ogc:And, ogc:Or and ogc:Not rules. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.XYZ> │ │ │ │ + * - <OpenLayers.Filter> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ +OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: key │ │ │ │ - * {String} API key for Bing maps, get your own key │ │ │ │ - * at http://bingmapsportal.com/ . │ │ │ │ + * APIProperty: filters │ │ │ │ + * {Array(<OpenLayers.Filter>)} Child filters for this filter. │ │ │ │ */ │ │ │ │ - key: null, │ │ │ │ + filters: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: serverResolutions │ │ │ │ - * {Array} the resolutions provided by the Bing servers. │ │ │ │ + * APIProperty: type │ │ │ │ + * {String} type of logical operator. Available types are: │ │ │ │ + * - OpenLayers.Filter.Logical.AND = "&&"; │ │ │ │ + * - OpenLayers.Filter.Logical.OR = "||"; │ │ │ │ + * - OpenLayers.Filter.Logical.NOT = "!"; │ │ │ │ */ │ │ │ │ - serverResolutions: [ │ │ │ │ - 156543.03390625, 78271.516953125, 39135.7584765625, │ │ │ │ - 19567.87923828125, 9783.939619140625, 4891.9698095703125, │ │ │ │ - 2445.9849047851562, 1222.9924523925781, 611.4962261962891, │ │ │ │ - 305.74811309814453, 152.87405654907226, 76.43702827453613, │ │ │ │ - 38.218514137268066, 19.109257068634033, 9.554628534317017, │ │ │ │ - 4.777314267158508, 2.388657133579254, 1.194328566789627, │ │ │ │ - 0.5971642833948135, 0.29858214169740677, 0.14929107084870338, │ │ │ │ - 0.07464553542435169 │ │ │ │ - ], │ │ │ │ + type: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: attributionTemplate │ │ │ │ - * {String} │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Filter.Logical │ │ │ │ + * Creates a logical filter (And, Or, Not). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object with properties to set on the │ │ │ │ + * filter. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Filter.Logical>} │ │ │ │ */ │ │ │ │ - attributionTemplate: '<span class="olBingAttribution ${type}">' + │ │ │ │ - '<div><a target="_blank" href="http://www.bing.com/maps/">' + │ │ │ │ - '<img src="${logo}" /></a></div>${copyrights}' + │ │ │ │ - '<a style="white-space: nowrap" target="_blank" ' + │ │ │ │ - 'href="http://www.microsoft.com/maps/product/terms.html">' + │ │ │ │ - 'Terms of Use</a></span>', │ │ │ │ + initialize: function(options) { │ │ │ │ + this.filters = []; │ │ │ │ + OpenLayers.Filter.prototype.initialize.apply(this, [options]); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: metadata │ │ │ │ - * {Object} Metadata for this layer, as returned by the callback script │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Remove reference to child filters. │ │ │ │ */ │ │ │ │ - metadata: null, │ │ │ │ + destroy: function() { │ │ │ │ + this.filters = null; │ │ │ │ + OpenLayers.Filter.prototype.destroy.apply(this); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: protocolRegex │ │ │ │ - * {RegExp} Regular expression to match and replace http: in bing urls │ │ │ │ + * APIMethod: evaluate │ │ │ │ + * Evaluates this filter in a specific context. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * context - {Object} Context to use in evaluating the filter. A vector │ │ │ │ + * feature may also be provided to evaluate feature attributes in │ │ │ │ + * comparison filters or geometries in spatial filters. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The filter applies. │ │ │ │ */ │ │ │ │ - protocolRegex: /^http:/i, │ │ │ │ + evaluate: function(context) { │ │ │ │ + var i, len; │ │ │ │ + switch (this.type) { │ │ │ │ + case OpenLayers.Filter.Logical.AND: │ │ │ │ + for (i = 0, len = this.filters.length; i < len; i++) { │ │ │ │ + if (this.filters[i].evaluate(context) == false) { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return true; │ │ │ │ + │ │ │ │ + case OpenLayers.Filter.Logical.OR: │ │ │ │ + for (i = 0, len = this.filters.length; i < len; i++) { │ │ │ │ + if (this.filters[i].evaluate(context) == true) { │ │ │ │ + return true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return false; │ │ │ │ + │ │ │ │ + case OpenLayers.Filter.Logical.NOT: │ │ │ │ + return (!this.filters[0].evaluate(context)); │ │ │ │ + } │ │ │ │ + return undefined; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: type │ │ │ │ - * {String} The layer identifier. Any non-birdseye imageryType │ │ │ │ - * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be │ │ │ │ - * used. Default is "Road". │ │ │ │ + * APIMethod: clone │ │ │ │ + * Clones this filter. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Filter.Logical>} Clone of this filter. │ │ │ │ */ │ │ │ │ - type: "Road", │ │ │ │ + clone: function() { │ │ │ │ + var filters = []; │ │ │ │ + for (var i = 0, len = this.filters.length; i < len; ++i) { │ │ │ │ + filters.push(this.filters[i].clone()); │ │ │ │ + } │ │ │ │ + return new OpenLayers.Filter.Logical({ │ │ │ │ + type: this.type, │ │ │ │ + filters: filters │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Filter.Logical" │ │ │ │ +}); │ │ │ │ + │ │ │ │ + │ │ │ │ +OpenLayers.Filter.Logical.AND = "&&"; │ │ │ │ +OpenLayers.Filter.Logical.OR = "||"; │ │ │ │ +OpenLayers.Filter.Logical.NOT = "!"; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Protocol.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Protocol │ │ │ │ + * Abstract vector layer protocol class. Not to be instantiated directly. Use │ │ │ │ + * one of the protocol subclasses instead. │ │ │ │ + */ │ │ │ │ +OpenLayers.Protocol = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: culture │ │ │ │ - * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx │ │ │ │ - * for the definition and the possible values. Default is "en-US". │ │ │ │ + * Property: format │ │ │ │ + * {<OpenLayers.Format>} The format used by this protocol. │ │ │ │ */ │ │ │ │ - culture: "en-US", │ │ │ │ + format: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: metadataParams │ │ │ │ - * {Object} Optional url parameters for the Get Imagery Metadata request │ │ │ │ - * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx │ │ │ │ + * Property: options │ │ │ │ + * {Object} Any options sent to the constructor. │ │ │ │ */ │ │ │ │ - metadataParams: null, │ │ │ │ + options: null, │ │ │ │ │ │ │ │ - /** APIProperty: tileOptions │ │ │ │ - * {Object} optional configuration options for <OpenLayers.Tile> instances │ │ │ │ - * created by this Layer. Default is │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * {crossOriginKeyword: 'anonymous'} │ │ │ │ - * (end) │ │ │ │ + /** │ │ │ │ + * Property: autoDestroy │ │ │ │ + * {Boolean} The creator of the protocol can set autoDestroy to false │ │ │ │ + * to fully control when the protocol is destroyed. Defaults to │ │ │ │ + * true. │ │ │ │ */ │ │ │ │ - tileOptions: null, │ │ │ │ + autoDestroy: true, │ │ │ │ │ │ │ │ - /** APIProperty: protocol │ │ │ │ - * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo │ │ │ │ - * Can be 'http:' 'https:' or '' │ │ │ │ - * │ │ │ │ - * Warning: tiles may not be available under both HTTP and HTTPS protocols. │ │ │ │ - * Microsoft approved use of both HTTP and HTTPS urls for tiles. However │ │ │ │ - * this is undocumented and the Imagery Metadata API always returns HTTP │ │ │ │ - * urls. │ │ │ │ - * │ │ │ │ - * Default is '', unless when executed from a file:/// uri, in which case │ │ │ │ - * it is 'http:'. │ │ │ │ + /** │ │ │ │ + * Property: defaultFilter │ │ │ │ + * {<OpenLayers.Filter>} Optional default filter to read requests │ │ │ │ */ │ │ │ │ - protocol: ~window.location.href.indexOf('http') ? '' : 'http:', │ │ │ │ + defaultFilter: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.Bing │ │ │ │ - * Create a new Bing layer. │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * var road = new OpenLayers.Layer.Bing({ │ │ │ │ - * name: "My Bing Aerial Layer", │ │ │ │ - * type: "Aerial", │ │ │ │ - * key: "my-api-key-here", │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ + * Constructor: OpenLayers.Protocol │ │ │ │ + * Abstract class for vector protocols. Create instances of a subclass. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Configuration properties for the layer. │ │ │ │ - * │ │ │ │ - * Required configuration properties: │ │ │ │ - * key - {String} Bing Maps API key for your application. Get one at │ │ │ │ - * http://bingmapsportal.com/. │ │ │ │ - * type - {String} The layer identifier. Any non-birdseye imageryType │ │ │ │ - * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be │ │ │ │ - * used. │ │ │ │ - * │ │ │ │ - * Any other documented layer properties can be provided in the config object. │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ initialize: function(options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults({ │ │ │ │ - sphericalMercator: true │ │ │ │ - }, options); │ │ │ │ - var name = options.name || "Bing " + (options.type || this.type); │ │ │ │ + options = options || {}; │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.options = options; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var newArgs = [name, null, options]; │ │ │ │ - OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs); │ │ │ │ - this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ - crossOriginKeyword: 'anonymous' │ │ │ │ - }, this.options.tileOptions); │ │ │ │ - this.loadMetadata(); │ │ │ │ + /** │ │ │ │ + * Method: mergeWithDefaultFilter │ │ │ │ + * Merge filter passed to the read method with the default one │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * filter - {<OpenLayers.Filter>} │ │ │ │ + */ │ │ │ │ + mergeWithDefaultFilter: function(filter) { │ │ │ │ + var merged; │ │ │ │ + if (filter && this.defaultFilter) { │ │ │ │ + merged = new OpenLayers.Filter.Logical({ │ │ │ │ + type: OpenLayers.Filter.Logical.AND, │ │ │ │ + filters: [this.defaultFilter, filter] │ │ │ │ + }); │ │ │ │ + } else { │ │ │ │ + merged = filter || this.defaultFilter || undefined; │ │ │ │ + } │ │ │ │ + return merged; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: loadMetadata │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up the protocol. │ │ │ │ */ │ │ │ │ - loadMetadata: function() { │ │ │ │ - this._callbackId = "_callback_" + this.id.replace(/\./g, "_"); │ │ │ │ - // link the processMetadata method to the global scope and bind it │ │ │ │ - // to this instance │ │ │ │ - window[this._callbackId] = OpenLayers.Function.bind( │ │ │ │ - OpenLayers.Layer.Bing.processMetadata, this │ │ │ │ - ); │ │ │ │ - var params = OpenLayers.Util.applyDefaults({ │ │ │ │ - key: this.key, │ │ │ │ - jsonp: this._callbackId, │ │ │ │ - include: "ImageryProviders" │ │ │ │ - }, this.metadataParams); │ │ │ │ - var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" + │ │ │ │ - this.type + "?" + OpenLayers.Util.getParameterString(params); │ │ │ │ - var script = document.createElement("script"); │ │ │ │ - script.type = "text/javascript"; │ │ │ │ - script.src = url; │ │ │ │ - script.id = this._callbackId; │ │ │ │ - document.getElementsByTagName("head")[0].appendChild(script); │ │ │ │ + destroy: function() { │ │ │ │ + this.options = null; │ │ │ │ + this.format = null; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: initLayer │ │ │ │ + * APIMethod: read │ │ │ │ + * Construct a request for reading new features. │ │ │ │ * │ │ │ │ - * Sets layer properties according to the metadata provided by the API │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ + * object, the same object will be passed to the callback function passed │ │ │ │ + * if one exists in the options object. │ │ │ │ */ │ │ │ │ - initLayer: function() { │ │ │ │ - var res = this.metadata.resourceSets[0].resources[0]; │ │ │ │ - var url = res.imageUrl.replace("{quadkey}", "${quadkey}"); │ │ │ │ - url = url.replace("{culture}", this.culture); │ │ │ │ - url = url.replace(this.protocolRegex, this.protocol); │ │ │ │ - this.url = []; │ │ │ │ - for (var i = 0; i < res.imageUrlSubdomains.length; ++i) { │ │ │ │ - this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i])); │ │ │ │ - } │ │ │ │ - this.addOptions({ │ │ │ │ - maxResolution: Math.min( │ │ │ │ - this.serverResolutions[res.zoomMin], │ │ │ │ - this.maxResolution || Number.POSITIVE_INFINITY │ │ │ │ - ), │ │ │ │ - numZoomLevels: Math.min( │ │ │ │ - res.zoomMax + 1 - res.zoomMin, this.numZoomLevels │ │ │ │ - ) │ │ │ │ - }, true); │ │ │ │ - if (!this.isBaseLayer) { │ │ │ │ - this.redraw(); │ │ │ │ - } │ │ │ │ - this.updateAttribution(); │ │ │ │ + read: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + options.filter = this.mergeWithDefaultFilter(options.filter); │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ + * APIMethod: create │ │ │ │ + * Construct a request for writing newly created features. │ │ │ │ * │ │ │ │ - * Paramters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ + * {<OpenLayers.Feature.Vector>} │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ + * object, the same object will be passed to the callback function passed │ │ │ │ + * if one exists in the options object. │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - if (!this.url) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - var xyz = this.getXYZ(bounds), │ │ │ │ - x = xyz.x, │ │ │ │ - y = xyz.y, │ │ │ │ - z = xyz.z; │ │ │ │ - var quadDigits = []; │ │ │ │ - for (var i = z; i > 0; --i) { │ │ │ │ - var digit = '0'; │ │ │ │ - var mask = 1 << (i - 1); │ │ │ │ - if ((x & mask) != 0) { │ │ │ │ - digit++; │ │ │ │ - } │ │ │ │ - if ((y & mask) != 0) { │ │ │ │ - digit++; │ │ │ │ - digit++; │ │ │ │ - } │ │ │ │ - quadDigits.push(digit); │ │ │ │ - } │ │ │ │ - var quadKey = quadDigits.join(""); │ │ │ │ - var url = this.selectUrl('' + x + y + z, this.url); │ │ │ │ - │ │ │ │ - return OpenLayers.String.format(url, { │ │ │ │ - 'quadkey': quadKey │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ + create: function() {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: updateAttribution │ │ │ │ - * Updates the attribution according to the requirements outlined in │ │ │ │ - * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html │ │ │ │ + * APIMethod: update │ │ │ │ + * Construct a request updating modified features. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ + * {<OpenLayers.Feature.Vector>} │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ + * object, the same object will be passed to the callback function passed │ │ │ │ + * if one exists in the options object. │ │ │ │ */ │ │ │ │ - updateAttribution: function() { │ │ │ │ - var metadata = this.metadata; │ │ │ │ - if (!metadata.resourceSets || !this.map || !this.map.center) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - var res = metadata.resourceSets[0].resources[0]; │ │ │ │ - var extent = this.map.getExtent().transform( │ │ │ │ - this.map.getProjectionObject(), │ │ │ │ - new OpenLayers.Projection("EPSG:4326") │ │ │ │ - ); │ │ │ │ - var providers = res.imageryProviders || [], │ │ │ │ - zoom = OpenLayers.Util.indexOf(this.serverResolutions, │ │ │ │ - this.getServerResolution()), │ │ │ │ - copyrights = "", │ │ │ │ - provider, i, ii, j, jj, bbox, coverage; │ │ │ │ - for (i = 0, ii = providers.length; i < ii; ++i) { │ │ │ │ - provider = providers[i]; │ │ │ │ - for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) { │ │ │ │ - coverage = provider.coverageAreas[j]; │ │ │ │ - // axis order provided is Y,X │ │ │ │ - bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true); │ │ │ │ - if (extent.intersectsBounds(bbox) && │ │ │ │ - zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) { │ │ │ │ - copyrights += provider.attribution + " "; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol); │ │ │ │ - this.attribution = OpenLayers.String.format(this.attributionTemplate, { │ │ │ │ - type: this.type.toLowerCase(), │ │ │ │ - logo: logo, │ │ │ │ - copyrights: copyrights │ │ │ │ - }); │ │ │ │ - this.map && this.map.events.triggerEvent("changelayer", { │ │ │ │ - layer: this, │ │ │ │ - property: "attribution" │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ + update: function() {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setMap │ │ │ │ + * APIMethod: delete │ │ │ │ + * Construct a request deleting a removed feature. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ + * object, the same object will be passed to the callback function passed │ │ │ │ + * if one exists in the options object. │ │ │ │ */ │ │ │ │ - setMap: function() { │ │ │ │ - OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments); │ │ │ │ - this.map.events.register("moveend", this, this.updateAttribution); │ │ │ │ - }, │ │ │ │ + "delete": function() {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * │ │ │ │ + * APIMethod: commit │ │ │ │ + * Go over the features and for each take action │ │ │ │ + * based on the feature state. Possible actions are create, │ │ │ │ + * update and delete. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * obj - {Object} │ │ │ │ - * │ │ │ │ + * features - {Array({<OpenLayers.Feature.Vector>})} │ │ │ │ + * options - {Object} Object whose possible keys are "create", "update", │ │ │ │ + * "delete", "callback" and "scope", the values referenced by the │ │ │ │ + * first three are objects as passed to the "create", "update", and │ │ │ │ + * "delete" methods, the value referenced by the "callback" key is │ │ │ │ + * a function which is called when the commit operation is complete │ │ │ │ + * using the scope referenced by the "scope" key. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing> │ │ │ │ + * {Array({<OpenLayers.Protocol.Response>})} An array of │ │ │ │ + * <OpenLayers.Protocol.Response> objects. │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Bing(this.options); │ │ │ │ - } │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - return obj; │ │ │ │ - }, │ │ │ │ + commit: function() {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ + * Method: abort │ │ │ │ + * Abort an ongoing request. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.map && │ │ │ │ - this.map.events.unregister("moveend", this, this.updateAttribution); │ │ │ │ - OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments); │ │ │ │ + abort: function(response) {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: createCallback │ │ │ │ + * Returns a function that applies the given public method with resp and │ │ │ │ + * options arguments. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * method - {Function} The method to be applied by the callback. │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} The protocol response object. │ │ │ │ + * options - {Object} Options sent to the protocol method │ │ │ │ + */ │ │ │ │ + createCallback: function(method, response, options) { │ │ │ │ + return OpenLayers.Function.bind(function() { │ │ │ │ + method.apply(this, [response, options]); │ │ │ │ + }, this); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Bing" │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol" │ │ │ │ }); │ │ │ │ │ │ │ │ /** │ │ │ │ - * Function: OpenLayers.Layer.Bing.processMetadata │ │ │ │ - * This function will be bound to an instance, linked to the global scope with │ │ │ │ - * an id, and called by the JSONP script returned by the API. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * metadata - {Object} metadata as returned by the API │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Bing.processMetadata = function(metadata) { │ │ │ │ - this.metadata = metadata; │ │ │ │ - this.initLayer(); │ │ │ │ - var script = document.getElementById(this._callbackId); │ │ │ │ - script.parentNode.removeChild(script); │ │ │ │ - window[this._callbackId] = undefined; // cannot delete from window in IE │ │ │ │ - delete this._callbackId; │ │ │ │ -}; │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Renderer.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Renderer │ │ │ │ - * This is the base class for all renderers. │ │ │ │ - * │ │ │ │ - * This is based on a merger code written by Paul Spencer and Bertil Chapuis. │ │ │ │ - * It is largely composed of virtual functions that are to be implemented │ │ │ │ - * in technology-specific subclasses, but there is some generic code too. │ │ │ │ - * │ │ │ │ - * The functions that *are* implemented here merely deal with the maintenance │ │ │ │ - * of the size and extent variables, as well as the cached 'resolution' │ │ │ │ - * value. │ │ │ │ - * │ │ │ │ - * A note to the user that all subclasses should use getResolution() instead │ │ │ │ - * of directly accessing this.resolution in order to correctly use the │ │ │ │ - * cacheing system. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Protocol.Response │ │ │ │ + * Protocols return Response objects to their users. │ │ │ │ */ │ │ │ │ -OpenLayers.Renderer = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: container │ │ │ │ - * {DOMElement} │ │ │ │ +OpenLayers.Protocol.Response = OpenLayers.Class({ │ │ │ │ + /** │ │ │ │ + * Property: code │ │ │ │ + * {Number} - OpenLayers.Protocol.Response.SUCCESS or │ │ │ │ + * OpenLayers.Protocol.Response.FAILURE │ │ │ │ */ │ │ │ │ - container: null, │ │ │ │ + code: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: root │ │ │ │ - * {DOMElement} │ │ │ │ + * Property: requestType │ │ │ │ + * {String} The type of request this response corresponds to. Either │ │ │ │ + * "create", "read", "update" or "delete". │ │ │ │ */ │ │ │ │ - root: null, │ │ │ │ + requestType: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: extent │ │ │ │ - * {<OpenLayers.Bounds>} │ │ │ │ + /** │ │ │ │ + * Property: last │ │ │ │ + * {Boolean} - true if this is the last response expected in a commit, │ │ │ │ + * false otherwise, defaults to true. │ │ │ │ */ │ │ │ │ - extent: null, │ │ │ │ + last: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: features │ │ │ │ + * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>} │ │ │ │ + * The features returned in the response by the server. Depending on the │ │ │ │ + * protocol's read payload, either features or data will be populated. │ │ │ │ */ │ │ │ │ - locked: false, │ │ │ │ + features: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: size │ │ │ │ - * {<OpenLayers.Size>} │ │ │ │ + /** │ │ │ │ + * Property: data │ │ │ │ + * {Object} │ │ │ │ + * The data returned in the response by the server. Depending on the │ │ │ │ + * protocol's read payload, either features or data will be populated. │ │ │ │ */ │ │ │ │ - size: null, │ │ │ │ + data: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: resolution │ │ │ │ - * {Float} cache of current map resolution │ │ │ │ + * Property: reqFeatures │ │ │ │ + * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>} │ │ │ │ + * The features provided by the user and placed in the request by the │ │ │ │ + * protocol. │ │ │ │ */ │ │ │ │ - resolution: null, │ │ │ │ + reqFeatures: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: map │ │ │ │ - * {<OpenLayers.Map>} Reference to the map -- this is set in Vector's setMap() │ │ │ │ + * Property: priv │ │ │ │ */ │ │ │ │ - map: null, │ │ │ │ + priv: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: featureDx │ │ │ │ - * {Number} Feature offset in x direction. Will be calculated for and │ │ │ │ - * applied to the current feature while rendering (see │ │ │ │ - * <calculateFeatureDx>). │ │ │ │ + * Property: error │ │ │ │ + * {Object} The error object in case a service exception was encountered. │ │ │ │ */ │ │ │ │ - featureDx: 0, │ │ │ │ + error: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Renderer │ │ │ │ + * Constructor: OpenLayers.Protocol.Response │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * containerID - {<String>} │ │ │ │ - * options - {Object} options for this renderer. See sublcasses for │ │ │ │ - * supported options. │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ */ │ │ │ │ - initialize: function(containerID, options) { │ │ │ │ - this.container = OpenLayers.Util.getElement(containerID); │ │ │ │ + initialize: function(options) { │ │ │ │ OpenLayers.Util.extend(this, options); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ + * Method: success │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} - true on success, false otherwise │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.container = null; │ │ │ │ - this.extent = null; │ │ │ │ - this.size = null; │ │ │ │ - this.resolution = null; │ │ │ │ - this.map = null; │ │ │ │ + success: function() { │ │ │ │ + return this.code > 0; │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.Response" │ │ │ │ +}); │ │ │ │ + │ │ │ │ +OpenLayers.Protocol.Response.SUCCESS = 1; │ │ │ │ +OpenLayers.Protocol.Response.FAILURE = 0; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Request.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Events.js │ │ │ │ + * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * TODO: deprecate me │ │ │ │ + * Use OpenLayers.Request.proxy instead. │ │ │ │ + */ │ │ │ │ +OpenLayers.ProxyHost = ""; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Namespace: OpenLayers.Request │ │ │ │ + * The OpenLayers.Request namespace contains convenience methods for working │ │ │ │ + * with XMLHttpRequests. These methods work with a cross-browser │ │ │ │ + * W3C compliant <OpenLayers.Request.XMLHttpRequest> class. │ │ │ │ + */ │ │ │ │ +if (!OpenLayers.Request) { │ │ │ │ /** │ │ │ │ - * APIMethod: supported │ │ │ │ - * This should be overridden by specific subclasses │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Whether or not the browser supports the renderer class │ │ │ │ + * This allows for OpenLayers/Request/XMLHttpRequest.js to be included │ │ │ │ + * before or after this script. │ │ │ │ */ │ │ │ │ - supported: function() { │ │ │ │ - return false; │ │ │ │ - }, │ │ │ │ + OpenLayers.Request = {}; │ │ │ │ +} │ │ │ │ +OpenLayers.Util.extend(OpenLayers.Request, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setExtent │ │ │ │ - * Set the visible part of the layer. │ │ │ │ - * │ │ │ │ - * Resolution has probably changed, so we nullify the resolution │ │ │ │ - * cache (this.resolution) -- this way it will be re-computed when │ │ │ │ - * next it is needed. │ │ │ │ - * We nullify the resolution cache (this.resolution) if resolutionChanged │ │ │ │ - * is set to true - this way it will be re-computed on the next │ │ │ │ - * getResolution() request. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * extent - {<OpenLayers.Bounds>} │ │ │ │ - * resolutionChanged - {Boolean} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} true to notify the layer that the new extent does not exceed │ │ │ │ - * the coordinate range, and the features will not need to be redrawn. │ │ │ │ - * False otherwise. │ │ │ │ + * Constant: DEFAULT_CONFIG │ │ │ │ + * {Object} Default configuration for all requests. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + DEFAULT_CONFIG: { │ │ │ │ + method: "GET", │ │ │ │ + url: window.location.href, │ │ │ │ + async: true, │ │ │ │ + user: undefined, │ │ │ │ + password: undefined, │ │ │ │ + params: null, │ │ │ │ + proxy: OpenLayers.ProxyHost, │ │ │ │ + headers: {}, │ │ │ │ + data: null, │ │ │ │ + callback: function() {}, │ │ │ │ + success: null, │ │ │ │ + failure: null, │ │ │ │ + scope: null │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setSize │ │ │ │ - * Sets the size of the drawing surface. │ │ │ │ - * │ │ │ │ - * Resolution has probably changed, so we nullify the resolution │ │ │ │ - * cache (this.resolution) -- this way it will be re-computed when │ │ │ │ - * next it is needed. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * size - {<OpenLayers.Size>} │ │ │ │ + * Constant: URL_SPLIT_REGEX │ │ │ │ */ │ │ │ │ - setSize: function(size) { │ │ │ │ - this.size = size.clone(); │ │ │ │ - this.resolution = null; │ │ │ │ - }, │ │ │ │ + URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getResolution │ │ │ │ - * Uses cached copy of resolution if available to minimize computing │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} An events object that handles all │ │ │ │ + * events on the {<OpenLayers.Request>} object. │ │ │ │ + * │ │ │ │ + * All event listeners will receive an event object with three properties: │ │ │ │ + * request - {<OpenLayers.Request.XMLHttpRequest>} The request object. │ │ │ │ + * config - {Object} The config object sent to the specific request method. │ │ │ │ + * requestUrl - {String} The request url. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Float} The current map's resolution │ │ │ │ + * Supported event types: │ │ │ │ + * complete - Triggered when we have a response from the request, if a │ │ │ │ + * listener returns false, no further response processing will take │ │ │ │ + * place. │ │ │ │ + * success - Triggered when the HTTP response has a success code (200-299). │ │ │ │ + * failure - Triggered when the HTTP response does not have a success code. │ │ │ │ */ │ │ │ │ - getResolution: function() { │ │ │ │ - this.resolution = this.resolution || this.map.getResolution(); │ │ │ │ - return this.resolution; │ │ │ │ - }, │ │ │ │ + events: new OpenLayers.Events(this), │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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(). │ │ │ │ + * Method: makeSameOrigin │ │ │ │ + * Using the specified proxy, returns a same origin url of the provided url. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * style - {<Object>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} true if the feature has been drawn completely, false if not, │ │ │ │ - * undefined if the feature had no geometry │ │ │ │ + * url - {String} An arbitrary url │ │ │ │ + * proxy {String|Function} The proxy to use to make the provided url a │ │ │ │ + * same origin url. │ │ │ │ + * │ │ │ │ + * Returns │ │ │ │ + * {String} the same origin url. If no proxy is provided, the returned url │ │ │ │ + * will be the same as the provided url. │ │ │ │ */ │ │ │ │ - drawFeature: function(feature, style) { │ │ │ │ - if (style == null) { │ │ │ │ - style = feature.style; │ │ │ │ + makeSameOrigin: function(url, proxy) { │ │ │ │ + var sameOrigin = url.indexOf("http") !== 0; │ │ │ │ + var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX); │ │ │ │ + if (urlParts) { │ │ │ │ + var location = window.location; │ │ │ │ + sameOrigin = │ │ │ │ + urlParts[1] == location.protocol && │ │ │ │ + urlParts[3] == location.hostname; │ │ │ │ + var uPort = urlParts[4], │ │ │ │ + lPort = location.port; │ │ │ │ + if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") { │ │ │ │ + sameOrigin = sameOrigin && uPort == lPort; │ │ │ │ + } │ │ │ │ } │ │ │ │ - if (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); │ │ │ │ + if (!sameOrigin) { │ │ │ │ + if (proxy) { │ │ │ │ + if (typeof proxy == "function") { │ │ │ │ + url = proxy(url); │ │ │ │ } else { │ │ │ │ - this.removeText(feature.id); │ │ │ │ + url = proxy + encodeURIComponent(url); │ │ │ │ } │ │ │ │ - return rendered; │ │ │ │ } │ │ │ │ } │ │ │ │ + return url; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIMethod: issue │ │ │ │ + * Create a new XMLHttpRequest object, open it, set any headers, bind │ │ │ │ + * a callback to done state, and send any data. It is recommended that │ │ │ │ + * you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>. │ │ │ │ + * This method is only documented to provide detail on the configuration │ │ │ │ + * options available to all request methods. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} Bounds of the feature │ │ │ │ - * worldBounds - {<OpenLayers.Bounds>} Bounds of the world │ │ │ │ + * config - {Object} Object containing properties for configuring the │ │ │ │ + * request. Allowed configuration properties are described below. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ + * │ │ │ │ + * Allowed config properties: │ │ │ │ + * method - {String} One of GET, POST, PUT, DELETE, HEAD, or │ │ │ │ + * OPTIONS. Default is GET. │ │ │ │ + * url - {String} URL for the request. │ │ │ │ + * async - {Boolean} Open an asynchronous request. Default is true. │ │ │ │ + * user - {String} User for relevant authentication scheme. Set │ │ │ │ + * to null to clear current user. │ │ │ │ + * password - {String} Password for relevant authentication scheme. │ │ │ │ + * Set to null to clear current password. │ │ │ │ + * proxy - {String} Optional proxy. Defaults to │ │ │ │ + * <OpenLayers.ProxyHost>. │ │ │ │ + * params - {Object} Any key:value pairs to be appended to the │ │ │ │ + * url as a query string. Assumes url doesn't already include a query │ │ │ │ + * string or hash. Typically, this is only appropriate for <GET> │ │ │ │ + * requests where the query string will be appended to the url. │ │ │ │ + * Parameter values that are arrays will be │ │ │ │ + * concatenated with a comma (note that this goes against form-encoding) │ │ │ │ + * as is done with <OpenLayers.Util.getParameterString>. │ │ │ │ + * headers - {Object} Object with header:value pairs to be set on │ │ │ │ + * the request. │ │ │ │ + * data - {String | Document} Optional data to send with the request. │ │ │ │ + * Typically, this is only used with <POST> and <PUT> requests. │ │ │ │ + * Make sure to provide the appropriate "Content-Type" header for your │ │ │ │ + * data. For <POST> and <PUT> requests, the content type defaults to │ │ │ │ + * "application-xml". If your data is a different content type, or │ │ │ │ + * if you are using a different HTTP method, set the "Content-Type" │ │ │ │ + * header to match your data type. │ │ │ │ + * callback - {Function} Function to call when request is done. │ │ │ │ + * To determine if the request failed, check request.status (200 │ │ │ │ + * indicates success). │ │ │ │ + * success - {Function} Optional function to call if request status is in │ │ │ │ + * the 200s. This will be called in addition to callback above and │ │ │ │ + * would typically only be used as an alternative. │ │ │ │ + * failure - {Function} Optional function to call if request status is not │ │ │ │ + * in the 200s. This will be called in addition to callback above and │ │ │ │ + * would typically only be used as an alternative. │ │ │ │ + * scope - {Object} If callback is a public method on some object, │ │ │ │ + * set the scope to that object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {XMLHttpRequest} Request object. To abort the request before a response │ │ │ │ + * is received, call abort() on the request object. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + issue: function(config) { │ │ │ │ + // apply default config - proxy host may have changed │ │ │ │ + var defaultConfig = OpenLayers.Util.extend( │ │ │ │ + this.DEFAULT_CONFIG, { │ │ │ │ + proxy: OpenLayers.ProxyHost │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + config = config || {}; │ │ │ │ + config.headers = config.headers || {}; │ │ │ │ + config = OpenLayers.Util.applyDefaults(config, defaultConfig); │ │ │ │ + config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers); │ │ │ │ + // Always set the "X-Requested-With" header to signal that this request │ │ │ │ + // was issued through the XHR-object. Since header keys are case │ │ │ │ + // insensitive and we want to allow overriding of the "X-Requested-With" │ │ │ │ + // header through the user we cannot use applyDefaults, but have to │ │ │ │ + // check manually whether we were called with a "X-Requested-With" │ │ │ │ + // header. │ │ │ │ + var customRequestedWithHeader = false, │ │ │ │ + headerKey; │ │ │ │ + for (headerKey in config.headers) { │ │ │ │ + if (config.headers.hasOwnProperty(headerKey)) { │ │ │ │ + if (headerKey.toLowerCase() === 'x-requested-with') { │ │ │ │ + customRequestedWithHeader = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (customRequestedWithHeader === false) { │ │ │ │ + // we did not have a custom "X-Requested-With" header │ │ │ │ + config.headers['X-Requested-With'] = 'XMLHttpRequest'; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // create request, open, and set headers │ │ │ │ + var request = new OpenLayers.Request.XMLHttpRequest(); │ │ │ │ + var url = OpenLayers.Util.urlAppend(config.url, │ │ │ │ + OpenLayers.Util.getParameterString(config.params || {})); │ │ │ │ + url = OpenLayers.Request.makeSameOrigin(url, config.proxy); │ │ │ │ + request.open( │ │ │ │ + config.method, url, config.async, config.user, config.password │ │ │ │ + ); │ │ │ │ + for (var header in config.headers) { │ │ │ │ + request.setRequestHeader(header, config.headers[header]); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var events = this.events; │ │ │ │ + │ │ │ │ + // we want to execute runCallbacks with "this" as the │ │ │ │ + // execution scope │ │ │ │ + var self = this; │ │ │ │ + │ │ │ │ + request.onreadystatechange = function() { │ │ │ │ + if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) { │ │ │ │ + var proceed = events.triggerEvent( │ │ │ │ + "complete", { │ │ │ │ + request: request, │ │ │ │ + config: config, │ │ │ │ + requestUrl: url │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + if (proceed !== false) { │ │ │ │ + self.runCallbacks({ │ │ │ │ + request: request, │ │ │ │ + config: config, │ │ │ │ + requestUrl: url │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }; │ │ │ │ + │ │ │ │ + // send request (optionally with data) and return │ │ │ │ + // call in a timeout for asynchronous requests so the return is │ │ │ │ + // available before readyState == 4 for cached docs │ │ │ │ + if (config.async === false) { │ │ │ │ + request.send(config.data); │ │ │ │ + } else { │ │ │ │ + window.setTimeout(function() { │ │ │ │ + if (request.readyState !== 0) { // W3C: 0-UNSENT │ │ │ │ + request.send(config.data); │ │ │ │ + } │ │ │ │ + }, 0); │ │ │ │ } │ │ │ │ + return request; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawGeometry │ │ │ │ - * │ │ │ │ - * Draw a geometry. This should only be called from the renderer itself. │ │ │ │ - * Use layer.drawFeature() from outside the renderer. │ │ │ │ - * virtual function │ │ │ │ + /** │ │ │ │ + * Method: runCallbacks │ │ │ │ + * Calls the complete, success and failure callbacks. Application │ │ │ │ + * can listen to the "complete" event, have the listener │ │ │ │ + * display a confirm window and always return false, and │ │ │ │ + * execute OpenLayers.Request.runCallbacks if the user │ │ │ │ + * hits "yes" in the confirm window. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {<String>} │ │ │ │ + * options - {Object} Hash containing request, config and requestUrl keys │ │ │ │ */ │ │ │ │ - drawGeometry: function(geometry, style, featureId) {}, │ │ │ │ + runCallbacks: function(options) { │ │ │ │ + var request = options.request; │ │ │ │ + var config = options.config; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawText │ │ │ │ - * Function for drawing text labels. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * featureId - {String} │ │ │ │ - * style - │ │ │ │ - * location - {<OpenLayers.Geometry.Point>} │ │ │ │ - */ │ │ │ │ - drawText: function(featureId, style, location) {}, │ │ │ │ + // bind callbacks to readyState 4 (done) │ │ │ │ + var complete = (config.scope) ? │ │ │ │ + OpenLayers.Function.bind(config.callback, config.scope) : │ │ │ │ + config.callback; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: removeText │ │ │ │ - * Function for removing text labels. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * featureId - {String} │ │ │ │ - */ │ │ │ │ - removeText: function(featureId) {}, │ │ │ │ + // optional success callback │ │ │ │ + var success; │ │ │ │ + if (config.success) { │ │ │ │ + success = (config.scope) ? │ │ │ │ + OpenLayers.Function.bind(config.success, config.scope) : │ │ │ │ + config.success; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: clear │ │ │ │ - * Clear all vectors from the renderer. │ │ │ │ - * virtual function. │ │ │ │ - */ │ │ │ │ - clear: function() {}, │ │ │ │ + // optional failure callback │ │ │ │ + var failure; │ │ │ │ + if (config.failure) { │ │ │ │ + failure = (config.scope) ? │ │ │ │ + OpenLayers.Function.bind(config.failure, config.scope) : │ │ │ │ + config.failure; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" && │ │ │ │ + request.responseText) { │ │ │ │ + request.status = 200; │ │ │ │ + } │ │ │ │ + complete(request); │ │ │ │ + │ │ │ │ + if (!request.status || (request.status >= 200 && request.status < 300)) { │ │ │ │ + this.events.triggerEvent("success", options); │ │ │ │ + if (success) { │ │ │ │ + success(request); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (request.status && (request.status < 200 || request.status >= 300)) { │ │ │ │ + this.events.triggerEvent("failure", options); │ │ │ │ + if (failure) { │ │ │ │ + failure(request); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getFeatureIdFromEvent │ │ │ │ - * Returns a feature id from an event on the renderer. │ │ │ │ - * How this happens is specific to the renderer. This should be │ │ │ │ - * called from layer.getFeatureFromEvent(). │ │ │ │ - * Virtual function. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {<OpenLayers.Event>} │ │ │ │ + * APIMethod: GET │ │ │ │ + * Send an HTTP GET request. Additional configuration properties are │ │ │ │ + * documented in the <issue> method, with the method property set │ │ │ │ + * to GET. │ │ │ │ * │ │ │ │ + * Parameters: │ │ │ │ + * config - {Object} Object with properties for configuring the request. │ │ │ │ + * See the <issue> method for documentation of allowed properties. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} A feature id or undefined. │ │ │ │ + * {XMLHttpRequest} Request object. │ │ │ │ */ │ │ │ │ - getFeatureIdFromEvent: function(evt) {}, │ │ │ │ + GET: function(config) { │ │ │ │ + config = OpenLayers.Util.extend(config, { │ │ │ │ + method: "GET" │ │ │ │ + }); │ │ │ │ + return OpenLayers.Request.issue(config); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: eraseFeatures │ │ │ │ - * This is called by the layer to erase features │ │ │ │ - * │ │ │ │ + * APIMethod: POST │ │ │ │ + * Send a POST request. Additional configuration properties are │ │ │ │ + * documented in the <issue> method, with the method property set │ │ │ │ + * to POST and "Content-Type" header set to "application/xml". │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + * config - {Object} Object with properties for configuring the request. │ │ │ │ + * See the <issue> method for documentation of allowed properties. The │ │ │ │ + * default "Content-Type" header will be set to "application-xml" if │ │ │ │ + * none is provided. This object is modified and should not be reused. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {XMLHttpRequest} Request object. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + POST: function(config) { │ │ │ │ + config = OpenLayers.Util.extend(config, { │ │ │ │ + method: "POST" │ │ │ │ + }); │ │ │ │ + // set content type to application/xml if it isn't already set │ │ │ │ + config.headers = config.headers ? config.headers : {}; │ │ │ │ + if (!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) { │ │ │ │ + config.headers["Content-Type"] = "application/xml"; │ │ │ │ } │ │ │ │ + return OpenLayers.Request.issue(config); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: eraseGeometry │ │ │ │ - * Remove a geometry from the renderer (by id). │ │ │ │ - * virtual function. │ │ │ │ - * │ │ │ │ + * APIMethod: PUT │ │ │ │ + * Send an HTTP PUT request. Additional configuration properties are │ │ │ │ + * documented in the <issue> method, with the method property set │ │ │ │ + * to PUT and "Content-Type" header set to "application/xml". │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * featureId - {String} │ │ │ │ + * config - {Object} Object with properties for configuring the request. │ │ │ │ + * See the <issue> method for documentation of allowed properties. The │ │ │ │ + * default "Content-Type" header will be set to "application-xml" if │ │ │ │ + * none is provided. This object is modified and should not be reused. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {XMLHttpRequest} Request object. │ │ │ │ */ │ │ │ │ - eraseGeometry: function(geometry, featureId) {}, │ │ │ │ + PUT: function(config) { │ │ │ │ + config = OpenLayers.Util.extend(config, { │ │ │ │ + method: "PUT" │ │ │ │ + }); │ │ │ │ + // set content type to application/xml if it isn't already set │ │ │ │ + config.headers = config.headers ? config.headers : {}; │ │ │ │ + if (!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) { │ │ │ │ + config.headers["Content-Type"] = "application/xml"; │ │ │ │ + } │ │ │ │ + return OpenLayers.Request.issue(config); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ - * │ │ │ │ + * APIMethod: DELETE │ │ │ │ + * Send an HTTP DELETE request. Additional configuration properties are │ │ │ │ + * documented in the <issue> method, with the method property set │ │ │ │ + * to DELETE. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * renderer - {<OpenLayers.Renderer>} target renderer for the moved root │ │ │ │ + * config - {Object} Object with properties for configuring the request. │ │ │ │ + * See the <issue> method for documentation of allowed properties. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {XMLHttpRequest} Request object. │ │ │ │ */ │ │ │ │ - moveRoot: function(renderer) {}, │ │ │ │ + DELETE: function(config) { │ │ │ │ + config = OpenLayers.Util.extend(config, { │ │ │ │ + method: "DELETE" │ │ │ │ + }); │ │ │ │ + return OpenLayers.Request.issue(config); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIMethod: HEAD │ │ │ │ + * Send an HTTP HEAD request. Additional configuration properties are │ │ │ │ + * documented in the <issue> method, with the method property set │ │ │ │ + * to HEAD. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * config - {Object} Object with properties for configuring the request. │ │ │ │ + * See the <issue> method for documentation of allowed properties. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} the id of the output layer. │ │ │ │ + * {XMLHttpRequest} Request object. │ │ │ │ */ │ │ │ │ - getRenderLayerId: function() { │ │ │ │ - return this.container.id; │ │ │ │ + HEAD: function(config) { │ │ │ │ + config = OpenLayers.Util.extend(config, { │ │ │ │ + method: "HEAD" │ │ │ │ + }); │ │ │ │ + return OpenLayers.Request.issue(config); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: applyDefaultSymbolizer │ │ │ │ - * │ │ │ │ + * APIMethod: OPTIONS │ │ │ │ + * Send an HTTP OPTIONS request. Additional configuration properties are │ │ │ │ + * documented in the <issue> method, with the method property set │ │ │ │ + * to OPTIONS. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * symbolizer - {Object} │ │ │ │ + * config - {Object} Object with properties for configuring the request. │ │ │ │ + * See the <issue> method for documentation of allowed properties. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} │ │ │ │ + * {XMLHttpRequest} Request object. │ │ │ │ */ │ │ │ │ - applyDefaultSymbolizer: function(symbolizer) { │ │ │ │ - var result = OpenLayers.Util.extend({}, │ │ │ │ - OpenLayers.Renderer.defaultSymbolizer); │ │ │ │ - if (symbolizer.stroke === false) { │ │ │ │ - delete result.strokeWidth; │ │ │ │ - delete result.strokeColor; │ │ │ │ - } │ │ │ │ - if (symbolizer.fill === false) { │ │ │ │ - delete result.fillColor; │ │ │ │ - } │ │ │ │ - OpenLayers.Util.extend(result, symbolizer); │ │ │ │ - return result; │ │ │ │ - }, │ │ │ │ + OPTIONS: function(config) { │ │ │ │ + config = OpenLayers.Util.extend(config, { │ │ │ │ + method: "OPTIONS" │ │ │ │ + }); │ │ │ │ + return OpenLayers.Request.issue(config); │ │ │ │ + } │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer" │ │ │ │ }); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Renderer.defaultSymbolizer │ │ │ │ - * {Object} Properties from this symbolizer will be applied to symbolizers │ │ │ │ - * with missing properties. This can also be used to set a global │ │ │ │ - * symbolizer default in OpenLayers. To be SLD 1.x compliant, add the │ │ │ │ - * following code before rendering any vector features: │ │ │ │ - * (code) │ │ │ │ - * OpenLayers.Renderer.defaultSymbolizer = { │ │ │ │ - * fillColor: "#808080", │ │ │ │ - * fillOpacity: 1, │ │ │ │ - * strokeColor: "#000000", │ │ │ │ - * strokeOpacity: 1, │ │ │ │ - * strokeWidth: 1, │ │ │ │ - * pointRadius: 3, │ │ │ │ - * graphicName: "square" │ │ │ │ - * }; │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.defaultSymbolizer = { │ │ │ │ - fillColor: "#000000", │ │ │ │ - strokeColor: "#000000", │ │ │ │ - strokeWidth: 2, │ │ │ │ - fillOpacity: 1, │ │ │ │ - strokeOpacity: 1, │ │ │ │ - pointRadius: 0, │ │ │ │ - labelAlign: 'cm' │ │ │ │ -}; │ │ │ │ - │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Renderer.symbol │ │ │ │ - * Coordinate arrays for well known (named) symbols. │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.symbol = { │ │ │ │ - "star": [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301, │ │ │ │ - 303, 215, 231, 161, 321, 161, 350, 75 │ │ │ │ - ], │ │ │ │ - "cross": [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4, │ │ │ │ - 4, 0 │ │ │ │ - ], │ │ │ │ - "x": [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0], │ │ │ │ - "square": [0, 0, 0, 1, 1, 1, 1, 0, 0, 0], │ │ │ │ - "triangle": [0, 10, 10, 10, 5, 0, 0, 10] │ │ │ │ -}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/Vector.js │ │ │ │ + OpenLayers/Request/XMLHttpRequest.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer.js │ │ │ │ - * @requires OpenLayers/Renderer.js │ │ │ │ - * @requires OpenLayers/StyleMap.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ - * @requires OpenLayers/Console.js │ │ │ │ - * @requires OpenLayers/Lang.js │ │ │ │ - */ │ │ │ │ +// XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com) │ │ │ │ +// │ │ │ │ +// Licensed under the Apache License, Version 2.0 (the "License"); │ │ │ │ +// you may not use this file except in compliance with the License. │ │ │ │ +// You may obtain a copy of the License at │ │ │ │ +// │ │ │ │ +// http://www.apache.org/licenses/LICENSE-2.0 │ │ │ │ +// │ │ │ │ +// Unless required by applicable law or agreed to in writing, software │ │ │ │ +// distributed under the License is distributed on an "AS IS" BASIS, │ │ │ │ +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ │ │ │ +// See the License for the specific language governing permissions and │ │ │ │ +// limitations under the License. │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.Vector │ │ │ │ - * Instances of OpenLayers.Layer.Vector are used to render vector data from │ │ │ │ - * a variety of sources. Create a new vector layer with the │ │ │ │ - * <OpenLayers.Layer.Vector> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer> │ │ │ │ + * @requires OpenLayers/Request.js │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * layer.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Listeners will be called with a reference to an event object. The │ │ │ │ - * properties of this event depends on exactly what happened. │ │ │ │ - * │ │ │ │ - * All event objects have at least the following properties: │ │ │ │ - * object - {Object} A reference to layer.events.object. │ │ │ │ - * element - {DOMElement} A reference to layer.events.element. │ │ │ │ - * │ │ │ │ - * Supported map event types (in addition to those from <OpenLayers.Layer.events>): │ │ │ │ - * beforefeatureadded - Triggered before a feature is added. Listeners │ │ │ │ - * will receive an object with a *feature* property referencing the │ │ │ │ - * feature to be added. To stop the feature from being added, a │ │ │ │ - * listener should return false. │ │ │ │ - * beforefeaturesadded - Triggered before an array of features is added. │ │ │ │ - * Listeners will receive an object with a *features* property │ │ │ │ - * referencing the feature to be added. To stop the features from │ │ │ │ - * being added, a listener should return false. │ │ │ │ - * featureadded - Triggered after a feature is added. The event │ │ │ │ - * object passed to listeners will have a *feature* property with a │ │ │ │ - * reference to the added feature. │ │ │ │ - * featuresadded - Triggered after features are added. The event │ │ │ │ - * object passed to listeners will have a *features* property with a │ │ │ │ - * reference to an array of added features. │ │ │ │ - * beforefeatureremoved - Triggered before a feature is removed. Listeners │ │ │ │ - * will receive an object with a *feature* property referencing the │ │ │ │ - * feature to be removed. │ │ │ │ - * beforefeaturesremoved - Triggered before multiple features are removed. │ │ │ │ - * Listeners will receive an object with a *features* property │ │ │ │ - * referencing the features to be removed. │ │ │ │ - * featureremoved - Triggerd after a feature is removed. The event │ │ │ │ - * object passed to listeners will have a *feature* property with a │ │ │ │ - * reference to the removed feature. │ │ │ │ - * featuresremoved - Triggered after features are removed. The event │ │ │ │ - * object passed to listeners will have a *features* property with a │ │ │ │ - * reference to an array of removed features. │ │ │ │ - * beforefeatureselected - Triggered before a feature is selected. Listeners │ │ │ │ - * will receive an object with a *feature* property referencing the │ │ │ │ - * feature to be selected. To stop the feature from being selectd, a │ │ │ │ - * listener should return false. │ │ │ │ - * featureselected - Triggered after a feature is selected. Listeners │ │ │ │ - * will receive an object with a *feature* property referencing the │ │ │ │ - * selected feature. │ │ │ │ - * featureunselected - Triggered after a feature is unselected. │ │ │ │ - * Listeners will receive an object with a *feature* property │ │ │ │ - * referencing the unselected feature. │ │ │ │ - * beforefeaturemodified - Triggered when a feature is selected to │ │ │ │ - * be modified. Listeners will receive an object with a *feature* │ │ │ │ - * property referencing the selected feature. │ │ │ │ - * featuremodified - Triggered when a feature has been modified. │ │ │ │ - * Listeners will receive an object with a *feature* property referencing │ │ │ │ - * the modified feature. │ │ │ │ - * afterfeaturemodified - Triggered when a feature is finished being modified. │ │ │ │ - * Listeners will receive an object with a *feature* property referencing │ │ │ │ - * the modified feature. │ │ │ │ - * vertexmodified - Triggered when a vertex within any feature geometry │ │ │ │ - * has been modified. Listeners will receive an object with a │ │ │ │ - * *feature* property referencing the modified feature, a *vertex* │ │ │ │ - * property referencing the vertex modified (always a point geometry), │ │ │ │ - * and a *pixel* property referencing the pixel location of the │ │ │ │ - * modification. │ │ │ │ - * vertexremoved - Triggered when a vertex within any feature geometry │ │ │ │ - * has been deleted. Listeners will receive an object with a │ │ │ │ - * *feature* property referencing the modified feature, a *vertex* │ │ │ │ - * property referencing the vertex modified (always a point geometry), │ │ │ │ - * and a *pixel* property referencing the pixel location of the │ │ │ │ - * removal. │ │ │ │ - * sketchstarted - Triggered when a feature sketch bound for this layer │ │ │ │ - * is started. Listeners will receive an object with a *feature* │ │ │ │ - * property referencing the new sketch feature and a *vertex* property │ │ │ │ - * referencing the creation point. │ │ │ │ - * sketchmodified - Triggered when a feature sketch bound for this layer │ │ │ │ - * is modified. Listeners will receive an object with a *vertex* │ │ │ │ - * property referencing the modified vertex and a *feature* property │ │ │ │ - * referencing the sketch feature. │ │ │ │ - * sketchcomplete - Triggered when a feature sketch bound for this layer │ │ │ │ - * is complete. Listeners will receive an object with a *feature* │ │ │ │ - * property referencing the sketch feature. By returning false, a │ │ │ │ - * listener can stop the sketch feature from being added to the layer. │ │ │ │ - * refresh - Triggered when something wants a strategy to ask the protocol │ │ │ │ - * for a new set of features. │ │ │ │ - */ │ │ │ │ +(function() { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} The layer is a base layer. Default is false. Set this property │ │ │ │ - * in the layer options. │ │ │ │ - */ │ │ │ │ - isBaseLayer: false, │ │ │ │ + // Save reference to earlier defined object implementation (if any) │ │ │ │ + var oXMLHttpRequest = window.XMLHttpRequest; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: isFixed │ │ │ │ - * {Boolean} Whether the layer remains in one place while dragging the │ │ │ │ - * map. Note that setting this to true will move the layer to the bottom │ │ │ │ - * of the layer stack. │ │ │ │ - */ │ │ │ │ - isFixed: false, │ │ │ │ + // Define on browser type │ │ │ │ + var bGecko = !!window.controllers, │ │ │ │ + bIE = window.document.all && !window.opera, │ │ │ │ + bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: features │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ - */ │ │ │ │ - features: null, │ │ │ │ + // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()" │ │ │ │ + function fXMLHttpRequest() { │ │ │ │ + this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP"); │ │ │ │ + this._listeners = []; │ │ │ │ + }; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: filter │ │ │ │ - * {<OpenLayers.Filter>} The filter set in this layer, │ │ │ │ - * a strategy launching read requests can combined │ │ │ │ - * this filter with its own filter. │ │ │ │ - */ │ │ │ │ - filter: null, │ │ │ │ + // Constructor │ │ │ │ + function cXMLHttpRequest() { │ │ │ │ + return new fXMLHttpRequest; │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype = fXMLHttpRequest.prototype; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: selectedFeatures │ │ │ │ - * {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ - */ │ │ │ │ - selectedFeatures: null, │ │ │ │ + // BUGFIX: Firefox with Firebug installed would break pages if not executed │ │ │ │ + if (bGecko && oXMLHttpRequest.wrapped) │ │ │ │ + cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: unrenderedFeatures │ │ │ │ - * {Object} hash of features, keyed by feature.id, that the renderer │ │ │ │ - * failed to draw │ │ │ │ - */ │ │ │ │ - unrenderedFeatures: null, │ │ │ │ + // Constants │ │ │ │ + cXMLHttpRequest.UNSENT = 0; │ │ │ │ + cXMLHttpRequest.OPENED = 1; │ │ │ │ + cXMLHttpRequest.HEADERS_RECEIVED = 2; │ │ │ │ + cXMLHttpRequest.LOADING = 3; │ │ │ │ + cXMLHttpRequest.DONE = 4; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: reportError │ │ │ │ - * {Boolean} report friendly error message when loading of renderer │ │ │ │ - * fails. │ │ │ │ - */ │ │ │ │ - reportError: true, │ │ │ │ + // Public Properties │ │ │ │ + cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ + cXMLHttpRequest.prototype.responseText = ''; │ │ │ │ + cXMLHttpRequest.prototype.responseXML = null; │ │ │ │ + cXMLHttpRequest.prototype.status = 0; │ │ │ │ + cXMLHttpRequest.prototype.statusText = ''; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: style │ │ │ │ - * {Object} Default style for the layer │ │ │ │ - */ │ │ │ │ - style: null, │ │ │ │ + // Priority proposal │ │ │ │ + cXMLHttpRequest.prototype.priority = "NORMAL"; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: styleMap │ │ │ │ - * {<OpenLayers.StyleMap>} │ │ │ │ - */ │ │ │ │ - styleMap: null, │ │ │ │ + // Instance-level Events Handlers │ │ │ │ + cXMLHttpRequest.prototype.onreadystatechange = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: strategies │ │ │ │ - * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer. │ │ │ │ - */ │ │ │ │ - strategies: null, │ │ │ │ + // Class-level Events Handlers │ │ │ │ + cXMLHttpRequest.onreadystatechange = null; │ │ │ │ + cXMLHttpRequest.onopen = null; │ │ │ │ + cXMLHttpRequest.onsend = null; │ │ │ │ + cXMLHttpRequest.onabort = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: protocol │ │ │ │ - * {<OpenLayers.Protocol>} Optional protocol for the layer. │ │ │ │ - */ │ │ │ │ - protocol: null, │ │ │ │ + // Public Methods │ │ │ │ + cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) { │ │ │ │ + // Delete headers, required when object is reused │ │ │ │ + delete this._headers; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: renderers │ │ │ │ - * {Array(String)} List of supported Renderer classes. Add to this list to │ │ │ │ - * add support for additional renderers. This list is ordered: │ │ │ │ - * the first renderer which returns true for the 'supported()' │ │ │ │ - * method will be used, if not defined in the 'renderer' option. │ │ │ │ - */ │ │ │ │ - renderers: ['SVG', 'VML', 'Canvas'], │ │ │ │ + // When bAsync parameter value is omitted, use true as default │ │ │ │ + if (arguments.length < 3) │ │ │ │ + bAsync = true; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: renderer │ │ │ │ - * {<OpenLayers.Renderer>} │ │ │ │ - */ │ │ │ │ - renderer: null, │ │ │ │ + // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests │ │ │ │ + this._async = bAsync; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: rendererOptions │ │ │ │ - * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for │ │ │ │ - * supported options. │ │ │ │ - */ │ │ │ │ - rendererOptions: null, │ │ │ │ + // Set the onreadystatechange handler │ │ │ │ + var oRequest = this, │ │ │ │ + nState = this.readyState, │ │ │ │ + fOnUnload; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: geometryType │ │ │ │ - * {String} geometryType allows you to limit the types of geometries this │ │ │ │ - * layer supports. This should be set to something like │ │ │ │ - * "OpenLayers.Geometry.Point" to limit types. │ │ │ │ - */ │ │ │ │ - geometryType: null, │ │ │ │ + // BUGFIX: IE - memory leak on page unload (inter-page leak) │ │ │ │ + if (bIE && bAsync) { │ │ │ │ + fOnUnload = function() { │ │ │ │ + if (nState != cXMLHttpRequest.DONE) { │ │ │ │ + fCleanTransport(oRequest); │ │ │ │ + // Safe to abort here since onreadystatechange handler removed │ │ │ │ + oRequest.abort(); │ │ │ │ + } │ │ │ │ + }; │ │ │ │ + window.attachEvent("onunload", fOnUnload); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: drawn │ │ │ │ - * {Boolean} Whether the Vector Layer features have been drawn yet. │ │ │ │ - */ │ │ │ │ - drawn: false, │ │ │ │ + // Add method sniffer │ │ │ │ + if (cXMLHttpRequest.onopen) │ │ │ │ + cXMLHttpRequest.onopen.apply(this, arguments); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: ratio │ │ │ │ - * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map. │ │ │ │ - */ │ │ │ │ - ratio: 1, │ │ │ │ + if (arguments.length > 4) │ │ │ │ + this._object.open(sMethod, sUrl, bAsync, sUser, sPassword); │ │ │ │ + else │ │ │ │ + if (arguments.length > 3) │ │ │ │ + this._object.open(sMethod, sUrl, bAsync, sUser); │ │ │ │ + else │ │ │ │ + this._object.open(sMethod, sUrl, bAsync); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Layer.Vector │ │ │ │ - * Create a new vector layer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} A name for the layer │ │ │ │ - * options - {Object} Optional object with non-default properties to set on │ │ │ │ - * the layer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.Vector>} A new vector layer │ │ │ │ - */ │ │ │ │ - initialize: function(name, options) { │ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ + this.readyState = cXMLHttpRequest.OPENED; │ │ │ │ + fReadyStateChange(this); │ │ │ │ │ │ │ │ - // allow user-set renderer, otherwise assign one │ │ │ │ - if (!this.renderer || !this.renderer.supported()) { │ │ │ │ - this.assignRenderer(); │ │ │ │ - } │ │ │ │ + this._object.onreadystatechange = function() { │ │ │ │ + if (bGecko && !bAsync) │ │ │ │ + return; │ │ │ │ │ │ │ │ - // if no valid renderer found, display error │ │ │ │ - if (!this.renderer || !this.renderer.supported()) { │ │ │ │ - this.renderer = null; │ │ │ │ - this.displayError(); │ │ │ │ - } │ │ │ │ + // Synchronize state │ │ │ │ + oRequest.readyState = oRequest._object.readyState; │ │ │ │ │ │ │ │ - if (!this.styleMap) { │ │ │ │ - this.styleMap = new OpenLayers.StyleMap(); │ │ │ │ - } │ │ │ │ + // │ │ │ │ + fSynchronizeValues(oRequest); │ │ │ │ │ │ │ │ - this.features = []; │ │ │ │ - this.selectedFeatures = []; │ │ │ │ - this.unrenderedFeatures = {}; │ │ │ │ + // BUGFIX: Firefox fires unnecessary DONE when aborting │ │ │ │ + if (oRequest._aborted) { │ │ │ │ + // Reset readyState to UNSENT │ │ │ │ + oRequest.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ │ │ │ │ - // Allow for custom layer behavior │ │ │ │ - if (this.strategies) { │ │ │ │ - for (var i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - this.strategies[i].setLayer(this); │ │ │ │ + // Return now │ │ │ │ + return; │ │ │ │ } │ │ │ │ - } │ │ │ │ │ │ │ │ - }, │ │ │ │ + if (oRequest.readyState == cXMLHttpRequest.DONE) { │ │ │ │ + // Free up queue │ │ │ │ + delete oRequest._data; │ │ │ │ + /* if (bAsync) │ │ │ │ + fQueue_remove(oRequest);*/ │ │ │ │ + // │ │ │ │ + fCleanTransport(oRequest); │ │ │ │ + // Uncomment this block if you need a fix for IE cache │ │ │ │ + /* │ │ │ │ + // BUGFIX: IE - cache issue │ │ │ │ + if (!oRequest._object.getResponseHeader("Date")) { │ │ │ │ + // Save object to cache │ │ │ │ + oRequest._cached = oRequest._object; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Destroy this layer │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.strategies) { │ │ │ │ - var strategy, i, len; │ │ │ │ - for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - strategy = this.strategies[i]; │ │ │ │ - if (strategy.autoDestroy) { │ │ │ │ - strategy.destroy(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.strategies = null; │ │ │ │ - } │ │ │ │ - if (this.protocol) { │ │ │ │ - if (this.protocol.autoDestroy) { │ │ │ │ - this.protocol.destroy(); │ │ │ │ - } │ │ │ │ - this.protocol = null; │ │ │ │ - } │ │ │ │ - this.destroyFeatures(); │ │ │ │ - this.features = null; │ │ │ │ - this.selectedFeatures = null; │ │ │ │ - this.unrenderedFeatures = null; │ │ │ │ - if (this.renderer) { │ │ │ │ - this.renderer.destroy(); │ │ │ │ - } │ │ │ │ - this.renderer = null; │ │ │ │ - this.geometryType = null; │ │ │ │ - this.drawn = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + // Instantiate a new transport object │ │ │ │ + cXMLHttpRequest.call(oRequest); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: clone │ │ │ │ - * Create a clone of this layer. │ │ │ │ - * │ │ │ │ - * Note: Features of the layer are also cloned. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.Vector>} An exact clone of this layer │ │ │ │ - */ │ │ │ │ - clone: function(obj) { │ │ │ │ + // Re-send request │ │ │ │ + if (sUser) { │ │ │ │ + if (sPassword) │ │ │ │ + oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword); │ │ │ │ + else │ │ │ │ + oRequest._object.open(sMethod, sUrl, bAsync, sUser); │ │ │ │ + } │ │ │ │ + else │ │ │ │ + oRequest._object.open(sMethod, sUrl, bAsync); │ │ │ │ + oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0)); │ │ │ │ + // Copy headers set │ │ │ │ + if (oRequest._headers) │ │ │ │ + for (var sHeader in oRequest._headers) │ │ │ │ + if (typeof oRequest._headers[sHeader] == "string") // Some frameworks prototype objects with functions │ │ │ │ + oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]); │ │ │ │ │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Vector(this.name, this.getOptions()); │ │ │ │ - } │ │ │ │ + oRequest._object.onreadystatechange = function() { │ │ │ │ + // Synchronize state │ │ │ │ + oRequest.readyState = oRequest._object.readyState; │ │ │ │ │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ + if (oRequest._aborted) { │ │ │ │ + // │ │ │ │ + oRequest.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - var features = this.features; │ │ │ │ - var len = features.length; │ │ │ │ - var clonedFeatures = new Array(len); │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ - clonedFeatures[i] = features[i].clone(); │ │ │ │ - } │ │ │ │ - obj.features = clonedFeatures; │ │ │ │ + // Return │ │ │ │ + return; │ │ │ │ + } │ │ │ │ │ │ │ │ - return obj; │ │ │ │ - }, │ │ │ │ + if (oRequest.readyState == cXMLHttpRequest.DONE) { │ │ │ │ + // Clean Object │ │ │ │ + fCleanTransport(oRequest); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: refresh │ │ │ │ - * Ask the layer to request features again and redraw them. Triggers │ │ │ │ - * the refresh event if the layer is in range and visible. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {Object} Optional object with properties for any listener of │ │ │ │ - * the refresh event. │ │ │ │ - */ │ │ │ │ - refresh: function(obj) { │ │ │ │ - if (this.calculateInRange() && this.visibility) { │ │ │ │ - this.events.triggerEvent("refresh", obj); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + // get cached request │ │ │ │ + if (oRequest.status == 304) │ │ │ │ + oRequest._object = oRequest._cached; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: assignRenderer │ │ │ │ - * Iterates through the available renderer implementations and selects │ │ │ │ - * and assigns the first one whose "supported()" function returns true. │ │ │ │ - */ │ │ │ │ - assignRenderer: function() { │ │ │ │ - for (var i = 0, len = this.renderers.length; i < len; i++) { │ │ │ │ - var rendererClass = this.renderers[i]; │ │ │ │ - var renderer = (typeof rendererClass == "function") ? │ │ │ │ - rendererClass : │ │ │ │ - OpenLayers.Renderer[rendererClass]; │ │ │ │ - if (renderer && renderer.prototype.supported()) { │ │ │ │ - this.renderer = new renderer(this.div, this.rendererOptions); │ │ │ │ - break; │ │ │ │ + // │ │ │ │ + delete oRequest._cached; │ │ │ │ + │ │ │ │ + // │ │ │ │ + fSynchronizeValues(oRequest); │ │ │ │ + │ │ │ │ + // │ │ │ │ + fReadyStateChange(oRequest); │ │ │ │ + │ │ │ │ + // BUGFIX: IE - memory leak in interrupted │ │ │ │ + if (bIE && bAsync) │ │ │ │ + window.detachEvent("onunload", fOnUnload); │ │ │ │ + } │ │ │ │ + }; │ │ │ │ + oRequest._object.send(null); │ │ │ │ + │ │ │ │ + // Return now - wait until re-sent request is finished │ │ │ │ + return; │ │ │ │ + }; │ │ │ │ + */ │ │ │ │ + // BUGFIX: IE - memory leak in interrupted │ │ │ │ + if (bIE && bAsync) │ │ │ │ + window.detachEvent("onunload", fOnUnload); │ │ │ │ } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: displayError │ │ │ │ - * Let the user know their browser isn't supported. │ │ │ │ - */ │ │ │ │ - displayError: function() { │ │ │ │ - if (this.reportError) { │ │ │ │ - OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", { │ │ │ │ - renderers: this.renderers.join('\n') │ │ │ │ - })); │ │ │ │ + // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice │ │ │ │ + if (nState != oRequest.readyState) │ │ │ │ + fReadyStateChange(oRequest); │ │ │ │ + │ │ │ │ + nState = oRequest.readyState; │ │ │ │ } │ │ │ │ - }, │ │ │ │ + }; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * The layer has been added to the map. │ │ │ │ - * │ │ │ │ - * If there is no renderer set, the layer can't be used. Remove it. │ │ │ │ - * Otherwise, give the renderer a reference to the map and set its size. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ + function fXMLHttpRequest_send(oRequest) { │ │ │ │ + oRequest._object.send(oRequest._data); │ │ │ │ │ │ │ │ - if (!this.renderer) { │ │ │ │ - this.map.removeLayer(this); │ │ │ │ - } else { │ │ │ │ - this.renderer.map = this.map; │ │ │ │ + // BUGFIX: Gecko - missing readystatechange calls in synchronous requests │ │ │ │ + if (bGecko && !oRequest._async) { │ │ │ │ + oRequest.readyState = cXMLHttpRequest.OPENED; │ │ │ │ │ │ │ │ - var newSize = this.map.getSize(); │ │ │ │ - newSize.w = newSize.w * this.ratio; │ │ │ │ - newSize.h = newSize.h * this.ratio; │ │ │ │ - this.renderer.setSize(newSize); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + // Synchronize state │ │ │ │ + fSynchronizeValues(oRequest); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: afterAdd │ │ │ │ - * Called at the end of the map.addLayer sequence. At this point, the map │ │ │ │ - * will have a base layer. Any autoActivate strategies will be │ │ │ │ - * activated here. │ │ │ │ - */ │ │ │ │ - afterAdd: function() { │ │ │ │ - if (this.strategies) { │ │ │ │ - var strategy, i, len; │ │ │ │ - for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - strategy = this.strategies[i]; │ │ │ │ - if (strategy.autoActivate) { │ │ │ │ - strategy.activate(); │ │ │ │ - } │ │ │ │ + // Simulate missing states │ │ │ │ + while (oRequest.readyState < cXMLHttpRequest.DONE) { │ │ │ │ + oRequest.readyState++; │ │ │ │ + fReadyStateChange(oRequest); │ │ │ │ + // Check if we are aborted │ │ │ │ + if (oRequest._aborted) │ │ │ │ + return; │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.send = function(vData) { │ │ │ │ + // Add method sniffer │ │ │ │ + if (cXMLHttpRequest.onsend) │ │ │ │ + cXMLHttpRequest.onsend.apply(this, arguments); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: removeMap │ │ │ │ - * The layer has been removed from the map. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - removeMap: function(map) { │ │ │ │ - this.drawn = false; │ │ │ │ - if (this.strategies) { │ │ │ │ - var strategy, i, len; │ │ │ │ - for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - strategy = this.strategies[i]; │ │ │ │ - if (strategy.autoActivate) { │ │ │ │ - strategy.deactivate(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + if (!arguments.length) │ │ │ │ + vData = null; │ │ │ │ + │ │ │ │ + // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required │ │ │ │ + // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent │ │ │ │ + // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard) │ │ │ │ + if (vData && vData.nodeType) { │ │ │ │ + vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml; │ │ │ │ + if (!this._headers["Content-Type"]) │ │ │ │ + this._object.setRequestHeader("Content-Type", "application/xml"); │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: onMapResize │ │ │ │ - * Notify the renderer of the change in size. │ │ │ │ - * │ │ │ │ - */ │ │ │ │ - onMapResize: function() { │ │ │ │ - OpenLayers.Layer.prototype.onMapResize.apply(this, arguments); │ │ │ │ + this._data = vData; │ │ │ │ + /* │ │ │ │ + // Add to queue │ │ │ │ + if (this._async) │ │ │ │ + fQueue_add(this); │ │ │ │ + else*/ │ │ │ │ + fXMLHttpRequest_send(this); │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.abort = function() { │ │ │ │ + // Add method sniffer │ │ │ │ + if (cXMLHttpRequest.onabort) │ │ │ │ + cXMLHttpRequest.onabort.apply(this, arguments); │ │ │ │ │ │ │ │ - var newSize = this.map.getSize(); │ │ │ │ - newSize.w = newSize.w * this.ratio; │ │ │ │ - newSize.h = newSize.h * this.ratio; │ │ │ │ - this.renderer.setSize(newSize); │ │ │ │ - }, │ │ │ │ + // BUGFIX: Gecko - unnecessary DONE when aborting │ │ │ │ + if (this.readyState > cXMLHttpRequest.UNSENT) │ │ │ │ + this._aborted = true; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * Reset the vector layer's div so that it once again is lined up with │ │ │ │ - * the map. Notify the renderer of the change of extent, and in the │ │ │ │ - * case of a change of zoom level (resolution), have the │ │ │ │ - * renderer redraw features. │ │ │ │ - * │ │ │ │ - * If the layer has not yet been drawn, cycle through the layer's │ │ │ │ - * features and draw each one. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * zoomChanged - {Boolean} │ │ │ │ - * dragging - {Boolean} │ │ │ │ - */ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ + this._object.abort(); │ │ │ │ │ │ │ │ - var coordSysUnchanged = true; │ │ │ │ - if (!dragging) { │ │ │ │ - this.renderer.root.style.visibility = 'hidden'; │ │ │ │ + // BUGFIX: IE - memory leak │ │ │ │ + fCleanTransport(this); │ │ │ │ │ │ │ │ - var viewSize = this.map.getSize(), │ │ │ │ - viewWidth = viewSize.w, │ │ │ │ - viewHeight = viewSize.h, │ │ │ │ - offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2, │ │ │ │ - offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2; │ │ │ │ - offsetLeft += this.map.layerContainerOriginPx.x; │ │ │ │ - offsetLeft = -Math.round(offsetLeft); │ │ │ │ - offsetTop += this.map.layerContainerOriginPx.y; │ │ │ │ - offsetTop = -Math.round(offsetTop); │ │ │ │ + this.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ │ │ │ │ - this.div.style.left = offsetLeft + 'px'; │ │ │ │ - this.div.style.top = offsetTop + 'px'; │ │ │ │ + delete this._data; │ │ │ │ + /* if (this._async) │ │ │ │ + fQueue_remove(this);*/ │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.getAllResponseHeaders = function() { │ │ │ │ + return this._object.getAllResponseHeaders(); │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.getResponseHeader = function(sName) { │ │ │ │ + return this._object.getResponseHeader(sName); │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) { │ │ │ │ + // BUGFIX: IE - cache issue │ │ │ │ + if (!this._headers) │ │ │ │ + this._headers = {}; │ │ │ │ + this._headers[sName] = sValue; │ │ │ │ │ │ │ │ - var extent = this.map.getExtent().scale(this.ratio); │ │ │ │ - coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged); │ │ │ │ + return this._object.setRequestHeader(sName, sValue); │ │ │ │ + }; │ │ │ │ │ │ │ │ - this.renderer.root.style.visibility = 'visible'; │ │ │ │ + // EventTarget interface implementation │ │ │ │ + cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) { │ │ │ │ + for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) │ │ │ │ + if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) │ │ │ │ + return; │ │ │ │ + // Add listener │ │ │ │ + this._listeners.push([sName, fHandler, bUseCapture]); │ │ │ │ + }; │ │ │ │ │ │ │ │ - // Force a reflow on gecko based browsers to prevent jump/flicker. │ │ │ │ - // This seems to happen on only certain configurations; it was originally │ │ │ │ - // noticed in FF 2.0 and Linux. │ │ │ │ - if (OpenLayers.IS_GECKO === true) { │ │ │ │ - this.div.scrollLeft = this.div.scrollLeft; │ │ │ │ - } │ │ │ │ + cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) { │ │ │ │ + for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) │ │ │ │ + if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) │ │ │ │ + break; │ │ │ │ + // Remove listener │ │ │ │ + if (oListener) │ │ │ │ + this._listeners.splice(nIndex, 1); │ │ │ │ + }; │ │ │ │ │ │ │ │ - if (!zoomChanged && coordSysUnchanged) { │ │ │ │ - for (var i in this.unrenderedFeatures) { │ │ │ │ - var feature = this.unrenderedFeatures[i]; │ │ │ │ - this.drawFeature(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (!this.drawn || zoomChanged || !coordSysUnchanged) { │ │ │ │ - this.drawn = true; │ │ │ │ - var feature; │ │ │ │ - for (var i = 0, len = this.features.length; i < len; i++) { │ │ │ │ - this.renderer.locked = (i !== (len - 1)); │ │ │ │ - feature = this.features[i]; │ │ │ │ - this.drawFeature(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) { │ │ │ │ + var oEventPseudo = { │ │ │ │ + 'type': oEvent.type, │ │ │ │ + 'target': this, │ │ │ │ + 'currentTarget': this, │ │ │ │ + 'eventPhase': 2, │ │ │ │ + 'bubbles': oEvent.bubbles, │ │ │ │ + 'cancelable': oEvent.cancelable, │ │ │ │ + 'timeStamp': oEvent.timeStamp, │ │ │ │ + 'stopPropagation': function() {}, // There is no flow │ │ │ │ + 'preventDefault': function() {}, // There is no default action │ │ │ │ + 'initEvent': function() {} // Original event object should be initialized │ │ │ │ + }; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: display │ │ │ │ - * Hide or show the Layer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * display - {Boolean} │ │ │ │ - */ │ │ │ │ - display: function(display) { │ │ │ │ - OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ - // we need to set the display style of the root in case it is attached │ │ │ │ - // to a foreign layer │ │ │ │ - var currentDisplay = this.div.style.display; │ │ │ │ - if (currentDisplay != this.renderer.root.style.display) { │ │ │ │ - this.renderer.root.style.display = currentDisplay; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + // Execute onreadystatechange │ │ │ │ + if (oEventPseudo.type == "readystatechange" && this.onreadystatechange) │ │ │ │ + (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: addFeatures │ │ │ │ - * Add Features to the layer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ - addFeatures: function(features, options) { │ │ │ │ - if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ - features = [features]; │ │ │ │ - } │ │ │ │ + // Execute listeners │ │ │ │ + for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) │ │ │ │ + if (oListener[0] == oEventPseudo.type && !oListener[2]) │ │ │ │ + (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]); │ │ │ │ + }; │ │ │ │ │ │ │ │ - var notify = !options || !options.silent; │ │ │ │ - if (notify) { │ │ │ │ - var event = { │ │ │ │ - features: features │ │ │ │ - }; │ │ │ │ - var ret = this.events.triggerEvent("beforefeaturesadded", event); │ │ │ │ - if (ret === false) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - features = event.features; │ │ │ │ - } │ │ │ │ + // │ │ │ │ + cXMLHttpRequest.prototype.toString = function() { │ │ │ │ + return '[' + "object" + ' ' + "XMLHttpRequest" + ']'; │ │ │ │ + }; │ │ │ │ │ │ │ │ - // Track successfully added features for featuresadded event, since │ │ │ │ - // beforefeatureadded can veto single features. │ │ │ │ - var featuresAdded = []; │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - if (i != (features.length - 1)) { │ │ │ │ - this.renderer.locked = true; │ │ │ │ - } else { │ │ │ │ - this.renderer.locked = false; │ │ │ │ - } │ │ │ │ - var feature = features[i]; │ │ │ │ + cXMLHttpRequest.toString = function() { │ │ │ │ + return '[' + "XMLHttpRequest" + ']'; │ │ │ │ + }; │ │ │ │ │ │ │ │ - if (this.geometryType && │ │ │ │ - !(feature.geometry instanceof this.geometryType)) { │ │ │ │ - throw new TypeError('addFeatures: component should be an ' + │ │ │ │ - this.geometryType.prototype.CLASS_NAME); │ │ │ │ - } │ │ │ │ + // Helper function │ │ │ │ + function fReadyStateChange(oRequest) { │ │ │ │ + // Sniffing code │ │ │ │ + if (cXMLHttpRequest.onreadystatechange) │ │ │ │ + cXMLHttpRequest.onreadystatechange.apply(oRequest); │ │ │ │ │ │ │ │ - //give feature reference to its layer │ │ │ │ - feature.layer = this; │ │ │ │ + // Fake event │ │ │ │ + oRequest.dispatchEvent({ │ │ │ │ + 'type': "readystatechange", │ │ │ │ + 'bubbles': false, │ │ │ │ + 'cancelable': false, │ │ │ │ + 'timeStamp': new Date + 0 │ │ │ │ + }); │ │ │ │ + }; │ │ │ │ │ │ │ │ - if (!feature.style && this.style) { │ │ │ │ - feature.style = OpenLayers.Util.extend({}, this.style); │ │ │ │ - } │ │ │ │ + function fGetDocument(oRequest) { │ │ │ │ + var oDocument = oRequest.responseXML, │ │ │ │ + sResponse = oRequest.responseText; │ │ │ │ + // Try parsing responseText │ │ │ │ + if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) { │ │ │ │ + oDocument = new window.ActiveXObject("Microsoft.XMLDOM"); │ │ │ │ + oDocument.async = false; │ │ │ │ + oDocument.validateOnParse = false; │ │ │ │ + oDocument.loadXML(sResponse); │ │ │ │ + } │ │ │ │ + // Check if there is no error in document │ │ │ │ + if (oDocument) │ │ │ │ + if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror")) │ │ │ │ + return null; │ │ │ │ + return oDocument; │ │ │ │ + }; │ │ │ │ │ │ │ │ - if (notify) { │ │ │ │ - if (this.events.triggerEvent("beforefeatureadded", { │ │ │ │ - feature: feature │ │ │ │ - }) === false) { │ │ │ │ - continue; │ │ │ │ + function fSynchronizeValues(oRequest) { │ │ │ │ + try { │ │ │ │ + oRequest.responseText = oRequest._object.responseText; │ │ │ │ + } catch (e) {} │ │ │ │ + try { │ │ │ │ + oRequest.responseXML = fGetDocument(oRequest._object); │ │ │ │ + } catch (e) {} │ │ │ │ + try { │ │ │ │ + oRequest.status = oRequest._object.status; │ │ │ │ + } catch (e) {} │ │ │ │ + try { │ │ │ │ + oRequest.statusText = oRequest._object.statusText; │ │ │ │ + } catch (e) {} │ │ │ │ + }; │ │ │ │ + │ │ │ │ + function fCleanTransport(oRequest) { │ │ │ │ + // BUGFIX: IE - memory leak (on-page leak) │ │ │ │ + oRequest._object.onreadystatechange = new window.Function; │ │ │ │ + }; │ │ │ │ + /* │ │ │ │ + // Queue manager │ │ │ │ + var oQueuePending = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]}, │ │ │ │ + aQueueRunning = []; │ │ │ │ + function fQueue_add(oRequest) { │ │ │ │ + oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest); │ │ │ │ + // │ │ │ │ + setTimeout(fQueue_process); │ │ │ │ + }; │ │ │ │ + │ │ │ │ + function fQueue_remove(oRequest) { │ │ │ │ + for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++) │ │ │ │ + if (bFound) │ │ │ │ + aQueueRunning[nIndex - 1] = aQueueRunning[nIndex]; │ │ │ │ + else │ │ │ │ + if (aQueueRunning[nIndex] == oRequest) │ │ │ │ + bFound = true; │ │ │ │ + if (bFound) │ │ │ │ + aQueueRunning.length--; │ │ │ │ + // │ │ │ │ + setTimeout(fQueue_process); │ │ │ │ + }; │ │ │ │ + │ │ │ │ + function fQueue_process() { │ │ │ │ + if (aQueueRunning.length < 6) { │ │ │ │ + for (var sPriority in oQueuePending) { │ │ │ │ + if (oQueuePending[sPriority].length) { │ │ │ │ + var oRequest = oQueuePending[sPriority][0]; │ │ │ │ + oQueuePending[sPriority] = oQueuePending[sPriority].slice(1); │ │ │ │ + // │ │ │ │ + aQueueRunning.push(oRequest); │ │ │ │ + // Send request │ │ │ │ + fXMLHttpRequest_send(oRequest); │ │ │ │ + break; │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.preFeatureInsert(feature); │ │ │ │ } │ │ │ │ + }; │ │ │ │ + */ │ │ │ │ + // Internet Explorer 5.0 (missing apply) │ │ │ │ + if (!window.Function.prototype.apply) { │ │ │ │ + window.Function.prototype.apply = function(oRequest, oArguments) { │ │ │ │ + if (!oArguments) │ │ │ │ + oArguments = []; │ │ │ │ + oRequest.__func = this; │ │ │ │ + oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]); │ │ │ │ + delete oRequest.__func; │ │ │ │ + }; │ │ │ │ + }; │ │ │ │ │ │ │ │ - featuresAdded.push(feature); │ │ │ │ - this.features.push(feature); │ │ │ │ - this.drawFeature(feature); │ │ │ │ + // Register new object with window │ │ │ │ + /** │ │ │ │ + * Class: OpenLayers.Request.XMLHttpRequest │ │ │ │ + * Standard-compliant (W3C) cross-browser implementation of the │ │ │ │ + * XMLHttpRequest object. From │ │ │ │ + * http://code.google.com/p/xmlhttprequest/. │ │ │ │ + */ │ │ │ │ + if (!OpenLayers.Request) { │ │ │ │ + /** │ │ │ │ + * This allows for OpenLayers/Request.js to be included │ │ │ │ + * before or after this script. │ │ │ │ + */ │ │ │ │ + OpenLayers.Request = {}; │ │ │ │ + } │ │ │ │ + OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest; │ │ │ │ +})(); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Protocol/HTTP.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featureadded", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.onFeatureInsert(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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 (notify) { │ │ │ │ - this.events.triggerEvent("featuresadded", { │ │ │ │ - features: featuresAdded │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Protocol.js │ │ │ │ + * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * if application uses the query string, for example, for BBOX parameters, │ │ │ │ + * OpenLayers/Format/QueryStringFilter.js should be included in the build config file │ │ │ │ + */ │ │ │ │ │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Protocol.HTTP │ │ │ │ + * A basic HTTP protocol for vector layers. Create a new instance with the │ │ │ │ + * <OpenLayers.Protocol.HTTP> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Protocol> │ │ │ │ + */ │ │ │ │ +OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: removeFeatures │ │ │ │ - * Remove features from the layer. This erases any drawn features and │ │ │ │ - * removes them from the layer's control. The beforefeatureremoved │ │ │ │ - * and featureremoved events will be triggered for each feature. The │ │ │ │ - * featuresremoved event will be triggered after all features have │ │ │ │ - * been removed. To supress event triggering, use the silent option. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be │ │ │ │ - * removed. │ │ │ │ - * options - {Object} Optional properties for changing behavior of the │ │ │ │ - * removal. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * silent - {Boolean} Supress event triggering. Default is false. │ │ │ │ + * Property: url │ │ │ │ + * {String} Service URL, read-only, set through the options │ │ │ │ + * passed to constructor. │ │ │ │ */ │ │ │ │ - removeFeatures: function(features, options) { │ │ │ │ - if (!features || features.length === 0) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (features === this.features) { │ │ │ │ - return this.removeAllFeatures(options); │ │ │ │ - } │ │ │ │ - if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ - features = [features]; │ │ │ │ - } │ │ │ │ - if (features === this.selectedFeatures) { │ │ │ │ - features = features.slice(); │ │ │ │ - } │ │ │ │ + url: null, │ │ │ │ │ │ │ │ - var notify = !options || !options.silent; │ │ │ │ + /** │ │ │ │ + * Property: headers │ │ │ │ + * {Object} HTTP request headers, read-only, set through the options │ │ │ │ + * passed to the constructor, │ │ │ │ + * Example: {'Content-Type': 'plain/text'} │ │ │ │ + */ │ │ │ │ + headers: null, │ │ │ │ │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent( │ │ │ │ - "beforefeaturesremoved", { │ │ │ │ - features: features │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: params │ │ │ │ + * {Object} Parameters of GET requests, read-only, set through the options │ │ │ │ + * passed to the constructor, │ │ │ │ + * Example: {'bbox': '5,5,5,5'} │ │ │ │ + */ │ │ │ │ + params: null, │ │ │ │ │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - // We remain locked so long as we're not at 0 │ │ │ │ - // and the 'next' feature has a geometry. We do the geometry check │ │ │ │ - // because if all the features after the current one are 'null', we │ │ │ │ - // won't call eraseGeometry, so we break the 'renderer functions │ │ │ │ - // will always be called with locked=false *last*' rule. The end result │ │ │ │ - // is a possible gratiutious unlocking to save a loop through the rest │ │ │ │ - // of the list checking the remaining features every time. So long as │ │ │ │ - // null geoms are rare, this is probably okay. │ │ │ │ - if (i != 0 && features[i - 1].geometry) { │ │ │ │ - this.renderer.locked = true; │ │ │ │ - } else { │ │ │ │ - this.renderer.locked = false; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: callback │ │ │ │ + * {Object} Function to be called when the <read>, <create>, │ │ │ │ + * <update>, <delete> or <commit> operation completes, read-only, │ │ │ │ + * set through the options passed to the constructor. │ │ │ │ + */ │ │ │ │ + callback: null, │ │ │ │ │ │ │ │ - var feature = features[i]; │ │ │ │ - delete this.unrenderedFeatures[feature.id]; │ │ │ │ + /** │ │ │ │ + * Property: scope │ │ │ │ + * {Object} Callback execution scope, read-only, set through the │ │ │ │ + * options passed to the constructor. │ │ │ │ + */ │ │ │ │ + scope: null, │ │ │ │ │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: readWithPOST │ │ │ │ + * {Boolean} true if read operations are done with POST requests │ │ │ │ + * instead of GET, defaults to false. │ │ │ │ + */ │ │ │ │ + readWithPOST: false, │ │ │ │ │ │ │ │ - this.features = OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ - // feature has no layer at this point │ │ │ │ - feature.layer = null; │ │ │ │ + /** │ │ │ │ + * APIProperty: updateWithPOST │ │ │ │ + * {Boolean} true if update operations are done with POST requests │ │ │ │ + * defaults to false. │ │ │ │ + */ │ │ │ │ + updateWithPOST: false, │ │ │ │ │ │ │ │ - if (feature.geometry) { │ │ │ │ - this.renderer.eraseFeatures(feature); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: deleteWithPOST │ │ │ │ + * {Boolean} true if delete operations are done with POST requests │ │ │ │ + * defaults to false. │ │ │ │ + * if true, POST data is set to output of format.write(). │ │ │ │ + */ │ │ │ │ + deleteWithPOST: false, │ │ │ │ │ │ │ │ - //in the case that this feature is one of the selected features, │ │ │ │ - // remove it from that array as well. │ │ │ │ - if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) { │ │ │ │ - OpenLayers.Util.removeItem(this.selectedFeatures, feature); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: wildcarded. │ │ │ │ + * {Boolean} If true percent signs are added around values │ │ │ │ + * read from LIKE filters, for example if the protocol │ │ │ │ + * read method is passed a LIKE filter whose property │ │ │ │ + * is "foo" and whose value is "bar" the string │ │ │ │ + * "foo__ilike=%bar%" will be sent in the query string; │ │ │ │ + * defaults to false. │ │ │ │ + */ │ │ │ │ + wildcarded: false, │ │ │ │ │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: srsInBBOX │ │ │ │ + * {Boolean} Include the SRS identifier in BBOX query string parameter. │ │ │ │ + * Default is false. If true and the layer has a projection object set, │ │ │ │ + * any BBOX filter will be serialized with a fifth item identifying the │ │ │ │ + * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913 │ │ │ │ + */ │ │ │ │ + srsInBBOX: false, │ │ │ │ │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featuresremoved", { │ │ │ │ - features: features │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Protocol.HTTP │ │ │ │ + * A class for giving layers generic HTTP protocol. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object whose properties will be set on the │ │ │ │ + * instance. │ │ │ │ + * │ │ │ │ + * Valid options include: │ │ │ │ + * url - {String} │ │ │ │ + * headers - {Object} │ │ │ │ + * params - {Object} URL parameters for GET requests │ │ │ │ + * format - {<OpenLayers.Format>} │ │ │ │ + * callback - {Function} │ │ │ │ + * scope - {Object} │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + this.params = {}; │ │ │ │ + this.headers = {}; │ │ │ │ + OpenLayers.Protocol.prototype.initialize.apply(this, arguments); │ │ │ │ + │ │ │ │ + if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { │ │ │ │ + var format = new OpenLayers.Format.QueryStringFilter({ │ │ │ │ + wildcarded: this.wildcarded, │ │ │ │ + srsInBBOX: this.srsInBBOX │ │ │ │ }); │ │ │ │ + this.filterToParams = function(filter, params) { │ │ │ │ + return format.write(filter, params); │ │ │ │ + }; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: removeAllFeatures │ │ │ │ - * Remove all features from the layer. │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up the protocol. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.params = null; │ │ │ │ + this.headers = null; │ │ │ │ + OpenLayers.Protocol.prototype.destroy.apply(this); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: filterToParams │ │ │ │ + * Optional method to translate an <OpenLayers.Filter> object into an object │ │ │ │ + * that can be serialized as request query string provided. If a custom │ │ │ │ + * method is not provided, the filter will be serialized using the │ │ │ │ + * <OpenLayers.Format.QueryStringFilter> class. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional properties for changing behavior of the │ │ │ │ - * removal. │ │ │ │ + * filter - {<OpenLayers.Filter>} filter to convert. │ │ │ │ + * params - {Object} The parameters object. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} The resulting parameters object. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: read │ │ │ │ + * Construct a request for reading new features. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ * │ │ │ │ * Valid options: │ │ │ │ - * silent - {Boolean} Supress event triggering. Default is false. │ │ │ │ + * url - {String} Url for the request. │ │ │ │ + * params - {Object} Parameters to get serialized as a query string. │ │ │ │ + * headers - {Object} Headers to be set on the request. │ │ │ │ + * filter - {<OpenLayers.Filter>} Filter to get serialized as a │ │ │ │ + * query string. │ │ │ │ + * readWithPOST - {Boolean} If the request should be done with POST. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property │ │ │ │ + * references the HTTP request, this object is also passed to the │ │ │ │ + * callback function when the request completes, its "features" property │ │ │ │ + * is then populated with the features received from the server. │ │ │ │ */ │ │ │ │ - removeAllFeatures: function(options) { │ │ │ │ - var notify = !options || !options.silent; │ │ │ │ - var features = this.features; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent( │ │ │ │ - "beforefeaturesremoved", { │ │ │ │ - features: features │ │ │ │ - } │ │ │ │ + read: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ + options = options || {}; │ │ │ │ + options.params = OpenLayers.Util.applyDefaults( │ │ │ │ + options.params, this.options.params); │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + if (options.filter && this.filterToParams) { │ │ │ │ + options.params = this.filterToParams( │ │ │ │ + options.filter, options.params │ │ │ │ ); │ │ │ │ } │ │ │ │ - var feature; │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - feature = features[i]; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - feature.layer = null; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.renderer.clear(); │ │ │ │ - this.features = []; │ │ │ │ - this.unrenderedFeatures = {}; │ │ │ │ - this.selectedFeatures = []; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featuresremoved", { │ │ │ │ - features: features │ │ │ │ + var readWithPOST = (options.readWithPOST !== undefined) ? │ │ │ │ + options.readWithPOST : this.readWithPOST; │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "read" │ │ │ │ + }); │ │ │ │ + if (readWithPOST) { │ │ │ │ + var headers = options.headers || {}; │ │ │ │ + headers["Content-Type"] = "application/x-www-form-urlencoded"; │ │ │ │ + resp.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ + data: OpenLayers.Util.getParameterString(options.params), │ │ │ │ + headers: headers │ │ │ │ + }); │ │ │ │ + } else { │ │ │ │ + resp.priv = OpenLayers.Request.GET({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ + params: options.params, │ │ │ │ + headers: options.headers │ │ │ │ }); │ │ │ │ } │ │ │ │ + return resp; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroyFeatures │ │ │ │ - * Erase and destroy features on the layer. │ │ │ │ + * Method: handleRead │ │ │ │ + * Individual callbacks are created for read, create and update, should │ │ │ │ + * a subclass need to override each one separately. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of │ │ │ │ - * features to destroy. If not supplied, all features on the layer │ │ │ │ - * will be destroyed. │ │ │ │ - * options - {Object} │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ + * the user callback. │ │ │ │ + * options - {Object} The user options passed to the read call. │ │ │ │ */ │ │ │ │ - destroyFeatures: function(features, options) { │ │ │ │ - var all = (features == undefined); // evaluates to true if │ │ │ │ - // features is null │ │ │ │ - if (all) { │ │ │ │ - features = this.features; │ │ │ │ - } │ │ │ │ - if (features) { │ │ │ │ - this.removeFeatures(features, options); │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - features[i].destroy(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + handleRead: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: drawFeature │ │ │ │ - * Draw (or redraw) a feature on the layer. If the optional style argument │ │ │ │ - * is included, this style will be used. If no style is included, the │ │ │ │ - * feature's style will be used. If the feature doesn't have a style, │ │ │ │ - * the layer's style will be used. │ │ │ │ - * │ │ │ │ - * This function is not designed to be used when adding features to │ │ │ │ - * the layer (use addFeatures instead). It is meant to be used when │ │ │ │ - * the style of a feature has changed, or in some other way needs to │ │ │ │ - * visually updated *after* it has already been added to a layer. You │ │ │ │ - * must add the feature to the layer for most layer-related events to │ │ │ │ - * happen. │ │ │ │ + * APIMethod: create │ │ │ │ + * Construct a request for writing newly created features. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * style - {String | Object} Named render intent or full symbolizer object. │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ + * {<OpenLayers.Feature.Vector>} │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ + * object, whose "priv" property references the HTTP request, this │ │ │ │ + * object is also passed to the callback function when the request │ │ │ │ + * completes, its "features" property is then populated with the │ │ │ │ + * the features received from the server. │ │ │ │ */ │ │ │ │ - drawFeature: function(feature, style) { │ │ │ │ - // don't try to draw the feature with the renderer if the layer is not │ │ │ │ - // drawn itself │ │ │ │ - if (!this.drawn) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (typeof style != "object") { │ │ │ │ - if (!style && feature.state === OpenLayers.State.DELETE) { │ │ │ │ - style = "delete"; │ │ │ │ - } │ │ │ │ - var renderIntent = style || feature.renderIntent; │ │ │ │ - style = feature.style || this.style; │ │ │ │ - if (!style) { │ │ │ │ - style = this.styleMap.createSymbolizer(feature, renderIntent); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + create: function(features, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ │ │ │ │ - var drawn = this.renderer.drawFeature(feature, style); │ │ │ │ - //TODO remove the check for null when we get rid of Renderer.SVG │ │ │ │ - if (drawn === false || drawn === null) { │ │ │ │ - this.unrenderedFeatures[feature.id] = feature; │ │ │ │ - } else { │ │ │ │ - delete this.unrenderedFeatures[feature.id]; │ │ │ │ - } │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: features, │ │ │ │ + requestType: "create" │ │ │ │ + }); │ │ │ │ + │ │ │ │ + resp.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleCreate, resp, options), │ │ │ │ + headers: options.headers, │ │ │ │ + data: this.format.write(features) │ │ │ │ + }); │ │ │ │ + │ │ │ │ + return resp; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: eraseFeatures │ │ │ │ - * Erase features from the layer. │ │ │ │ + * Method: handleCreate │ │ │ │ + * Called the the request issued by <create> is complete. May be overridden │ │ │ │ + * by subclasses. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ + * any user callback. │ │ │ │ + * options - {Object} The user options passed to the create call. │ │ │ │ */ │ │ │ │ - eraseFeatures: function(features) { │ │ │ │ - this.renderer.eraseFeatures(features); │ │ │ │ + handleCreate: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getFeatureFromEvent │ │ │ │ - * Given an event, return a feature if the event occurred over one. │ │ │ │ - * Otherwise, return null. │ │ │ │ + * APIMethod: update │ │ │ │ + * Construct a request updating modified feature. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A feature if one was under the event. │ │ │ │ + * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ + * object, whose "priv" property references the HTTP request, this │ │ │ │ + * object is also passed to the callback function when the request │ │ │ │ + * completes, its "features" property is then populated with the │ │ │ │ + * the feature received from the server. │ │ │ │ */ │ │ │ │ - getFeatureFromEvent: function(evt) { │ │ │ │ - if (!this.renderer) { │ │ │ │ - throw new Error('getFeatureFromEvent called on layer with no ' + │ │ │ │ - 'renderer. This usually means you destroyed a ' + │ │ │ │ - 'layer, but not some handler which is associated ' + │ │ │ │ - 'with it.'); │ │ │ │ - } │ │ │ │ - var feature = null; │ │ │ │ - var featureId = this.renderer.getFeatureIdFromEvent(evt); │ │ │ │ - if (featureId) { │ │ │ │ - if (typeof featureId === "string") { │ │ │ │ - feature = this.getFeatureById(featureId); │ │ │ │ - } else { │ │ │ │ - feature = featureId; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return feature; │ │ │ │ + update: function(feature, options) { │ │ │ │ + options = options || {}; │ │ │ │ + var url = options.url || │ │ │ │ + feature.url || │ │ │ │ + this.options.url + "/" + feature.fid; │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: feature, │ │ │ │ + requestType: "update" │ │ │ │ + }); │ │ │ │ + │ │ │ │ + var method = this.updateWithPOST ? "POST" : "PUT"; │ │ │ │ + resp.priv = OpenLayers.Request[method]({ │ │ │ │ + url: url, │ │ │ │ + callback: this.createCallback(this.handleUpdate, resp, options), │ │ │ │ + headers: options.headers, │ │ │ │ + data: this.format.write(feature) │ │ │ │ + }); │ │ │ │ + │ │ │ │ + return resp; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getFeatureBy │ │ │ │ - * Given a property value, return the feature if it exists in the features array │ │ │ │ + * Method: handleUpdate │ │ │ │ + * Called the the request issued by <update> is complete. May be overridden │ │ │ │ + * by subclasses. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * property - {String} │ │ │ │ - * value - {String} │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ + * any user callback. │ │ │ │ + * options - {Object} The user options passed to the update call. │ │ │ │ + */ │ │ │ │ + handleUpdate: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: delete │ │ │ │ + * Construct a request deleting a removed feature. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * options - {Object} Optional object for configuring the request. │ │ │ │ + * This object is modified and should not be reused. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A feature corresponding to the given │ │ │ │ - * property value or null if there is no such feature. │ │ │ │ + * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ + * object, whose "priv" property references the HTTP request, this │ │ │ │ + * object is also passed to the callback function when the request │ │ │ │ + * completes. │ │ │ │ */ │ │ │ │ - getFeatureBy: function(property, value) { │ │ │ │ - //TBD - would it be more efficient to use a hash for this.features? │ │ │ │ - var feature = null; │ │ │ │ - for (var i = 0, len = this.features.length; i < len; ++i) { │ │ │ │ - if (this.features[i][property] == value) { │ │ │ │ - feature = this.features[i]; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ + "delete": function(feature, options) { │ │ │ │ + options = options || {}; │ │ │ │ + var url = options.url || │ │ │ │ + feature.url || │ │ │ │ + this.options.url + "/" + feature.fid; │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: feature, │ │ │ │ + requestType: "delete" │ │ │ │ + }); │ │ │ │ + │ │ │ │ + var method = this.deleteWithPOST ? "POST" : "DELETE"; │ │ │ │ + var requestOptions = { │ │ │ │ + url: url, │ │ │ │ + callback: this.createCallback(this.handleDelete, resp, options), │ │ │ │ + headers: options.headers │ │ │ │ + }; │ │ │ │ + if (this.deleteWithPOST) { │ │ │ │ + requestOptions.data = this.format.write(feature); │ │ │ │ } │ │ │ │ - return feature; │ │ │ │ + resp.priv = OpenLayers.Request[method](requestOptions); │ │ │ │ + │ │ │ │ + return resp; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getFeatureById │ │ │ │ - * Given a feature id, return the feature if it exists in the features array │ │ │ │ + * Method: handleDelete │ │ │ │ + * Called the the request issued by <delete> is complete. May be overridden │ │ │ │ + * by subclasses. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * featureId - {String} │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ + * any user callback. │ │ │ │ + * options - {Object} The user options passed to the delete call. │ │ │ │ + */ │ │ │ │ + handleDelete: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: handleResponse │ │ │ │ + * Called by CRUD specific handlers. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A feature corresponding to the given │ │ │ │ - * featureId or null if there is no such feature. │ │ │ │ + * Parameters: │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ + * any user callback. │ │ │ │ + * options - {Object} The user options passed to the create, read, update, │ │ │ │ + * or delete call. │ │ │ │ */ │ │ │ │ - getFeatureById: function(featureId) { │ │ │ │ - return this.getFeatureBy('id', featureId); │ │ │ │ + handleResponse: function(resp, options) { │ │ │ │ + var request = resp.priv; │ │ │ │ + if (options.callback) { │ │ │ │ + if (request.status >= 200 && request.status < 300) { │ │ │ │ + // success │ │ │ │ + if (resp.requestType != "delete") { │ │ │ │ + resp.features = this.parseFeatures(request); │ │ │ │ + } │ │ │ │ + resp.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ + } else { │ │ │ │ + // failure │ │ │ │ + resp.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + } │ │ │ │ + options.callback.call(options.scope, resp); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getFeatureByFid │ │ │ │ - * Given a feature fid, return the feature if it exists in the features array │ │ │ │ + * Method: parseFeatures │ │ │ │ + * Read HTTP response body and return features. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * featureFid - {String} │ │ │ │ + * request - {XMLHttpRequest} The request object │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A feature corresponding to the given │ │ │ │ - * featureFid or null if there is no such feature. │ │ │ │ + * {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ + * {<OpenLayers.Feature.Vector>} Array of features or a single feature. │ │ │ │ */ │ │ │ │ - getFeatureByFid: function(featureFid) { │ │ │ │ - return this.getFeatureBy('fid', featureFid); │ │ │ │ + parseFeatures: function(request) { │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText; │ │ │ │ + } │ │ │ │ + if (!doc || doc.length <= 0) { │ │ │ │ + return null; │ │ │ │ + } │ │ │ │ + return this.format.read(doc); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getFeaturesByAttribute │ │ │ │ - * Returns an array of features that have the given attribute key set to the │ │ │ │ - * given value. Comparison of attribute values takes care of datatypes, e.g. │ │ │ │ - * the string '1234' is not equal to the number 1234. │ │ │ │ + * APIMethod: commit │ │ │ │ + * Iterate over each feature and take action based on the feature state. │ │ │ │ + * Possible actions are create, update and delete. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * attrName - {String} │ │ │ │ - * attrValue - {Mixed} │ │ │ │ + * features - {Array({<OpenLayers.Feature.Vector>})} │ │ │ │ + * options - {Object} Optional object for setting up intermediate commit │ │ │ │ + * callbacks. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * create - {Object} Optional object to be passed to the <create> method. │ │ │ │ + * update - {Object} Optional object to be passed to the <update> method. │ │ │ │ + * delete - {Object} Optional object to be passed to the <delete> method. │ │ │ │ + * callback - {Function} Optional function to be called when the commit │ │ │ │ + * is complete. │ │ │ │ + * scope - {Object} Optional object to be set as the scope of the callback. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * Array({<OpenLayers.Feature.Vector>}) An array of features that have the │ │ │ │ - * passed named attribute set to the given value. │ │ │ │ + * {Array(<OpenLayers.Protocol.Response>)} An array of response objects, │ │ │ │ + * one per request made to the server, each object's "priv" property │ │ │ │ + * references the corresponding HTTP request. │ │ │ │ */ │ │ │ │ - getFeaturesByAttribute: function(attrName, attrValue) { │ │ │ │ - var i, │ │ │ │ - feature, │ │ │ │ - len = this.features.length, │ │ │ │ - foundFeatures = []; │ │ │ │ - for (i = 0; i < len; i++) { │ │ │ │ - feature = this.features[i]; │ │ │ │ - if (feature && feature.attributes) { │ │ │ │ - if (feature.attributes[attrName] === attrValue) { │ │ │ │ - foundFeatures.push(feature); │ │ │ │ - } │ │ │ │ + commit: function(features, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var resp = [], │ │ │ │ + nResponses = 0; │ │ │ │ + │ │ │ │ + // Divide up features before issuing any requests. This properly │ │ │ │ + // counts requests in the event that any responses come in before │ │ │ │ + // all requests have been issued. │ │ │ │ + var types = {}; │ │ │ │ + types[OpenLayers.State.INSERT] = []; │ │ │ │ + types[OpenLayers.State.UPDATE] = []; │ │ │ │ + types[OpenLayers.State.DELETE] = []; │ │ │ │ + var feature, list, requestFeatures = []; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + feature = features[i]; │ │ │ │ + list = types[feature.state]; │ │ │ │ + if (list) { │ │ │ │ + list.push(feature); │ │ │ │ + requestFeatures.push(feature); │ │ │ │ } │ │ │ │ } │ │ │ │ - return foundFeatures; │ │ │ │ - }, │ │ │ │ + // tally up number of requests │ │ │ │ + var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + │ │ │ │ + types[OpenLayers.State.UPDATE].length + │ │ │ │ + types[OpenLayers.State.DELETE].length; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Unselect the selected features │ │ │ │ - * i.e. clears the featureSelection array │ │ │ │ - * change the style back │ │ │ │ - clearSelection: function() { │ │ │ │ + // This response will be sent to the final callback after all the others │ │ │ │ + // have been fired. │ │ │ │ + var success = true; │ │ │ │ + var finalResponse = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: requestFeatures │ │ │ │ + }); │ │ │ │ │ │ │ │ - var vectorLayer = this.map.vectorLayer; │ │ │ │ - for (var i = 0; i < this.map.featureSelection.length; i++) { │ │ │ │ - var featureSelection = this.map.featureSelection[i]; │ │ │ │ - vectorLayer.drawFeature(featureSelection, vectorLayer.style); │ │ │ │ + function insertCallback(response) { │ │ │ │ + var len = response.features ? response.features.length : 0; │ │ │ │ + var fids = new Array(len); │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ + fids[i] = response.features[i].fid; │ │ │ │ + } │ │ │ │ + finalResponse.insertIds = fids; │ │ │ │ + callback.apply(this, [response]); │ │ │ │ + } │ │ │ │ + │ │ │ │ + function callback(response) { │ │ │ │ + this.callUserCallback(response, options); │ │ │ │ + success = success && response.success(); │ │ │ │ + nResponses++; │ │ │ │ + if (nResponses >= nRequests) { │ │ │ │ + if (options.callback) { │ │ │ │ + finalResponse.code = success ? │ │ │ │ + OpenLayers.Protocol.Response.SUCCESS : │ │ │ │ + OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + options.callback.apply(options.scope, [finalResponse]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.map.featureSelection = []; │ │ │ │ - }, │ │ │ │ - */ │ │ │ │ │ │ │ │ + // start issuing requests │ │ │ │ + var queue = types[OpenLayers.State.INSERT]; │ │ │ │ + if (queue.length > 0) { │ │ │ │ + resp.push(this.create( │ │ │ │ + queue, OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: insertCallback, │ │ │ │ + scope: this │ │ │ │ + }, options.create) │ │ │ │ + )); │ │ │ │ + } │ │ │ │ + queue = types[OpenLayers.State.UPDATE]; │ │ │ │ + for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ + resp.push(this.update( │ │ │ │ + queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: callback, │ │ │ │ + scope: this │ │ │ │ + }, options.update))); │ │ │ │ + } │ │ │ │ + queue = types[OpenLayers.State.DELETE]; │ │ │ │ + for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ + resp.push(this["delete"]( │ │ │ │ + queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: callback, │ │ │ │ + scope: this │ │ │ │ + }, options["delete"]))); │ │ │ │ + } │ │ │ │ + return resp; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: onFeatureInsert │ │ │ │ - * method called after a feature is inserted. │ │ │ │ - * Does nothing by default. Override this if you │ │ │ │ - * need to do something on feature updates. │ │ │ │ + * APIMethod: abort │ │ │ │ + * Abort an ongoing request, the response object passed to │ │ │ │ + * this method must come from this HTTP protocol (as a result │ │ │ │ + * of a create, read, update, delete or commit operation). │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * Parameters: │ │ │ │ + * response - {<OpenLayers.Protocol.Response>} │ │ │ │ */ │ │ │ │ - onFeatureInsert: function(feature) {}, │ │ │ │ + abort: function(response) { │ │ │ │ + if (response) { │ │ │ │ + response.priv.abort(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: preFeatureInsert │ │ │ │ - * method called before a feature is inserted. │ │ │ │ - * Does nothing by default. Override this if you │ │ │ │ - * need to do something when features are first added to the │ │ │ │ - * layer, but before they are drawn, such as adjust the style. │ │ │ │ + * Method: callUserCallback │ │ │ │ + * This method is used from within the commit method each time an │ │ │ │ + * an HTTP response is received from the server, it is responsible │ │ │ │ + * for calling the user-supplied callbacks. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - */ │ │ │ │ - preFeatureInsert: function(feature) {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getDataExtent │ │ │ │ - * Calculates the max extent which includes all of the features. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} or null if the layer has no features with │ │ │ │ - * geometries. │ │ │ │ + * resp - {<OpenLayers.Protocol.Response>} │ │ │ │ + * options - {Object} The map of options passed to the commit call. │ │ │ │ */ │ │ │ │ - getDataExtent: function() { │ │ │ │ - var maxExtent = null; │ │ │ │ - var features = this.features; │ │ │ │ - if (features && (features.length > 0)) { │ │ │ │ - var geometry = null; │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - geometry = features[i].geometry; │ │ │ │ - if (geometry) { │ │ │ │ - if (maxExtent === null) { │ │ │ │ - maxExtent = new OpenLayers.Bounds(); │ │ │ │ - } │ │ │ │ - maxExtent.extend(geometry.getBounds()); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + callUserCallback: function(resp, options) { │ │ │ │ + var opt = options[resp.requestType]; │ │ │ │ + if (opt && opt.callback) { │ │ │ │ + opt.callback.call(opt.scope, resp); │ │ │ │ } │ │ │ │ - return maxExtent; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Vector" │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.HTTP" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/WMS.js │ │ │ │ + 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/Layer/Grid.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.WMS │ │ │ │ - * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web │ │ │ │ - * Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS> │ │ │ │ - * constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Grid> │ │ │ │ + * 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.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ +OpenLayers.Format = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: DEFAULT_PARAMS │ │ │ │ - * {Object} Hashtable of default parameter key/value pairs │ │ │ │ + * Property: options │ │ │ │ + * {Object} A reference to options passed to the constructor. │ │ │ │ */ │ │ │ │ - DEFAULT_PARAMS: { │ │ │ │ - service: "WMS", │ │ │ │ - version: "1.1.1", │ │ │ │ - request: "GetMap", │ │ │ │ - styles: "", │ │ │ │ - format: "image/jpeg" │ │ │ │ - }, │ │ │ │ + options: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: isBaseLayer │ │ │ │ - * {Boolean} Default is true for WMS layer │ │ │ │ + * APIProperty: externalProjection │ │ │ │ + * {<OpenLayers.Projection>} When passed a externalProjection and │ │ │ │ + * internalProjection, the format will reproject the geometries it │ │ │ │ + * reads or writes. The externalProjection is the projection used by │ │ │ │ + * the content which is passed into read or which comes out of write. │ │ │ │ + * In order to reproject, a projection transformation function for the │ │ │ │ + * specified projections must be available. This support may be │ │ │ │ + * provided via proj4js or via a custom transformation function. See │ │ │ │ + * {<OpenLayers.Projection.addTransform>} for more information on │ │ │ │ + * custom transformations. │ │ │ │ */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + externalProjection: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: encodeBBOX │ │ │ │ - * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', │ │ │ │ - * but some services want it that way. Default false. │ │ │ │ + * APIProperty: internalProjection │ │ │ │ + * {<OpenLayers.Projection>} When passed a externalProjection and │ │ │ │ + * internalProjection, the format will reproject the geometries it │ │ │ │ + * reads or writes. The internalProjection is the projection used by │ │ │ │ + * the geometries which are returned by read or which are passed into │ │ │ │ + * write. In order to reproject, a projection transformation function │ │ │ │ + * for the specified projections must be available. This support may be │ │ │ │ + * provided via proj4js or via a custom transformation function. See │ │ │ │ + * {<OpenLayers.Projection.addTransform>} for more information on │ │ │ │ + * custom transformations. │ │ │ │ */ │ │ │ │ - encodeBBOX: false, │ │ │ │ + internalProjection: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: noMagic │ │ │ │ - * {Boolean} If true, the image format will not be automagicaly switched │ │ │ │ - * from image/jpeg to image/png or image/gif when using │ │ │ │ - * TRANSPARENT=TRUE. Also isBaseLayer will not changed by the │ │ │ │ - * constructor. Default false. │ │ │ │ + /** │ │ │ │ + * APIProperty: data │ │ │ │ + * {Object} When <keepData> is true, this is the parsed string sent to │ │ │ │ + * <read>. │ │ │ │ */ │ │ │ │ - noMagic: false, │ │ │ │ + data: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: yx │ │ │ │ - * {Object} Keys in this object are EPSG codes for which the axis order │ │ │ │ - * is to be reversed (yx instead of xy, LatLon instead of LonLat), with │ │ │ │ - * true as value. This is only relevant for WMS versions >= 1.3.0, and │ │ │ │ - * only if yx is not set in <OpenLayers.Projection.defaults> for the │ │ │ │ - * used projection. │ │ │ │ + * APIProperty: keepData │ │ │ │ + * {Object} Maintain a reference (<data>) to the most recently read data. │ │ │ │ + * Default is false. │ │ │ │ */ │ │ │ │ - yx: {}, │ │ │ │ + keepData: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.WMS │ │ │ │ - * Create a new WMS layer object │ │ │ │ - * │ │ │ │ - * Examples: │ │ │ │ - * │ │ │ │ - * The code below creates a simple WMS layer using the image/jpeg format. │ │ │ │ - * (code) │ │ │ │ - * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic", │ │ │ │ - * "http://wms.jpl.nasa.gov/wms.cgi", │ │ │ │ - * {layers: "modis,global_mosaic"}); │ │ │ │ - * (end) │ │ │ │ - * Note the 3rd argument (params). Properties added to this object will be │ │ │ │ - * added to the WMS GetMap requests used for this layer's tiles. The only │ │ │ │ - * mandatory parameter is "layers". Other common WMS params include │ │ │ │ - * "transparent", "styles" and "format". Note that the "srs" param will │ │ │ │ - * always be ignored. Instead, it will be derived from the baseLayer's or │ │ │ │ - * map's projection. │ │ │ │ - * │ │ │ │ - * The code below creates a transparent WMS layer with additional options. │ │ │ │ - * (code) │ │ │ │ - * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic", │ │ │ │ - * "http://wms.jpl.nasa.gov/wms.cgi", │ │ │ │ - * { │ │ │ │ - * layers: "modis,global_mosaic", │ │ │ │ - * transparent: true │ │ │ │ - * }, { │ │ │ │ - * opacity: 0.5, │ │ │ │ - * singleTile: true │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ - * Note that by default, a WMS layer is configured as baseLayer. Setting │ │ │ │ - * the "transparent" param to true will apply some magic (see <noMagic>). │ │ │ │ - * The default image format changes from image/jpeg to image/png, and the │ │ │ │ - * layer is not configured as baseLayer. │ │ │ │ + * Constructor: OpenLayers.Format │ │ │ │ + * Instances of this class are not useful. See one of the subclasses. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} A name for the layer │ │ │ │ - * url - {String} Base url for the WMS │ │ │ │ - * (e.g. http://wms.jpl.nasa.gov/wms.cgi) │ │ │ │ - * params - {Object} An object with key/value pairs representing the │ │ │ │ - * GetMap query string parameters and parameter values. │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer. │ │ │ │ - * These options include all properties listed above, plus the ones │ │ │ │ - * inherited from superclasses. │ │ │ │ - */ │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - var newArguments = []; │ │ │ │ - //uppercase params │ │ │ │ - params = OpenLayers.Util.upperCaseObject(params); │ │ │ │ - if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) { │ │ │ │ - params.EXCEPTIONS = "INIMAGE"; │ │ │ │ - } │ │ │ │ - newArguments.push(name, url, params, options); │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ - OpenLayers.Util.applyDefaults( │ │ │ │ - this.params, │ │ │ │ - OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS) │ │ │ │ - ); │ │ │ │ - │ │ │ │ - │ │ │ │ - //layer is transparent │ │ │ │ - if (!this.noMagic && this.params.TRANSPARENT && │ │ │ │ - this.params.TRANSPARENT.toString().toLowerCase() == "true") { │ │ │ │ - │ │ │ │ - // unless explicitly set in options, make layer an overlay │ │ │ │ - if ((options == null) || (!options.isBaseLayer)) { │ │ │ │ - this.isBaseLayer = false; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // jpegs can never be transparent, so intelligently switch the │ │ │ │ - // format, depending on the browser's capabilities │ │ │ │ - if (this.params.FORMAT == "image/jpeg") { │ │ │ │ - this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif" : │ │ │ │ - "image/png"; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ + * options - {Object} An optional object with properties to set on the │ │ │ │ + * format │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * keepData - {Boolean} If true, upon <read>, the data property will be │ │ │ │ + * set to the parsed object (e.g. the json or xml object). │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Layer.WMS>} An exact clone of this layer │ │ │ │ + * An instance of OpenLayers.Format │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.WMS(this.name, │ │ │ │ - this.url, │ │ │ │ - this.params, │ │ │ │ - this.getOptions()); │ │ │ │ - } │ │ │ │ - │ │ │ │ - //get all additions from superclasses │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - │ │ │ │ - // copy/set any non-init, non-simple values here │ │ │ │ - │ │ │ │ - return obj; │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.options = options; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: reverseAxisOrder │ │ │ │ - * Returns true if the axis order is reversed for the WMS version and │ │ │ │ - * projection of the layer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} true if the axis order is reversed, false otherwise. │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up. │ │ │ │ */ │ │ │ │ - reverseAxisOrder: function() { │ │ │ │ - var projCode = this.projection.getCode(); │ │ │ │ - return parseFloat(this.params.VERSION) >= 1.3 && │ │ │ │ - !!(this.yx[projCode] || (OpenLayers.Projection.defaults[projCode] && │ │ │ │ - OpenLayers.Projection.defaults[projCode].yx)); │ │ │ │ - }, │ │ │ │ + destroy: function() {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getURL │ │ │ │ - * Return a GetMap query string for this layer │ │ │ │ - * │ │ │ │ + * Method: read │ │ │ │ + * Read data from a string, and return an object whose type depends on the │ │ │ │ + * subclass. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the │ │ │ │ - * request. │ │ │ │ + * data - {string} Data to read/parse. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A string with the layer's url and parameters and also the │ │ │ │ - * passed-in bounds and appropriate tile size specified as │ │ │ │ - * parameters. │ │ │ │ + * Depends on the subclass │ │ │ │ */ │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - │ │ │ │ - var imageSize = this.getImageSize(); │ │ │ │ - var newParams = {}; │ │ │ │ - // WMS 1.3 introduced axis order │ │ │ │ - var reverseAxisOrder = this.reverseAxisOrder(); │ │ │ │ - newParams.BBOX = this.encodeBBOX ? │ │ │ │ - bounds.toBBOX(null, reverseAxisOrder) : │ │ │ │ - bounds.toArray(reverseAxisOrder); │ │ │ │ - newParams.WIDTH = imageSize.w; │ │ │ │ - newParams.HEIGHT = imageSize.h; │ │ │ │ - var requestString = this.getFullRequestString(newParams); │ │ │ │ - return requestString; │ │ │ │ + read: function(data) { │ │ │ │ + throw new Error('Read not implemented.'); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: mergeNewParams │ │ │ │ - * Catch changeParams and uppercase the new params to be merged in │ │ │ │ - * before calling changeParams on the super class. │ │ │ │ - * │ │ │ │ - * Once params have been changed, the tiles will be reloaded with │ │ │ │ - * the new parameters. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * newParams - {Object} Hashtable of new params to use │ │ │ │ - */ │ │ │ │ - mergeNewParams: function(newParams) { │ │ │ │ - var upperParams = OpenLayers.Util.upperCaseObject(newParams); │ │ │ │ - var newArguments = [upperParams]; │ │ │ │ - return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, │ │ │ │ - newArguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getFullRequestString │ │ │ │ - * Combine the layer's url with its params and these newParams. │ │ │ │ - * │ │ │ │ - * Add the SRS parameter from projection -- this is probably │ │ │ │ - * more eloquently done via a setProjection() method, but this │ │ │ │ - * works for now and always. │ │ │ │ + * Method: write │ │ │ │ + * Accept an object, and return a string. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * newParams - {Object} │ │ │ │ - * altUrl - {String} Use this as the url instead of the layer's url │ │ │ │ - * │ │ │ │ + * object - {Object} Object to be serialized │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} │ │ │ │ + * {String} A string representation of the object. │ │ │ │ */ │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ - var mapProjection = this.map.getProjectionObject(); │ │ │ │ - var projectionCode = this.projection && this.projection.equals(mapProjection) ? │ │ │ │ - this.projection.getCode() : │ │ │ │ - mapProjection.getCode(); │ │ │ │ - var value = (projectionCode == "none") ? null : projectionCode; │ │ │ │ - if (parseFloat(this.params.VERSION) >= 1.3) { │ │ │ │ - this.params.CRS = value; │ │ │ │ - } else { │ │ │ │ - this.params.SRS = value; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (typeof this.params.TRANSPARENT == "boolean") { │ │ │ │ - newParams.TRANSPARENT = this.params.TRANSPARENT ? "TRUE" : "FALSE"; │ │ │ │ - } │ │ │ │ - │ │ │ │ - return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply( │ │ │ │ - this, arguments); │ │ │ │ + write: function(object) { │ │ │ │ + throw new Error('Write not implemented.'); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.WMS" │ │ │ │ + CLASS_NAME: "OpenLayers.Format" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/OSM.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/Layer/XYZ.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. │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.OSM │ │ │ │ - * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap │ │ │ │ - * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use │ │ │ │ - * a different layer instead, you need to provide a different │ │ │ │ - * URL to the constructor. Here's an example for using OpenCycleMap: │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * new OpenLayers.Layer.OSM("OpenCycleMap", │ │ │ │ - * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ - * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ - * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); │ │ │ │ - * (end) │ │ │ │ + * @requires OpenLayers/Format.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Format.JSON │ │ │ │ + * A parser to read/write JSON safely. Create a new instance with the │ │ │ │ + * <OpenLayers.Format.JSON> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.XYZ> │ │ │ │ + * - <OpenLayers.Format> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ +OpenLayers.Format.JSON = OpenLayers.Class(OpenLayers.Format, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: name │ │ │ │ - * {String} The layer name. Defaults to "OpenStreetMap" if the first │ │ │ │ - * argument to the constructor is null or undefined. │ │ │ │ + * APIProperty: indent │ │ │ │ + * {String} For "pretty" printing, the indent string will be used once for │ │ │ │ + * each indentation level. │ │ │ │ */ │ │ │ │ - name: "OpenStreetMap", │ │ │ │ + indent: " ", │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: url │ │ │ │ - * {String} The tileset URL scheme. Defaults to │ │ │ │ - * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png │ │ │ │ - * (the official OSM tileset) if the second argument to the constructor │ │ │ │ - * is null or undefined. To use another tileset you can have something │ │ │ │ - * like this: │ │ │ │ - * (code) │ │ │ │ - * new OpenLayers.Layer.OSM("OpenCycleMap", │ │ │ │ - * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ - * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ - * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); │ │ │ │ - * (end) │ │ │ │ + * APIProperty: space │ │ │ │ + * {String} For "pretty" printing, the space string will be used after │ │ │ │ + * the ":" separating a name/value pair. │ │ │ │ */ │ │ │ │ - url: [ │ │ │ │ - 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png', │ │ │ │ - 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png', │ │ │ │ - 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png' │ │ │ │ - ], │ │ │ │ + space: " ", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: attribution │ │ │ │ - * {String} The layer attribution. │ │ │ │ + * APIProperty: newline │ │ │ │ + * {String} For "pretty" printing, the newline string will be used at the │ │ │ │ + * end of each name/value pair or array item. │ │ │ │ */ │ │ │ │ - attribution: "© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors", │ │ │ │ + newline: "\n", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: sphericalMercator │ │ │ │ - * {Boolean} │ │ │ │ + * Property: level │ │ │ │ + * {Integer} For "pretty" printing, this is incremented/decremented during │ │ │ │ + * serialization. │ │ │ │ */ │ │ │ │ - sphericalMercator: true, │ │ │ │ + level: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: wrapDateLine │ │ │ │ - * {Boolean} │ │ │ │ + * Property: pretty │ │ │ │ + * {Boolean} Serialize with extra whitespace for structure. This is set │ │ │ │ + * by the <write> method. │ │ │ │ */ │ │ │ │ - wrapDateLine: true, │ │ │ │ + pretty: false, │ │ │ │ │ │ │ │ - /** APIProperty: tileOptions │ │ │ │ - * {Object} optional configuration options for <OpenLayers.Tile> instances │ │ │ │ - * created by this Layer. Default is │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * {crossOriginKeyword: 'anonymous'} │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * When using OSM tilesets other than the default ones, it may be │ │ │ │ - * necessary to set this to │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * {crossOriginKeyword: null} │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * if the server does not send Access-Control-Allow-Origin headers. │ │ │ │ + /** │ │ │ │ + * Property: nativeJSON │ │ │ │ + * {Boolean} Does the browser support native json? │ │ │ │ */ │ │ │ │ - tileOptions: null, │ │ │ │ + nativeJSON: (function() { │ │ │ │ + return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function"); │ │ │ │ + })(), │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.OSM │ │ │ │ + * Constructor: OpenLayers.Format.JSON │ │ │ │ + * Create a new parser for JSON. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * name - {String} The layer name. │ │ │ │ - * url - {String} The tileset URL scheme. │ │ │ │ - * options - {Object} Configuration options for the layer. Any inherited │ │ │ │ - * layer option can be set in this object (e.g. │ │ │ │ - * <OpenLayers.Layer.Grid.buffer>). │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); │ │ │ │ - this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ - crossOriginKeyword: 'anonymous' │ │ │ │ - }, this.options && this.options.tileOptions); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clone │ │ │ │ + * APIMethod: read │ │ │ │ + * Deserialize a json string. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * json - {String} A JSON string │ │ │ │ + * filter - {Function} A function which will be called for every key and │ │ │ │ + * value at every level of the final result. Each value will be │ │ │ │ + * replaced by the result of the filter function. This can be used to │ │ │ │ + * reform generic objects into instances of classes, or to transform │ │ │ │ + * date strings into Date objects. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An object, array, string, or number . │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.OSM( │ │ │ │ - this.name, this.url, this.getOptions()); │ │ │ │ - } │ │ │ │ - obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj; │ │ │ │ - }, │ │ │ │ + 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, ''))) { │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.OSM" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/SphericalMercator.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * 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 + ')'); │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer.js │ │ │ │ - * @requires OpenLayers/Projection.js │ │ │ │ - */ │ │ │ │ + if (this.keepData) { │ │ │ │ + this.data = object; │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.SphericalMercator │ │ │ │ - * A mixin for layers that wraps up the pieces neccesary to have a coordinate │ │ │ │ - * conversion for working with commercial APIs which use a spherical │ │ │ │ - * mercator projection. Using this layer as a base layer, additional │ │ │ │ - * layers can be used as overlays if they are in the same projection. │ │ │ │ - * │ │ │ │ - * A layer is given properties of this object by setting the sphericalMercator │ │ │ │ - * property to true. │ │ │ │ - * │ │ │ │ - * More projection information: │ │ │ │ - * - http://spatialreference.org/ref/user/google-projection/ │ │ │ │ - * │ │ │ │ - * Proj4 Text: │ │ │ │ - * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 │ │ │ │ - * +k=1.0 +units=m +nadgrids=@null +no_defs │ │ │ │ - * │ │ │ │ - * WKT: │ │ │ │ - * 900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84", │ │ │ │ - * DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]], │ │ │ │ - * PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295], │ │ │ │ - * AXIS["Longitude", EAST], AXIS["Latitude", NORTH]], │ │ │ │ - * PROJECTION["Mercator_1SP_Google"], │ │ │ │ - * PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0], │ │ │ │ - * PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0], │ │ │ │ - * PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST], │ │ │ │ - * AXIS["y", NORTH], AUTHORITY["EPSG","900913"]] │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.SphericalMercator = { │ │ │ │ + return object; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getExtent │ │ │ │ - * Get the map's extent. │ │ │ │ + * 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: │ │ │ │ - * {<OpenLayers.Bounds>} The map extent. │ │ │ │ + * {String} The JSON string representation of the input value. │ │ │ │ */ │ │ │ │ - getExtent: function() { │ │ │ │ - var extent = null; │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - extent = this.map.calculateBounds(); │ │ │ │ - } else { │ │ │ │ - extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this); │ │ │ │ + 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 extent; │ │ │ │ + return json; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getLonLatFromViewPortPx │ │ │ │ - * Get a map location from a pixel location │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * viewPortPx - {<OpenLayers.Pixel>} │ │ │ │ + * Method: writeIndent │ │ │ │ + * Output an indentation string depending on the indentation level. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view │ │ │ │ - * port OpenLayers.Pixel, translated into lon/lat by map lib │ │ │ │ - * If the map lib is not loaded or not centered, returns null │ │ │ │ + * {String} An appropriate indentation string. │ │ │ │ */ │ │ │ │ - getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ - return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments); │ │ │ │ + writeIndent: function() { │ │ │ │ + var pieces = []; │ │ │ │ + if (this.pretty) { │ │ │ │ + for (var i = 0; i < this.level; ++i) { │ │ │ │ + pieces.push(this.indent); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return pieces.join(''); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getViewPortPxFromLonLat │ │ │ │ - * Get a pixel location from a map location │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * lonlat - {<OpenLayers.LonLat>} │ │ │ │ + * Method: writeNewline │ │ │ │ + * Output a string representing a newline if in pretty printing mode. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in │ │ │ │ - * OpenLayers.LonLat, translated into view port pixels by map lib │ │ │ │ - * If map lib is not loaded or not centered, returns null │ │ │ │ - */ │ │ │ │ - getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ - return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: initMercatorParameters │ │ │ │ - * Set up the mercator parameters on the layer: resolutions, │ │ │ │ - * projection, units. │ │ │ │ + * {String} A string representing a new line. │ │ │ │ */ │ │ │ │ - initMercatorParameters: function() { │ │ │ │ - // set up properties for Mercator - assume EPSG:900913 │ │ │ │ - this.RESOLUTIONS = []; │ │ │ │ - var maxResolution = 156543.03390625; │ │ │ │ - for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) { │ │ │ │ - this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom); │ │ │ │ - } │ │ │ │ - this.units = "m"; │ │ │ │ - this.projection = this.projection || "EPSG:900913"; │ │ │ │ + writeNewline: function() { │ │ │ │ + return (this.pretty) ? this.newline : ''; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: forwardMercator │ │ │ │ - * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator. │ │ │ │ + * Method: writeSpace │ │ │ │ + * Output a string representing a space if in pretty printing mode. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * lon - {float} │ │ │ │ - * lat - {float} │ │ │ │ - * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} The coordinates transformed to Mercator. │ │ │ │ + * {String} A space. │ │ │ │ */ │ │ │ │ - forwardMercator: (function() { │ │ │ │ - var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ - var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ - return function(lon, lat) { │ │ │ │ - var point = OpenLayers.Projection.transform({ │ │ │ │ - x: lon, │ │ │ │ - y: lat │ │ │ │ - }, gg, sm); │ │ │ │ - return new OpenLayers.LonLat(point.x, point.y); │ │ │ │ - }; │ │ │ │ - })(), │ │ │ │ + writeSpace: function() { │ │ │ │ + return (this.pretty) ? this.space : ''; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: inverseMercator │ │ │ │ - * Given a x,y in Spherical Mercator, return a point in EPSG:4326. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * x - {float} A map x in Spherical Mercator. │ │ │ │ - * y - {float} A map y in Spherical Mercator. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326. │ │ │ │ + * Property: serialize │ │ │ │ + * Object with properties corresponding to the serializable data types. │ │ │ │ + * Property values are functions that do the actual serializing. │ │ │ │ */ │ │ │ │ - inverseMercator: (function() { │ │ │ │ - var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ - var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ - return function(x, y) { │ │ │ │ - var point = OpenLayers.Projection.transform({ │ │ │ │ - x: x, │ │ │ │ - y: y │ │ │ │ - }, sm, gg); │ │ │ │ - return new OpenLayers.LonLat(point.x, point.y); │ │ │ │ - }; │ │ │ │ - })() │ │ │ │ + serialize: { │ │ │ │ + /** │ │ │ │ + * Method: serialize.object │ │ │ │ + * Transform an object into a JSON string. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * object - {Object} The object to be serialized. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A JSON string representing the object. │ │ │ │ + */ │ │ │ │ + 'object': function(object) { │ │ │ │ + // three special objects that we want to treat differently │ │ │ │ + if (object == null) { │ │ │ │ + return "null"; │ │ │ │ + } │ │ │ │ + if (object.constructor == Date) { │ │ │ │ + return this.serialize.date.apply(this, [object]); │ │ │ │ + } │ │ │ │ + if (object.constructor == Array) { │ │ │ │ + return this.serialize.array.apply(this, [object]); │ │ │ │ + } │ │ │ │ + var pieces = ['{']; │ │ │ │ + this.level += 1; │ │ │ │ + var key, keyJSON, valueJSON; │ │ │ │ │ │ │ │ -}; │ │ │ │ + var addComma = false; │ │ │ │ + for (key in object) { │ │ │ │ + if (object.hasOwnProperty(key)) { │ │ │ │ + // recursive calls need to allow for sub-classing │ │ │ │ + keyJSON = OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ + [key, this.pretty]); │ │ │ │ + valueJSON = OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ + [object[key], this.pretty]); │ │ │ │ + if (keyJSON != null && valueJSON != null) { │ │ │ │ + if (addComma) { │ │ │ │ + pieces.push(','); │ │ │ │ + } │ │ │ │ + pieces.push(this.writeNewline(), this.writeIndent(), │ │ │ │ + keyJSON, ':', this.writeSpace(), valueJSON); │ │ │ │ + addComma = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.level -= 1; │ │ │ │ + pieces.push(this.writeNewline(), this.writeIndent(), '}'); │ │ │ │ + return pieces.join(''); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: serialize.array │ │ │ │ + * Transform an array into a JSON string. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * array - {Array} The array to be serialized │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A JSON string representing the array. │ │ │ │ + */ │ │ │ │ + 'array': function(array) { │ │ │ │ + var json; │ │ │ │ + var pieces = ['[']; │ │ │ │ + this.level += 1; │ │ │ │ + │ │ │ │ + for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ + // recursive calls need to allow for sub-classing │ │ │ │ + json = OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ + [array[i], this.pretty]); │ │ │ │ + if (json != null) { │ │ │ │ + if (i > 0) { │ │ │ │ + pieces.push(','); │ │ │ │ + } │ │ │ │ + pieces.push(this.writeNewline(), this.writeIndent(), json); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.level -= 1; │ │ │ │ + pieces.push(this.writeNewline(), this.writeIndent(), ']'); │ │ │ │ + return pieces.join(''); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: serialize.string │ │ │ │ + * Transform a string into a JSON string. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * string - {String} The string to be serialized │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A JSON string representing the string. │ │ │ │ + */ │ │ │ │ + 'string': function(string) { │ │ │ │ + // If the string contains no control characters, no quote characters, and no │ │ │ │ + // backslash characters, then we can simply slap some quotes around it. │ │ │ │ + // Otherwise we must also replace the offending characters with safe │ │ │ │ + // sequences. │ │ │ │ + var m = { │ │ │ │ + '\b': '\\b', │ │ │ │ + '\t': '\\t', │ │ │ │ + '\n': '\\n', │ │ │ │ + '\f': '\\f', │ │ │ │ + '\r': '\\r', │ │ │ │ + '"': '\\"', │ │ │ │ + '\\': '\\\\' │ │ │ │ + }; │ │ │ │ + if (/["\\\x00-\x1f]/.test(string)) { │ │ │ │ + return '"' + string.replace(/([\x00-\x1f\\"])/g, function(a, b) { │ │ │ │ + var c = m[b]; │ │ │ │ + if (c) { │ │ │ │ + return c; │ │ │ │ + } │ │ │ │ + c = b.charCodeAt(); │ │ │ │ + return '\\u00' + │ │ │ │ + Math.floor(c / 16).toString(16) + │ │ │ │ + (c % 16).toString(16); │ │ │ │ + }) + '"'; │ │ │ │ + } │ │ │ │ + return '"' + string + '"'; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: serialize.number │ │ │ │ + * Transform a number into a JSON string. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * number - {Number} The number to be serialized. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A JSON string representing the number. │ │ │ │ + */ │ │ │ │ + 'number': function(number) { │ │ │ │ + return isFinite(number) ? String(number) : "null"; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: serialize.boolean │ │ │ │ + * Transform a boolean into a JSON string. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bool - {Boolean} The boolean to be serialized. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A JSON string representing the boolean. │ │ │ │ + */ │ │ │ │ + 'boolean': function(bool) { │ │ │ │ + return String(bool); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: serialize.object │ │ │ │ + * Transform a date into a JSON string. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * date - {Date} The date to be serialized. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} A JSON string representing the date. │ │ │ │ + */ │ │ │ │ + 'date': function(date) { │ │ │ │ + function format(number) { │ │ │ │ + // Format integers to have at least two digits. │ │ │ │ + return (number < 10) ? '0' + number : number; │ │ │ │ + } │ │ │ │ + return '"' + date.getFullYear() + '-' + │ │ │ │ + format(date.getMonth() + 1) + '-' + │ │ │ │ + format(date.getDate()) + 'T' + │ │ │ │ + format(date.getHours()) + ':' + │ │ │ │ + format(date.getMinutes()) + ':' + │ │ │ │ + format(date.getSeconds()) + '"'; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Format.JSON" │ │ │ │ + │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Layer/EventPane.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/Layer.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.EventPane │ │ │ │ - * Base class for 3rd party layers, providing a DOM element which isolates │ │ │ │ - * the 3rd-party layer from mouse events. │ │ │ │ - * Only used by Google layers. │ │ │ │ - * │ │ │ │ - * Automatically instantiated by the Google constructor, and not usually instantiated directly. │ │ │ │ + * Class: OpenLayers.Geometry │ │ │ │ + * A Geometry is a description of a geographic object. Create an instance of │ │ │ │ + * this class with the <OpenLayers.Geometry> constructor. This is a base class, │ │ │ │ + * typical geometry types are described by subclasses of this class. │ │ │ │ * │ │ │ │ - * Create a new event pane layer with the │ │ │ │ - * <OpenLayers.Layer.EventPane> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer> │ │ │ │ + * Note that if you use the <OpenLayers.Geometry.fromWKT> method, you must │ │ │ │ + * explicitly include the OpenLayers.Format.WKT in your build. │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: smoothDragPan │ │ │ │ - * {Boolean} smoothDragPan determines whether non-public/internal API │ │ │ │ - * methods are used for better performance while dragging EventPane │ │ │ │ - * layers. When not in sphericalMercator mode, the smoother dragging │ │ │ │ - * doesn't actually move north/south directly with the number of │ │ │ │ - * pixels moved, resulting in a slight offset when you drag your mouse │ │ │ │ - * north south with this option on. If this visual disparity bothers │ │ │ │ - * you, you should turn this option off, or use spherical mercator. │ │ │ │ - * Default is on. │ │ │ │ - */ │ │ │ │ - smoothDragPan: true, │ │ │ │ +OpenLayers.Geometry = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: isBaseLayer │ │ │ │ - * {Boolean} EventPaned layers are always base layers, by necessity. │ │ │ │ + * Property: id │ │ │ │ + * {String} A unique identifier for this geometry. │ │ │ │ */ │ │ │ │ - isBaseLayer: true, │ │ │ │ + id: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: isFixed │ │ │ │ - * {Boolean} EventPaned layers are fixed by default. │ │ │ │ + * Property: parent │ │ │ │ + * {<OpenLayers.Geometry>}This is set when a Geometry is added as component │ │ │ │ + * of another geometry │ │ │ │ */ │ │ │ │ - isFixed: true, │ │ │ │ + parent: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: pane │ │ │ │ - * {DOMElement} A reference to the element that controls the events. │ │ │ │ + * Property: bounds │ │ │ │ + * {<OpenLayers.Bounds>} The bounds of this geometry │ │ │ │ */ │ │ │ │ - pane: null, │ │ │ │ - │ │ │ │ + bounds: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: mapObject │ │ │ │ - * {Object} This is the object which will be used to load the 3rd party library │ │ │ │ - * in the case of the google layer, this will be of type GMap, │ │ │ │ - * in the case of the ve layer, this will be of type VEMap │ │ │ │ + * Constructor: OpenLayers.Geometry │ │ │ │ + * Creates a geometry object. │ │ │ │ */ │ │ │ │ - mapObject: null, │ │ │ │ - │ │ │ │ + initialize: function() { │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Layer.EventPane │ │ │ │ - * Create a new event pane layer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} │ │ │ │ - * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ + * Method: destroy │ │ │ │ + * Destroy this geometry. │ │ │ │ */ │ │ │ │ - initialize: function(name, options) { │ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ - if (this.pane == null) { │ │ │ │ - this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane"); │ │ │ │ - } │ │ │ │ + destroy: function() { │ │ │ │ + this.id = null; │ │ │ │ + this.bounds = null; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Deconstruct this layer. │ │ │ │ + * APIMethod: clone │ │ │ │ + * Create a clone of this geometry. Does not set any non-standard │ │ │ │ + * properties of the cloned geometry. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} An exact clone of this geometry. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.mapObject = null; │ │ │ │ - this.pane = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ + clone: function() { │ │ │ │ + return new OpenLayers.Geometry(); │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ - * │ │ │ │ + * Method: setBounds │ │ │ │ + * Set the bounds for this Geometry. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ - │ │ │ │ - this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; │ │ │ │ - this.pane.style.display = this.div.style.display; │ │ │ │ - this.pane.style.width = "100%"; │ │ │ │ - this.pane.style.height = "100%"; │ │ │ │ - if (OpenLayers.BROWSER_NAME == "msie") { │ │ │ │ - this.pane.style.background = │ │ │ │ - "url(" + OpenLayers.Util.getImageLocation("blank.gif") + ")"; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.isFixed) { │ │ │ │ - this.map.viewPortDiv.appendChild(this.pane); │ │ │ │ - } else { │ │ │ │ - this.map.layerContainerDiv.appendChild(this.pane); │ │ │ │ + setBounds: function(bounds) { │ │ │ │ + if (bounds) { │ │ │ │ + this.bounds = bounds.clone(); │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // once our layer has been added to the map, we can load it │ │ │ │ - this.loadMapObject(); │ │ │ │ - │ │ │ │ - // if map didn't load, display warning │ │ │ │ - if (this.mapObject == null) { │ │ │ │ - this.loadWarningMessage(); │ │ │ │ + /** │ │ │ │ + * Method: clearBounds │ │ │ │ + * Nullify this components bounds and that of its parent as well. │ │ │ │ + */ │ │ │ │ + clearBounds: function() { │ │ │ │ + this.bounds = null; │ │ │ │ + if (this.parent) { │ │ │ │ + this.parent.clearBounds(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: removeMap │ │ │ │ - * On being removed from the map, we'll like to remove the invisible 'pane' │ │ │ │ - * div that we added to it on creation. │ │ │ │ + * 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: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * newBounds - {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - removeMap: function(map) { │ │ │ │ - if (this.pane && this.pane.parentNode) { │ │ │ │ - this.pane.parentNode.removeChild(this.pane); │ │ │ │ + extendBounds: function(newBounds) { │ │ │ │ + var bounds = this.getBounds(); │ │ │ │ + if (!bounds) { │ │ │ │ + this.setBounds(newBounds); │ │ │ │ + } else { │ │ │ │ + this.bounds.extend(newBounds); │ │ │ │ } │ │ │ │ - OpenLayers.Layer.prototype.removeMap.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: loadWarningMessage │ │ │ │ - * If we can't load the map lib, then display an error message to the │ │ │ │ - * user and tell them where to go for help. │ │ │ │ + * APIMethod: getBounds │ │ │ │ + * Get the bounds for this Geometry. If bounds is not set, it │ │ │ │ + * is calculated again, this makes queries faster. │ │ │ │ * │ │ │ │ - * This function sets up the layout for the warning message. Each 3rd │ │ │ │ - * party layer must implement its own getWarningHTML() function to │ │ │ │ - * provide the actual warning message. │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - loadWarningMessage: function() { │ │ │ │ - │ │ │ │ - this.div.style.backgroundColor = "darkblue"; │ │ │ │ - │ │ │ │ - var viewSize = this.map.getSize(); │ │ │ │ - │ │ │ │ - var msgW = Math.min(viewSize.w, 300); │ │ │ │ - var msgH = Math.min(viewSize.h, 200); │ │ │ │ - var size = new OpenLayers.Size(msgW, msgH); │ │ │ │ - │ │ │ │ - var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2); │ │ │ │ - │ │ │ │ - var topLeft = centerPx.add(-size.w / 2, -size.h / 2); │ │ │ │ - │ │ │ │ - var div = OpenLayers.Util.createDiv(this.name + "_warning", │ │ │ │ - topLeft, │ │ │ │ - size, │ │ │ │ - null, │ │ │ │ - null, │ │ │ │ - null, │ │ │ │ - "auto"); │ │ │ │ - │ │ │ │ - div.style.padding = "7px"; │ │ │ │ - div.style.backgroundColor = "yellow"; │ │ │ │ - │ │ │ │ - div.innerHTML = this.getWarningHTML(); │ │ │ │ - this.div.appendChild(div); │ │ │ │ + getBounds: function() { │ │ │ │ + if (this.bounds == null) { │ │ │ │ + this.calculateBounds(); │ │ │ │ + } │ │ │ │ + return this.bounds; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getWarningHTML │ │ │ │ - * To be implemented by subclasses. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} String with information on why layer is broken, how to get │ │ │ │ - * it working. │ │ │ │ + * APIMethod: calculateBounds │ │ │ │ + * Recalculate the bounds for the geometry. │ │ │ │ */ │ │ │ │ - getWarningHTML: function() { │ │ │ │ - //should be implemented by subclasses │ │ │ │ - return ""; │ │ │ │ + calculateBounds: function() { │ │ │ │ + // │ │ │ │ + // This should be overridden by subclasses. │ │ │ │ + // │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: display │ │ │ │ - * Set the display on the pane │ │ │ │ + * APIMethod: distanceTo │ │ │ │ + * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * display - {Boolean} │ │ │ │ - */ │ │ │ │ - display: function(display) { │ │ │ │ - OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ - this.pane.style.display = this.div.style.display; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setZIndex │ │ │ │ - * Set the z-index order for the pane. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ + * options - {Object} Optional properties for configuring the distance │ │ │ │ + * calculation. │ │ │ │ + * │ │ │ │ + * Valid options depend on the specific geometry type. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * zIndex - {int} │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - setZIndex: function(zIndex) { │ │ │ │ - OpenLayers.Layer.prototype.setZIndex.apply(this, arguments); │ │ │ │ - this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; │ │ │ │ - }, │ │ │ │ + distanceTo: function(geometry, options) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveByPx │ │ │ │ - * Move the layer based on pixel vector. To be implemented by subclasses. │ │ │ │ + * APIMethod: getVertices │ │ │ │ + * Return a list of all points in this geometry. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * dx - {Number} The x coord of the displacement vector. │ │ │ │ - * dy - {Number} The y coord of the displacement vector. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - moveByPx: function(dx, dy) { │ │ │ │ - OpenLayers.Layer.prototype.moveByPx.apply(this, arguments); │ │ │ │ - │ │ │ │ - if (this.dragPanMapObject) { │ │ │ │ - this.dragPanMapObject(dx, -dy); │ │ │ │ - } else { │ │ │ │ - this.moveTo(this.map.getCachedCenter()); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + getVertices: function(nodes) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveTo │ │ │ │ - * Handle calls to move the layer. │ │ │ │ + * Method: atPoint │ │ │ │ + * Note - This is only an approximation based on the bounds of the │ │ │ │ + * geometry. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * zoomChanged - {Boolean} │ │ │ │ - * dragging - {Boolean} │ │ │ │ + * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an │ │ │ │ + * object with a 'lon' and 'lat' properties. │ │ │ │ + * toleranceLon - {float} Optional tolerance in Geometric Coords │ │ │ │ + * toleranceLat - {float} Optional tolerance in Geographic Coords │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Whether or not the geometry is at the specified location │ │ │ │ */ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ - │ │ │ │ - if (this.mapObject != null) { │ │ │ │ - │ │ │ │ - var newCenter = this.map.getCenter(); │ │ │ │ - var newZoom = this.map.getZoom(); │ │ │ │ - │ │ │ │ - if (newCenter != null) { │ │ │ │ - │ │ │ │ - var moOldCenter = this.getMapObjectCenter(); │ │ │ │ - var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter); │ │ │ │ + atPoint: function(lonlat, toleranceLon, toleranceLat) { │ │ │ │ + var atPoint = false; │ │ │ │ + var bounds = this.getBounds(); │ │ │ │ + if ((bounds != null) && (lonlat != null)) { │ │ │ │ │ │ │ │ - var moOldZoom = this.getMapObjectZoom(); │ │ │ │ - var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom); │ │ │ │ + var dX = (toleranceLon != null) ? toleranceLon : 0; │ │ │ │ + var dY = (toleranceLat != null) ? toleranceLat : 0; │ │ │ │ │ │ │ │ - if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) { │ │ │ │ + var toleranceBounds = │ │ │ │ + new OpenLayers.Bounds(this.bounds.left - dX, │ │ │ │ + this.bounds.bottom - dY, │ │ │ │ + this.bounds.right + dX, │ │ │ │ + this.bounds.top + dY); │ │ │ │ │ │ │ │ - if (!zoomChanged && oldCenter && this.dragPanMapObject && │ │ │ │ - this.smoothDragPan) { │ │ │ │ - var oldPx = this.map.getViewPortPxFromLonLat(oldCenter); │ │ │ │ - var newPx = this.map.getViewPortPxFromLonLat(newCenter); │ │ │ │ - this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y); │ │ │ │ - } else { │ │ │ │ - var center = this.getMapObjectLonLatFromOLLonLat(newCenter); │ │ │ │ - var zoom = this.getMapObjectZoomFromOLZoom(newZoom); │ │ │ │ - this.setMapObjectCenter(center, zoom, dragging); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + atPoint = toleranceBounds.containsLonLat(lonlat); │ │ │ │ } │ │ │ │ + return atPoint; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ - /********************************************************/ │ │ │ │ - /* */ │ │ │ │ - /* Baselayer Functions */ │ │ │ │ - /* */ │ │ │ │ - /********************************************************/ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: getLonLatFromViewPortPx │ │ │ │ - * Get a map location from a pixel location │ │ │ │ + * Method: getLength │ │ │ │ + * Calculate the length of this geometry. This method is defined in │ │ │ │ + * subclasses. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * viewPortPx - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view │ │ │ │ - * port OpenLayers.Pixel, translated into lon/lat by map lib │ │ │ │ - * If the map lib is not loaded or not centered, returns null │ │ │ │ + * {Float} The length of the collection by summing its parts │ │ │ │ */ │ │ │ │ - getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ - var lonlat = null; │ │ │ │ - if ((this.mapObject != null) && │ │ │ │ - (this.getMapObjectCenter() != null)) { │ │ │ │ - var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx); │ │ │ │ - var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel); │ │ │ │ - lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat); │ │ │ │ - } │ │ │ │ - return lonlat; │ │ │ │ + getLength: function() { │ │ │ │ + //to be overridden by geometries that actually have a length │ │ │ │ + // │ │ │ │ + return 0.0; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: getViewPortPxFromLonLat │ │ │ │ - * Get a pixel location from a map location │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * lonlat - {<OpenLayers.LonLat>} │ │ │ │ - * │ │ │ │ + * Method: getArea │ │ │ │ + * Calculate the area of this geometry. This method is defined in subclasses. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in │ │ │ │ - * OpenLayers.LonLat, translated into view port pixels by map lib │ │ │ │ - * If map lib is not loaded or not centered, returns null │ │ │ │ + * {Float} The area of the collection by summing its parts │ │ │ │ */ │ │ │ │ - getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ - var viewPortPx = null; │ │ │ │ - if ((this.mapObject != null) && │ │ │ │ - (this.getMapObjectCenter() != null)) { │ │ │ │ - │ │ │ │ - var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat); │ │ │ │ - var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat); │ │ │ │ - │ │ │ │ - viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel); │ │ │ │ - } │ │ │ │ - return viewPortPx; │ │ │ │ + getArea: function() { │ │ │ │ + //to be overridden by geometries that actually have an area │ │ │ │ + // │ │ │ │ + return 0.0; │ │ │ │ }, │ │ │ │ │ │ │ │ - /********************************************************/ │ │ │ │ - /* */ │ │ │ │ - /* Translation Functions */ │ │ │ │ - /* */ │ │ │ │ - /* The following functions translate Map Object and */ │ │ │ │ - /* OL formats for Pixel, LonLat */ │ │ │ │ - /* */ │ │ │ │ - /********************************************************/ │ │ │ │ - │ │ │ │ - // │ │ │ │ - // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat │ │ │ │ - // │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: getOLLonLatFromMapObjectLonLat │ │ │ │ - * Get an OL style map location from a 3rd party style map location │ │ │ │ + * APIMethod: getCentroid │ │ │ │ + * Calculate the centroid of this geometry. This method is defined in subclasses. │ │ │ │ * │ │ │ │ - * Parameters │ │ │ │ - * moLonLat - {Object} │ │ │ │ - * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in │ │ │ │ - * MapObject LonLat │ │ │ │ - * Returns null if null value is passed in │ │ │ │ + * {<OpenLayers.Geometry.Point>} The centroid of the collection │ │ │ │ */ │ │ │ │ - getOLLonLatFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - var olLonLat = null; │ │ │ │ - if (moLonLat != null) { │ │ │ │ - var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - olLonLat = new OpenLayers.LonLat(lon, lat); │ │ │ │ - } │ │ │ │ - return olLonLat; │ │ │ │ + getCentroid: function() { │ │ │ │ + return null; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getMapObjectLonLatFromOLLonLat │ │ │ │ - * Get a 3rd party map location from an OL map location. │ │ │ │ + * 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. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * olLonLat - {<OpenLayers.LonLat>} │ │ │ │ - * │ │ │ │ * Returns: │ │ │ │ - * {Object} A MapObject LonLat, translated from the passed in │ │ │ │ - * OpenLayers.LonLat │ │ │ │ - * Returns null if null value is passed in │ │ │ │ + * {String} String representation of this geometry. │ │ │ │ */ │ │ │ │ - getMapObjectLonLatFromOLLonLat: function(olLonLat) { │ │ │ │ - var moLatLng = null; │ │ │ │ - if (olLonLat != null) { │ │ │ │ - moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, │ │ │ │ - olLonLat.lat); │ │ │ │ + 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 moLatLng; │ │ │ │ + return string; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ - // │ │ │ │ - // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel │ │ │ │ - // │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getOLPixelFromMapObjectPixel │ │ │ │ - * Get an OL pixel location from a 3rd party pixel location. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moPixel - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in │ │ │ │ - * MapObject Pixel │ │ │ │ - * Returns null if null value is passed in │ │ │ │ - */ │ │ │ │ - getOLPixelFromMapObjectPixel: function(moPixel) { │ │ │ │ - var olPixel = null; │ │ │ │ - if (moPixel != null) { │ │ │ │ - var x = this.getXFromMapObjectPixel(moPixel); │ │ │ │ - var y = this.getYFromMapObjectPixel(moPixel); │ │ │ │ - olPixel = new OpenLayers.Pixel(x, y); │ │ │ │ - } │ │ │ │ - return olPixel; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getMapObjectPixelFromOLPixel │ │ │ │ - * Get a 3rd party pixel location from an OL pixel location │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * olPixel - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} A MapObject Pixel, translated from the passed in │ │ │ │ - * OpenLayers.Pixel │ │ │ │ - * Returns null if null value is passed in │ │ │ │ - */ │ │ │ │ - getMapObjectPixelFromOLPixel: function(olPixel) { │ │ │ │ - var moPixel = null; │ │ │ │ - if (olPixel != null) { │ │ │ │ - moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y); │ │ │ │ - } │ │ │ │ - return moPixel; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.EventPane" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/FixedZoomLevels.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry" │ │ │ │ +}); │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Layer.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.FixedZoomLevels │ │ │ │ - * Some Layers will already have established zoom levels (like google │ │ │ │ - * or ve). Instead of trying to determine them and populate a resolutions[] │ │ │ │ - * Array with those values, we will hijack the resolution functionality │ │ │ │ - * here. │ │ │ │ - * │ │ │ │ - * When you subclass FixedZoomLevels: │ │ │ │ - * │ │ │ │ - * The initResolutions() call gets nullified, meaning no resolutions[] array │ │ │ │ - * is set up. Which would be a big problem getResolution() in Layer, since │ │ │ │ - * it merely takes map.zoom and indexes into resolutions[]... but.... │ │ │ │ - * │ │ │ │ - * The getResolution() call is also overridden. Instead of using the │ │ │ │ - * resolutions[] array, we simply calculate the current resolution based │ │ │ │ - * on the current extent and the current map size. But how will we be able │ │ │ │ - * to calculate the current extent without knowing the resolution...? │ │ │ │ - * │ │ │ │ - * The getExtent() function is also overridden. Instead of calculating extent │ │ │ │ - * based on the center point and the current resolution, we instead │ │ │ │ - * calculate the extent by getting the lonlats at the top-left and │ │ │ │ - * bottom-right by using the getLonLatFromViewPortPx() translation function, │ │ │ │ - * taken from the pixel locations (0,0) and the size of the map. But how │ │ │ │ - * will we be able to do lonlat-px translation without resolution....? │ │ │ │ - * │ │ │ │ - * The getZoomForResolution() method is overridden. Instead of indexing into │ │ │ │ - * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in │ │ │ │ - * the desired resolution. With this extent, we then call getZoomForExtent() │ │ │ │ - * │ │ │ │ - * │ │ │ │ - * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels, │ │ │ │ - * it is your responsibility to provide the following three functions: │ │ │ │ - * │ │ │ │ - * - getLonLatFromViewPortPx │ │ │ │ - * - getViewPortPxFromLonLat │ │ │ │ - * - getZoomForExtent │ │ │ │ - * │ │ │ │ - * ...those three functions should generally be provided by any reasonable │ │ │ │ - * API that you might be working from. │ │ │ │ + * Function: OpenLayers.Geometry.fromWKT │ │ │ │ + * Generate a geometry given a Well-Known Text string. For this method to │ │ │ │ + * work, you must include the OpenLayers.Format.WKT in your build │ │ │ │ + * explicitly. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * wkt - {String} A string representing the geometry in Well-Known Text. │ │ │ │ * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} A geometry of the appropriate class. │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /********************************************************/ │ │ │ │ - /* */ │ │ │ │ - /* Baselayer Functions */ │ │ │ │ - /* */ │ │ │ │ - /* The following functions must all be implemented */ │ │ │ │ - /* by all base layers */ │ │ │ │ - /* */ │ │ │ │ - /********************************************************/ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Layer.FixedZoomLevels │ │ │ │ - * Create a new fixed zoom levels layer. │ │ │ │ - */ │ │ │ │ - initialize: function() { │ │ │ │ - //this class is only just to add the following functions... │ │ │ │ - // nothing to actually do here... but it is probably a good │ │ │ │ - // idea to have layers that use these functions call this │ │ │ │ - // inititalize() anyways, in case at some point we decide we │ │ │ │ - // do want to put some functionality or state in here. │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: initResolutions │ │ │ │ - * Populate the resolutions array │ │ │ │ - */ │ │ │ │ - initResolutions: function() { │ │ │ │ - │ │ │ │ - var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels']; │ │ │ │ - │ │ │ │ - for (var i = 0, len = props.length; i < len; i++) { │ │ │ │ - var property = props[i]; │ │ │ │ - this[property] = (this.options[property] != null) ? │ │ │ │ - this.options[property] : │ │ │ │ - this.map[property]; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if ((this.minZoomLevel == null) || │ │ │ │ - (this.minZoomLevel < this.MIN_ZOOM_LEVEL)) { │ │ │ │ - this.minZoomLevel = this.MIN_ZOOM_LEVEL; │ │ │ │ +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; │ │ │ │ } │ │ │ │ - │ │ │ │ - // │ │ │ │ - // At this point, we know what the minimum desired zoom level is, and │ │ │ │ - // we must calculate the total number of zoom levels. │ │ │ │ - // │ │ │ │ - // Because we allow for the setting of either the 'numZoomLevels' │ │ │ │ - // or the 'maxZoomLevel' properties... on either the layer or the │ │ │ │ - // map, we have to define some rules to see which we take into │ │ │ │ - // account first in this calculation. │ │ │ │ - // │ │ │ │ - // The following is the precedence list for these properties: │ │ │ │ - // │ │ │ │ - // (1) numZoomLevels set on layer │ │ │ │ - // (2) maxZoomLevel set on layer │ │ │ │ - // (3) numZoomLevels set on map │ │ │ │ - // (4) maxZoomLevel set on map* │ │ │ │ - // (5) none of the above* │ │ │ │ - // │ │ │ │ - // *Note that options (4) and (5) are only possible if the user │ │ │ │ - // _explicitly_ sets the 'numZoomLevels' property on the map to │ │ │ │ - // null, since it is set by default to 16. │ │ │ │ - // │ │ │ │ - │ │ │ │ - // │ │ │ │ - // Note to future: In 3.0, I think we should remove the default │ │ │ │ - // value of 16 for map.numZoomLevels. Rather, I think that value │ │ │ │ - // should be set as a default on the Layer.WMS class. If someone │ │ │ │ - // creates a 3rd party layer and does not specify any 'minZoomLevel', │ │ │ │ - // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly │ │ │ │ - // specified any of those on the map object either.. then I think │ │ │ │ - // it is fair to say that s/he wants all the zoom levels available. │ │ │ │ - // │ │ │ │ - // By making map.numZoomLevels *null* by default, that will be the │ │ │ │ - // case. As it is, I don't feel comfortable changing that right now │ │ │ │ - // as it would be a glaring API change and actually would probably │ │ │ │ - // break many peoples' codes. │ │ │ │ - // │ │ │ │ - │ │ │ │ - //the number of zoom levels we'd like to have. │ │ │ │ - var desiredZoomLevels; │ │ │ │ - │ │ │ │ - //this is the maximum number of zoom levels the layer will allow, │ │ │ │ - // given the specified starting minimum zoom level. │ │ │ │ - var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1; │ │ │ │ - │ │ │ │ - if (((this.options.numZoomLevels == null) && │ │ │ │ - (this.options.maxZoomLevel != null)) // (2) │ │ │ │ - || │ │ │ │ - ((this.numZoomLevels == null) && │ │ │ │ - (this.maxZoomLevel != null)) // (4) │ │ │ │ - ) { │ │ │ │ - //calculate based on specified maxZoomLevel (on layer or map) │ │ │ │ - desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1; │ │ │ │ - } else { │ │ │ │ - //calculate based on specified numZoomLevels (on layer or map) │ │ │ │ - // this covers cases (1) and (3) │ │ │ │ - desiredZoomLevels = this.numZoomLevels; │ │ │ │ + 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; │ │ │ │ +}; │ │ │ │ │ │ │ │ - if (desiredZoomLevels != null) { │ │ │ │ - //Now that we know what we would *like* the number of zoom levels │ │ │ │ - // to be, based on layer or map options, we have to make sure that │ │ │ │ - // it does not conflict with the actual limit, as specified by │ │ │ │ - // the constants on the layer itself (and calculated into the │ │ │ │ - // 'limitZoomLevels' variable). │ │ │ │ - this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels); │ │ │ │ - } else { │ │ │ │ - // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was │ │ │ │ - // set on either the layer or the map. So we just use the │ │ │ │ - // maximum limit as calculated by the layer's constants. │ │ │ │ - this.numZoomLevels = limitZoomLevels; │ │ │ │ +/** │ │ │ │ + * Method: OpenLayers.Geometry.segmentsIntersect │ │ │ │ + * Determine whether two line segments intersect. Optionally calculates │ │ │ │ + * and returns the intersection point. This function is optimized for │ │ │ │ + * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those │ │ │ │ + * obvious cases where there is no intersection, the function should │ │ │ │ + * not be called. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * seg1 - {Object} Object representing a segment with properties x1, y1, x2, │ │ │ │ + * and y2. The start point is represented by x1 and y1. The end point │ │ │ │ + * is represented by x2 and y2. Start and end are ordered so that x1 < x2. │ │ │ │ + * seg2 - {Object} Object representing a segment with properties x1, y1, x2, │ │ │ │ + * and y2. The start point is represented by x1 and y1. The end point │ │ │ │ + * is represented by x2 and y2. Start and end are ordered so that x1 < x2. │ │ │ │ + * options - {Object} Optional properties for calculating the intersection. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * point - {Boolean} Return the intersection point. If false, the actual │ │ │ │ + * intersection point will not be calculated. If true and the segments │ │ │ │ + * intersect, the intersection point will be returned. If true and │ │ │ │ + * the segments do not intersect, false will be returned. If true and │ │ │ │ + * the segments are coincident, true will be returned. │ │ │ │ + * tolerance - {Number} If a non-null value is provided, if the segments are │ │ │ │ + * within the tolerance distance, this will be considered an intersection. │ │ │ │ + * In addition, if the point option is true and the calculated intersection │ │ │ │ + * is within the tolerance distance of an end point, the endpoint will be │ │ │ │ + * returned instead of the calculated intersection. Further, if the │ │ │ │ + * intersection is within the tolerance of endpoints on both segments, or │ │ │ │ + * if two segment endpoints are within the tolerance distance of eachother │ │ │ │ + * (but no intersection is otherwise calculated), an endpoint on the │ │ │ │ + * first segment provided will be returned. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect. │ │ │ │ + * If the point argument is true, the return will be the intersection │ │ │ │ + * point or false if none exists. If point is true and the segments │ │ │ │ + * are coincident, return will be true (and the instersection is equal │ │ │ │ + * to the shorter segment). │ │ │ │ + */ │ │ │ │ +OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) { │ │ │ │ + var point = options && options.point; │ │ │ │ + var tolerance = options && options.tolerance; │ │ │ │ + var intersection = false; │ │ │ │ + var x11_21 = seg1.x1 - seg2.x1; │ │ │ │ + var y11_21 = seg1.y1 - seg2.y1; │ │ │ │ + var x12_11 = seg1.x2 - seg1.x1; │ │ │ │ + var y12_11 = seg1.y2 - seg1.y1; │ │ │ │ + var y22_21 = seg2.y2 - seg2.y1; │ │ │ │ + var x22_21 = seg2.x2 - seg2.x1; │ │ │ │ + var d = (y22_21 * x12_11) - (x22_21 * y12_11); │ │ │ │ + var n1 = (x22_21 * y11_21) - (y22_21 * x11_21); │ │ │ │ + var n2 = (x12_11 * y11_21) - (y12_11 * x11_21); │ │ │ │ + if (d == 0) { │ │ │ │ + // parallel │ │ │ │ + if (n1 == 0 && n2 == 0) { │ │ │ │ + // coincident │ │ │ │ + intersection = true; │ │ │ │ } │ │ │ │ - │ │ │ │ - //now that the 'numZoomLevels' is appropriately, safely set, │ │ │ │ - // we go back and re-calculate the 'maxZoomLevel'. │ │ │ │ - this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1; │ │ │ │ - │ │ │ │ - if (this.RESOLUTIONS != null) { │ │ │ │ - var resolutionsIndex = 0; │ │ │ │ - this.resolutions = []; │ │ │ │ - for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) { │ │ │ │ - this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i]; │ │ │ │ + } 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); │ │ │ │ } │ │ │ │ - this.maxResolution = this.resolutions[0]; │ │ │ │ - this.minResolution = this.resolutions[this.resolutions.length - 1]; │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getResolution │ │ │ │ - * Get the current map resolution │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Float} Map units per Pixel │ │ │ │ - */ │ │ │ │ - getResolution: function() { │ │ │ │ - │ │ │ │ - if (this.resolutions != null) { │ │ │ │ - return OpenLayers.Layer.prototype.getResolution.apply(this, arguments); │ │ │ │ - } else { │ │ │ │ - var resolution = null; │ │ │ │ - │ │ │ │ - var viewSize = this.map.getSize(); │ │ │ │ - var extent = this.getExtent(); │ │ │ │ + } │ │ │ │ + if (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; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - if ((viewSize != null) && (extent != null)) { │ │ │ │ - resolution = Math.max(extent.getWidth() / viewSize.w, │ │ │ │ - extent.getHeight() / viewSize.h); │ │ │ │ } │ │ │ │ - return resolution; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getExtent │ │ │ │ - * Calculates using px-> lonlat translation functions on tl and br │ │ │ │ - * corners of viewport │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat │ │ │ │ - * bounds of the current viewPort. │ │ │ │ - */ │ │ │ │ - getExtent: function() { │ │ │ │ - var size = this.map.getSize(); │ │ │ │ - var tl = this.getLonLatFromViewPortPx({ │ │ │ │ - x: 0, │ │ │ │ - y: 0 │ │ │ │ - }); │ │ │ │ - var br = this.getLonLatFromViewPortPx({ │ │ │ │ - x: size.w, │ │ │ │ - y: size.h │ │ │ │ - }); │ │ │ │ - │ │ │ │ - if ((tl != null) && (br != null)) { │ │ │ │ - return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat); │ │ │ │ } else { │ │ │ │ - return null; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getZoomForResolution │ │ │ │ - * Get the zoom level for a given resolution │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * resolution - {Float} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} A suitable zoom level for the specified resolution. │ │ │ │ - * If no baselayer is set, returns null. │ │ │ │ - */ │ │ │ │ - getZoomForResolution: function(resolution) { │ │ │ │ - │ │ │ │ - if (this.resolutions != null) { │ │ │ │ - return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments); │ │ │ │ - } else { │ │ │ │ - var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []); │ │ │ │ - return this.getZoomForExtent(extent); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - │ │ │ │ - │ │ │ │ - /********************************************************/ │ │ │ │ - /* */ │ │ │ │ - /* Translation Functions */ │ │ │ │ - /* */ │ │ │ │ - /* The following functions translate GMaps and OL */ │ │ │ │ - /* formats for Pixel, LonLat, Bounds, and Zoom */ │ │ │ │ - /* */ │ │ │ │ - /********************************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // │ │ │ │ - // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom │ │ │ │ - // │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getOLZoomFromMapObjectZoom │ │ │ │ - * Get the OL zoom index from the map object zoom level │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moZoom - {Integer} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} An OpenLayers Zoom level, translated from the passed in zoom │ │ │ │ - * Returns null if null value is passed in │ │ │ │ - */ │ │ │ │ - getOLZoomFromMapObjectZoom: function(moZoom) { │ │ │ │ - var zoom = null; │ │ │ │ - if (moZoom != null) { │ │ │ │ - zoom = moZoom - this.minZoomLevel; │ │ │ │ - if (this.map.baseLayer !== this) { │ │ │ │ - zoom = this.map.baseLayer.getZoomForResolution( │ │ │ │ - this.getResolutionForZoom(zoom) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return zoom; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getMapObjectZoomFromOLZoom │ │ │ │ - * Get the map object zoom level from the OL zoom level │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * olZoom - {Integer} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} A MapObject level, translated from the passed in olZoom │ │ │ │ - * Returns null if null value is passed in │ │ │ │ - */ │ │ │ │ - getMapObjectZoomFromOLZoom: function(olZoom) { │ │ │ │ - var zoom = null; │ │ │ │ - if (olZoom != null) { │ │ │ │ - zoom = olZoom + this.minZoomLevel; │ │ │ │ - if (this.map.baseLayer !== this) { │ │ │ │ - zoom = this.getZoomForResolution( │ │ │ │ - this.map.baseLayer.getResolutionForZoom(zoom) │ │ │ │ - ); │ │ │ │ + // 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 zoom; │ │ │ │ - }, │ │ │ │ + } │ │ │ │ + return intersection; │ │ │ │ +}; │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels" │ │ │ │ -}); │ │ │ │ +/** │ │ │ │ + * 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/Layer/Google.js │ │ │ │ + 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/Layer/SphericalMercator.js │ │ │ │ - * @requires OpenLayers/Layer/EventPane.js │ │ │ │ - * @requires OpenLayers/Layer/FixedZoomLevels.js │ │ │ │ - * @requires OpenLayers/Lang.js │ │ │ │ + * @requires OpenLayers/Geometry.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Layer.Google │ │ │ │ - * │ │ │ │ - * Provides a wrapper for Google's Maps API │ │ │ │ - * Normally the Terms of Use for this API do not allow wrapping, but Google │ │ │ │ - * have provided written consent to OpenLayers for this - see email in │ │ │ │ - * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html │ │ │ │ + * Class: OpenLayers.Geometry.Point │ │ │ │ + * Point geometry class. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.SphericalMercator> │ │ │ │ - * - <OpenLayers.Layer.EventPane> │ │ │ │ - * - <OpenLayers.Layer.FixedZoomLevels> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Google = OpenLayers.Class( │ │ │ │ - OpenLayers.Layer.EventPane, │ │ │ │ - OpenLayers.Layer.FixedZoomLevels, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: MIN_ZOOM_LEVEL │ │ │ │ - * {Integer} 0 │ │ │ │ - */ │ │ │ │ - MIN_ZOOM_LEVEL: 0, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: MAX_ZOOM_LEVEL │ │ │ │ - * {Integer} 21 │ │ │ │ - */ │ │ │ │ - MAX_ZOOM_LEVEL: 21, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: RESOLUTIONS │ │ │ │ - * {Array(Float)} Hardcode these resolutions so that they are more closely │ │ │ │ - * tied with the standard wms projection │ │ │ │ - */ │ │ │ │ - RESOLUTIONS: [ │ │ │ │ - 1.40625, │ │ │ │ - 0.703125, │ │ │ │ - 0.3515625, │ │ │ │ - 0.17578125, │ │ │ │ - 0.087890625, │ │ │ │ - 0.0439453125, │ │ │ │ - 0.02197265625, │ │ │ │ - 0.010986328125, │ │ │ │ - 0.0054931640625, │ │ │ │ - 0.00274658203125, │ │ │ │ - 0.001373291015625, │ │ │ │ - 0.0006866455078125, │ │ │ │ - 0.00034332275390625, │ │ │ │ - 0.000171661376953125, │ │ │ │ - 0.0000858306884765625, │ │ │ │ - 0.00004291534423828125, │ │ │ │ - 0.00002145767211914062, │ │ │ │ - 0.00001072883605957031, │ │ │ │ - 0.00000536441802978515, │ │ │ │ - 0.00000268220901489257, │ │ │ │ - 0.0000013411045074462891, │ │ │ │ - 0.00000067055225372314453 │ │ │ │ - ], │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: type │ │ │ │ - * {GMapType} │ │ │ │ - */ │ │ │ │ - type: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: wrapDateLine │ │ │ │ - * {Boolean} Allow user to pan forever east/west. Default is true. │ │ │ │ - * Setting this to false only restricts panning if │ │ │ │ - * <sphericalMercator> is true. │ │ │ │ - */ │ │ │ │ - wrapDateLine: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: sphericalMercator │ │ │ │ - * {Boolean} Should the map act as a mercator-projected map? This will │ │ │ │ - * cause all interactions with the map to be in the actual map │ │ │ │ - * projection, which allows support for vector drawing, overlaying │ │ │ │ - * other maps, etc. │ │ │ │ - */ │ │ │ │ - sphericalMercator: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: version │ │ │ │ - * {Number} The version of the Google Maps API │ │ │ │ - */ │ │ │ │ - version: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Layer.Google │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} A name for the layer. │ │ │ │ - * options - {Object} An optional object whose properties will be set │ │ │ │ - * on the layer. │ │ │ │ - */ │ │ │ │ - initialize: function(name, options) { │ │ │ │ - options = options || {}; │ │ │ │ - if (!options.version) { │ │ │ │ - options.version = typeof GMap2 === "function" ? "2" : "3"; │ │ │ │ - } │ │ │ │ - var mixin = OpenLayers.Layer.Google["v" + │ │ │ │ - options.version.replace(/\./g, "_")]; │ │ │ │ - if (mixin) { │ │ │ │ - OpenLayers.Util.applyDefaults(options, mixin); │ │ │ │ - } else { │ │ │ │ - throw "Unsupported Google Maps API version: " + options.version; │ │ │ │ - } │ │ │ │ - │ │ │ │ - OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS); │ │ │ │ - if (options.maxExtent) { │ │ │ │ - options.maxExtent = options.maxExtent.clone(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - OpenLayers.Layer.EventPane.prototype.initialize.apply(this, │ │ │ │ - [name, options]); │ │ │ │ - OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, │ │ │ │ - [name, options]); │ │ │ │ - │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator); │ │ │ │ - this.initMercatorParameters(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: clone │ │ │ │ - * Create a clone of this layer │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.Google>} An exact clone of this layer │ │ │ │ - */ │ │ │ │ - clone: function() { │ │ │ │ - /** │ │ │ │ - * This method isn't intended to be called by a subclass and it │ │ │ │ - * doesn't call the same method on the superclass. We don't call │ │ │ │ - * the super's clone because we don't want properties that are set │ │ │ │ - * on this layer after initialize (i.e. this.mapObject etc.). │ │ │ │ - */ │ │ │ │ - return new OpenLayers.Layer.Google( │ │ │ │ - this.name, this.getOptions() │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setVisibility │ │ │ │ - * Set the visibility flag for the layer and hide/show & redraw │ │ │ │ - * accordingly. Fire event unless otherwise specified │ │ │ │ - * │ │ │ │ - * Note that visibility is no longer simply whether or not the layer's │ │ │ │ - * style.display is set to "block". Now we store a 'visibility' state │ │ │ │ - * property on the layer class, this allows us to remember whether or │ │ │ │ - * not we *desire* for a layer to be visible. In the case where the │ │ │ │ - * map's resolution is out of the layer's range, this desire may be │ │ │ │ - * subverted. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * visible - {Boolean} Display the layer (if in range) │ │ │ │ - */ │ │ │ │ - setVisibility: function(visible) { │ │ │ │ - // sharing a map container, opacity has to be set per layer │ │ │ │ - var opacity = this.opacity == null ? 1 : this.opacity; │ │ │ │ - OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments); │ │ │ │ - this.setOpacity(opacity); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: display │ │ │ │ - * Hide or show the Layer │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * visible - {Boolean} │ │ │ │ - */ │ │ │ │ - display: function(visible) { │ │ │ │ - if (!this._dragging) { │ │ │ │ - this.setGMapVisibility(visible); │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to │ │ │ │ - * do some init work in that case. │ │ │ │ - * dragging - {Boolean} │ │ │ │ - */ │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - this._dragging = dragging; │ │ │ │ - OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments); │ │ │ │ - delete this._dragging; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setOpacity │ │ │ │ - * Sets the opacity for the entire layer (all images) │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * opacity - {Float} │ │ │ │ - */ │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ - if (opacity !== this.opacity) { │ │ │ │ - if (this.map != null) { │ │ │ │ - this.map.events.triggerEvent("changelayer", { │ │ │ │ - layer: this, │ │ │ │ - property: "opacity" │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - this.opacity = opacity; │ │ │ │ - } │ │ │ │ - // Though this layer's opacity may not change, we're sharing a container │ │ │ │ - // and need to update the opacity for the entire container. │ │ │ │ - if (this.getVisibility()) { │ │ │ │ - var container = this.getMapContainer(); │ │ │ │ - OpenLayers.Util.modifyDOMElement( │ │ │ │ - container, null, null, null, null, null, null, opacity │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Clean up this layer. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - /** │ │ │ │ - * We have to override this method because the event pane destroy │ │ │ │ - * deletes the mapObject reference before removing this layer from │ │ │ │ - * the map. │ │ │ │ - */ │ │ │ │ - if (this.map) { │ │ │ │ - this.setGMapVisibility(false); │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache && cache.count <= 1) { │ │ │ │ - this.removeGMapElements(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: removeGMapElements │ │ │ │ - * Remove all elements added to the dom. This should only be called if │ │ │ │ - * this is the last of the Google layers for the given map. │ │ │ │ - */ │ │ │ │ - removeGMapElements: function() { │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - // remove shared elements from dom │ │ │ │ - var container = this.mapObject && this.getMapContainer(); │ │ │ │ - if (container && container.parentNode) { │ │ │ │ - container.parentNode.removeChild(container); │ │ │ │ - } │ │ │ │ - var termsOfUse = cache.termsOfUse; │ │ │ │ - if (termsOfUse && termsOfUse.parentNode) { │ │ │ │ - termsOfUse.parentNode.removeChild(termsOfUse); │ │ │ │ - } │ │ │ │ - var poweredBy = cache.poweredBy; │ │ │ │ - if (poweredBy && poweredBy.parentNode) { │ │ │ │ - poweredBy.parentNode.removeChild(poweredBy); │ │ │ │ - } │ │ │ │ - if (this.mapObject && window.google && google.maps && │ │ │ │ - google.maps.event && google.maps.event.clearListeners) { │ │ │ │ - google.maps.event.clearListeners(this.mapObject, 'tilesloaded'); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: removeMap │ │ │ │ - * On being removed from the map, also remove termsOfUse and poweredBy divs │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - removeMap: function(map) { │ │ │ │ - // hide layer before removing │ │ │ │ - if (this.visibility && this.mapObject) { │ │ │ │ - this.setGMapVisibility(false); │ │ │ │ - } │ │ │ │ - // check to see if last Google layer in this map │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[map.id]; │ │ │ │ - if (cache) { │ │ │ │ - if (cache.count <= 1) { │ │ │ │ - this.removeGMapElements(); │ │ │ │ - delete OpenLayers.Layer.Google.cache[map.id]; │ │ │ │ - } else { │ │ │ │ - // decrement the layer count │ │ │ │ - --cache.count; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - // remove references to gmap elements │ │ │ │ - delete this.termsOfUse; │ │ │ │ - delete this.poweredBy; │ │ │ │ - delete this.mapObject; │ │ │ │ - delete this.dragObject; │ │ │ │ - OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - // │ │ │ │ - // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds │ │ │ │ - // │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getOLBoundsFromMapObjectBounds │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moBounds - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the │ │ │ │ - * passed-in MapObject Bounds. │ │ │ │ - * Returns null if null value is passed in. │ │ │ │ - */ │ │ │ │ - getOLBoundsFromMapObjectBounds: function(moBounds) { │ │ │ │ - var olBounds = null; │ │ │ │ - if (moBounds != null) { │ │ │ │ - var sw = moBounds.getSouthWest(); │ │ │ │ - var ne = moBounds.getNorthEast(); │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - sw = this.forwardMercator(sw.lng(), sw.lat()); │ │ │ │ - ne = this.forwardMercator(ne.lng(), ne.lat()); │ │ │ │ - } else { │ │ │ │ - sw = new OpenLayers.LonLat(sw.lng(), sw.lat()); │ │ │ │ - ne = new OpenLayers.LonLat(ne.lng(), ne.lat()); │ │ │ │ - } │ │ │ │ - olBounds = new OpenLayers.Bounds(sw.lon, │ │ │ │ - sw.lat, │ │ │ │ - ne.lon, │ │ │ │ - ne.lat); │ │ │ │ - } │ │ │ │ - return olBounds; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getWarningHTML │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} String with information on why layer is broken, how to get │ │ │ │ - * it working. │ │ │ │ - */ │ │ │ │ - getWarningHTML: function() { │ │ │ │ - return OpenLayers.i18n("googleWarning"); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - /************************************ │ │ │ │ - * * │ │ │ │ - * MapObject Interface Controls * │ │ │ │ - * * │ │ │ │ - ************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // Get&Set Center, Zoom │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectCenter │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} The mapObject's current center in Map Object format │ │ │ │ - */ │ │ │ │ - getMapObjectCenter: function() { │ │ │ │ - return this.mapObject.getCenter(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectZoom │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} The mapObject's current zoom, in Map Object format │ │ │ │ - */ │ │ │ │ - getMapObjectZoom: function() { │ │ │ │ - return this.mapObject.getZoom(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - /************************************ │ │ │ │ - * * │ │ │ │ - * MapObject Primitives * │ │ │ │ - * * │ │ │ │ - ************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // LonLat │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getLongitudeFromMapObjectLonLat │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moLonLat - {Object} MapObject LonLat format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Float} Longitude of the given MapObject LonLat │ │ │ │ - */ │ │ │ │ - getLongitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - return this.sphericalMercator ? │ │ │ │ - this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : │ │ │ │ - moLonLat.lng(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getLatitudeFromMapObjectLonLat │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moLonLat - {Object} MapObject LonLat format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Float} Latitude of the given MapObject LonLat │ │ │ │ - */ │ │ │ │ - getLatitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - var lat = this.sphericalMercator ? │ │ │ │ - this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : │ │ │ │ - moLonLat.lat(); │ │ │ │ - return lat; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - // Pixel │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getXFromMapObjectPixel │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moPixel - {Object} MapObject Pixel format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} X value of the MapObject Pixel │ │ │ │ - */ │ │ │ │ - getXFromMapObjectPixel: function(moPixel) { │ │ │ │ - return moPixel.x; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getYFromMapObjectPixel │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moPixel - {Object} MapObject Pixel format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} Y value of the MapObject Pixel │ │ │ │ - */ │ │ │ │ - getYFromMapObjectPixel: function(moPixel) { │ │ │ │ - return moPixel.y; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Google" │ │ │ │ - }); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Property: OpenLayers.Layer.Google.cache │ │ │ │ - * {Object} Cache for elements that should only be created once per map. │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Google.cache = {}; │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Layer.Google.v2 │ │ │ │ - * │ │ │ │ - * Mixin providing functionality specific to the Google Maps API v2. │ │ │ │ - * │ │ │ │ - * This API has been deprecated by Google. │ │ │ │ - * Developers are encouraged to migrate to v3 of the API; support for this │ │ │ │ - * is provided by <OpenLayers.Layer.Google.v3> │ │ │ │ + * - <OpenLayers.Geometry> │ │ │ │ */ │ │ │ │ -OpenLayers.Layer.Google.v2 = { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: termsOfUse │ │ │ │ - * {DOMElement} Div for Google's copyright and terms of use link │ │ │ │ - */ │ │ │ │ - termsOfUse: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: poweredBy │ │ │ │ - * {DOMElement} Div for Google's powered by logo and link │ │ │ │ - */ │ │ │ │ - poweredBy: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: dragObject │ │ │ │ - * {GDraggableObject} Since 2.93, Google has exposed the ability to get │ │ │ │ - * the maps GDraggableObject. We can now use this for smooth panning │ │ │ │ - */ │ │ │ │ - dragObject: null, │ │ │ │ +OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: loadMapObject │ │ │ │ - * Load the GMap and register appropriate event listeners. If we can't │ │ │ │ - * load GMap2, then display a warning message. │ │ │ │ + * APIProperty: x │ │ │ │ + * {float} │ │ │ │ */ │ │ │ │ - loadMapObject: function() { │ │ │ │ - if (!this.type) { │ │ │ │ - this.type = G_NORMAL_MAP; │ │ │ │ - } │ │ │ │ - var mapObject, termsOfUse, poweredBy; │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - // there are already Google layers added to this map │ │ │ │ - mapObject = cache.mapObject; │ │ │ │ - termsOfUse = cache.termsOfUse; │ │ │ │ - poweredBy = cache.poweredBy; │ │ │ │ - // increment the layer count │ │ │ │ - ++cache.count; │ │ │ │ - } else { │ │ │ │ - // this is the first Google layer for this map │ │ │ │ - │ │ │ │ - var container = this.map.viewPortDiv; │ │ │ │ - var div = document.createElement("div"); │ │ │ │ - div.id = this.map.id + "_GMap2Container"; │ │ │ │ - div.style.position = "absolute"; │ │ │ │ - div.style.width = "100%"; │ │ │ │ - div.style.height = "100%"; │ │ │ │ - container.appendChild(div); │ │ │ │ - │ │ │ │ - // create GMap and shuffle elements │ │ │ │ - try { │ │ │ │ - mapObject = new GMap2(div); │ │ │ │ - │ │ │ │ - // move the ToS and branding stuff up to the container div │ │ │ │ - termsOfUse = div.lastChild; │ │ │ │ - container.appendChild(termsOfUse); │ │ │ │ - termsOfUse.style.zIndex = "1100"; │ │ │ │ - termsOfUse.style.right = ""; │ │ │ │ - termsOfUse.style.bottom = ""; │ │ │ │ - termsOfUse.className = "olLayerGoogleCopyright"; │ │ │ │ - │ │ │ │ - poweredBy = div.lastChild; │ │ │ │ - container.appendChild(poweredBy); │ │ │ │ - poweredBy.style.zIndex = "1100"; │ │ │ │ - poweredBy.style.right = ""; │ │ │ │ - poweredBy.style.bottom = ""; │ │ │ │ - poweredBy.className = "olLayerGooglePoweredBy gmnoprint"; │ │ │ │ - │ │ │ │ - } catch (e) { │ │ │ │ - throw (e); │ │ │ │ - } │ │ │ │ - // cache elements for use by any other google layers added to │ │ │ │ - // this same map │ │ │ │ - OpenLayers.Layer.Google.cache[this.map.id] = { │ │ │ │ - mapObject: mapObject, │ │ │ │ - termsOfUse: termsOfUse, │ │ │ │ - poweredBy: poweredBy, │ │ │ │ - count: 1 │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - │ │ │ │ - this.mapObject = mapObject; │ │ │ │ - this.termsOfUse = termsOfUse; │ │ │ │ - this.poweredBy = poweredBy; │ │ │ │ - │ │ │ │ - // ensure this layer type is one of the mapObject types │ │ │ │ - if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), │ │ │ │ - this.type) === -1) { │ │ │ │ - this.mapObject.addMapType(this.type); │ │ │ │ - } │ │ │ │ - │ │ │ │ - //since v 2.93 getDragObject is now available. │ │ │ │ - if (typeof mapObject.getDragObject == "function") { │ │ │ │ - this.dragObject = mapObject.getDragObject(); │ │ │ │ - } else { │ │ │ │ - this.dragPanMapObject = null; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.isBaseLayer === false) { │ │ │ │ - this.setGMapVisibility(this.div.style.display !== "none"); │ │ │ │ - } │ │ │ │ - │ │ │ │ - }, │ │ │ │ + x: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: onMapResize │ │ │ │ + /** │ │ │ │ + * APIProperty: y │ │ │ │ + * {float} │ │ │ │ */ │ │ │ │ - onMapResize: function() { │ │ │ │ - // workaround for resizing of invisible or not yet fully loaded layers │ │ │ │ - // where GMap2.checkResize() does not work. We need to load the GMap │ │ │ │ - // for the old div size, then checkResize(), and then call │ │ │ │ - // layer.moveTo() to trigger GMap.setCenter() (which will finish │ │ │ │ - // the GMap initialization). │ │ │ │ - if (this.visibility && this.mapObject.isLoaded()) { │ │ │ │ - this.mapObject.checkResize(); │ │ │ │ - } else { │ │ │ │ - if (!this._resized) { │ │ │ │ - var layer = this; │ │ │ │ - var handle = GEvent.addListener(this.mapObject, "load", function() { │ │ │ │ - GEvent.removeListener(handle); │ │ │ │ - delete layer._resized; │ │ │ │ - layer.mapObject.checkResize(); │ │ │ │ - layer.moveTo(layer.map.getCenter(), layer.map.getZoom()); │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - this._resized = true; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + y: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setGMapVisibility │ │ │ │ - * Display the GMap container and associated elements. │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Geometry.Point │ │ │ │ + * Construct a point geometry. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * visible - {Boolean} Display the GMap elements. │ │ │ │ - */ │ │ │ │ - setGMapVisibility: function(visible) { │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - var container = this.mapObject.getContainer(); │ │ │ │ - if (visible === true) { │ │ │ │ - this.mapObject.setMapType(this.type); │ │ │ │ - container.style.display = ""; │ │ │ │ - this.termsOfUse.style.left = ""; │ │ │ │ - this.termsOfUse.style.display = ""; │ │ │ │ - this.poweredBy.style.display = ""; │ │ │ │ - cache.displayed = this.id; │ │ │ │ - } else { │ │ │ │ - if (cache.displayed === this.id) { │ │ │ │ - delete cache.displayed; │ │ │ │ - } │ │ │ │ - if (!cache.displayed) { │ │ │ │ - container.style.display = "none"; │ │ │ │ - this.termsOfUse.style.display = "none"; │ │ │ │ - // move ToU far to the left in addition to setting display │ │ │ │ - // to "none", because at the end of the GMap2 load │ │ │ │ - // sequence, display: none will be unset and ToU would be │ │ │ │ - // visible after loading a map with a google layer that is │ │ │ │ - // initially hidden. │ │ │ │ - this.termsOfUse.style.left = "-9999px"; │ │ │ │ - this.poweredBy.style.display = "none"; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getMapContainer │ │ │ │ + * x - {float} │ │ │ │ + * y - {float} │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} the GMap container's div │ │ │ │ */ │ │ │ │ - getMapContainer: function() { │ │ │ │ - return this.mapObject.getContainer(); │ │ │ │ - }, │ │ │ │ + initialize: function(x, y) { │ │ │ │ + OpenLayers.Geometry.prototype.initialize.apply(this, arguments); │ │ │ │ │ │ │ │ - // │ │ │ │ - // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds │ │ │ │ - // │ │ │ │ + this.x = parseFloat(x); │ │ │ │ + this.y = parseFloat(y); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getMapObjectBoundsFromOLBounds │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * olBounds - {<OpenLayers.Bounds>} │ │ │ │ + * APIMethod: clone │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} A MapObject Bounds, translated from olBounds │ │ │ │ - * Returns null if null value is passed in │ │ │ │ + * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point │ │ │ │ */ │ │ │ │ - getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ - var moBounds = null; │ │ │ │ - if (olBounds != null) { │ │ │ │ - var sw = this.sphericalMercator ? │ │ │ │ - this.inverseMercator(olBounds.bottom, olBounds.left) : │ │ │ │ - new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ - var ne = this.sphericalMercator ? │ │ │ │ - this.inverseMercator(olBounds.top, olBounds.right) : │ │ │ │ - new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ - moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), │ │ │ │ - new GLatLng(ne.lat, ne.lon)); │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Geometry.Point(this.x, this.y); │ │ │ │ } │ │ │ │ - return moBounds; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - /************************************ │ │ │ │ - * * │ │ │ │ - * MapObject Interface Controls * │ │ │ │ - * * │ │ │ │ - ************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // Get&Set Center, Zoom │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setMapObjectCenter │ │ │ │ - * Set the mapObject to the specified center and zoom │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * center - {Object} MapObject LonLat format │ │ │ │ - * zoom - {int} MapObject zoom format │ │ │ │ - */ │ │ │ │ - setMapObjectCenter: function(center, zoom) { │ │ │ │ - this.mapObject.setCenter(center, zoom); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: dragPanMapObject │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * dX - {Integer} │ │ │ │ - * dY - {Integer} │ │ │ │ - */ │ │ │ │ - dragPanMapObject: function(dX, dY) { │ │ │ │ - this.dragObject.moveBy(new GSize(-dX, dY)); │ │ │ │ - }, │ │ │ │ - │ │ │ │ │ │ │ │ - // LonLat - Pixel Translation │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectLonLatFromMapObjectPixel │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moPixel - {Object} MapObject Pixel format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} MapObject LonLat translated from MapObject Pixel │ │ │ │ - */ │ │ │ │ - getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ - return this.mapObject.fromContainerPixelToLatLng(moPixel); │ │ │ │ - }, │ │ │ │ + // catch any randomly tagged-on properties │ │ │ │ + OpenLayers.Util.applyDefaults(obj, this); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectPixelFromMapObjectLonLat │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moLonLat - {Object} MapObject LonLat format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} MapObject Pixel transtlated from MapObject LonLat │ │ │ │ - */ │ │ │ │ - getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - return this.mapObject.fromLatLngToContainerPixel(moLonLat); │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ - // Bounds │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIMethod: getMapObjectZoomFromMapObjectBounds │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moBounds - {Object} MapObject Bounds format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} MapObject Zoom for specified MapObject Bounds │ │ │ │ + * Method: calculateBounds │ │ │ │ + * Create a new Bounds based on the lon/lat │ │ │ │ */ │ │ │ │ - getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ - return this.mapObject.getBoundsZoomLevel(moBounds); │ │ │ │ + calculateBounds: function() { │ │ │ │ + this.bounds = new OpenLayers.Bounds(this.x, this.y, │ │ │ │ + this.x, this.y); │ │ │ │ }, │ │ │ │ │ │ │ │ - /************************************ │ │ │ │ - * * │ │ │ │ - * MapObject Primitives * │ │ │ │ - * * │ │ │ │ - ************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // LonLat │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIMethod: getMapObjectLonLatFromLonLat │ │ │ │ - * │ │ │ │ + * APIMethod: distanceTo │ │ │ │ + * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * lon - {Float} │ │ │ │ - * lat - {Float} │ │ │ │ - * │ │ │ │ + * geometry - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ + * options - {Object} Optional properties for configuring the distance │ │ │ │ + * calculation. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * details - {Boolean} Return details from the distance calculation. │ │ │ │ + * Default is false. │ │ │ │ + * edge - {Boolean} Calculate the distance from this geometry to the │ │ │ │ + * nearest edge of the target geometry. Default is true. If true, │ │ │ │ + * calling distanceTo from a geometry that is wholly contained within │ │ │ │ + * the target will result in a non-zero distance. If false, whenever │ │ │ │ + * geometries intersect, calling distanceTo will return 0. If false, │ │ │ │ + * details cannot be returned. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Object} MapObject LonLat built from lon and lat params │ │ │ │ + * {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. │ │ │ │ */ │ │ │ │ - getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ - var gLatLng; │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - var lonlat = this.inverseMercator(lon, lat); │ │ │ │ - gLatLng = new GLatLng(lonlat.lat, lonlat.lon); │ │ │ │ + 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 { │ │ │ │ - gLatLng = new GLatLng(lat, lon); │ │ │ │ + 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 gLatLng; │ │ │ │ + return result; │ │ │ │ }, │ │ │ │ │ │ │ │ - // Pixel │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectPixelFromXY │ │ │ │ + /** │ │ │ │ + * APIMethod: equals │ │ │ │ + * Determine whether another geometry is equivalent to this one. Geometries │ │ │ │ + * are considered equivalent if all components have the same coordinates. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * x - {Integer} │ │ │ │ - * y - {Integer} │ │ │ │ - * │ │ │ │ + * geom - {<OpenLayers.Geometry.Point>} The geometry to test. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Object} MapObject Pixel from x and y parameters │ │ │ │ - */ │ │ │ │ - getMapObjectPixelFromXY: function(x, y) { │ │ │ │ - return new GPoint(x, y); │ │ │ │ - } │ │ │ │ - │ │ │ │ -}; │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/Google/v3.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/Google.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Layer.Google.v3 │ │ │ │ - * │ │ │ │ - * Mixin providing functionality specific to the Google Maps API v3. │ │ │ │ - * │ │ │ │ - * To use this layer, you must include the GMaps v3 API in your html. │ │ │ │ - * │ │ │ │ - * Note that this layer configures the google.maps.map object with the │ │ │ │ - * "disableDefaultUI" option set to true. Using UI controls that the Google │ │ │ │ - * Maps API provides is not supported by the OpenLayers API. │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Google.v3 = { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constant: DEFAULTS │ │ │ │ - * {Object} It is not recommended to change the properties set here. Note │ │ │ │ - * that Google.v3 layers only work when sphericalMercator is set to true. │ │ │ │ - * │ │ │ │ - * (code) │ │ │ │ - * { │ │ │ │ - * sphericalMercator: true, │ │ │ │ - * projection: "EPSG:900913" │ │ │ │ - * } │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ - DEFAULTS: { │ │ │ │ - sphericalMercator: true, │ │ │ │ - projection: "EPSG:900913" │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: animationEnabled │ │ │ │ - * {Boolean} If set to true, the transition between zoom levels will be │ │ │ │ - * animated (if supported by the GMaps API for the device used). Set to │ │ │ │ - * false to match the zooming experience of other layer types. Default │ │ │ │ - * is true. Note that the GMaps API does not give us control over zoom │ │ │ │ - * animation, so if set to false, when zooming, this will make the │ │ │ │ - * layer temporarily invisible, wait until GMaps reports the map being │ │ │ │ - * idle, and make it visible again. The result will be a blank layer │ │ │ │ - * for a few moments while zooming. │ │ │ │ - */ │ │ │ │ - animationEnabled: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: loadMapObject │ │ │ │ - * Load the GMap and register appropriate event listeners. │ │ │ │ + * {Boolean} The supplied geometry is equivalent to this geometry. │ │ │ │ */ │ │ │ │ - loadMapObject: function() { │ │ │ │ - if (!this.type) { │ │ │ │ - this.type = google.maps.MapTypeId.ROADMAP; │ │ │ │ - } │ │ │ │ - var mapObject; │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - // there are already Google layers added to this map │ │ │ │ - mapObject = cache.mapObject; │ │ │ │ - // increment the layer count │ │ │ │ - ++cache.count; │ │ │ │ - } else { │ │ │ │ - // this is the first Google layer for this map │ │ │ │ - // create GMap │ │ │ │ - var center = this.map.getCenter(); │ │ │ │ - var container = document.createElement('div'); │ │ │ │ - container.className = "olForeignContainer"; │ │ │ │ - container.style.width = '100%'; │ │ │ │ - container.style.height = '100%'; │ │ │ │ - mapObject = new google.maps.Map(container, { │ │ │ │ - center: center ? │ │ │ │ - new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0), │ │ │ │ - zoom: this.map.getZoom() || 0, │ │ │ │ - mapTypeId: this.type, │ │ │ │ - disableDefaultUI: true, │ │ │ │ - keyboardShortcuts: false, │ │ │ │ - draggable: false, │ │ │ │ - disableDoubleClickZoom: true, │ │ │ │ - scrollwheel: false, │ │ │ │ - streetViewControl: false │ │ │ │ - }); │ │ │ │ - var googleControl = document.createElement('div'); │ │ │ │ - googleControl.style.width = '100%'; │ │ │ │ - googleControl.style.height = '100%'; │ │ │ │ - mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl); │ │ │ │ - │ │ │ │ - // cache elements for use by any other google layers added to │ │ │ │ - // this same map │ │ │ │ - cache = { │ │ │ │ - googleControl: googleControl, │ │ │ │ - mapObject: mapObject, │ │ │ │ - count: 1 │ │ │ │ - }; │ │ │ │ - OpenLayers.Layer.Google.cache[this.map.id] = cache; │ │ │ │ + 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))); │ │ │ │ } │ │ │ │ - this.mapObject = mapObject; │ │ │ │ - this.setGMapVisibility(this.visibility); │ │ │ │ + return equals; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: onMapResize │ │ │ │ + * Method: toShortString │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} Shortened String representation of Point object. │ │ │ │ + * (ex. <i>"5, 42"</i>) │ │ │ │ */ │ │ │ │ - onMapResize: function() { │ │ │ │ - if (this.visibility) { │ │ │ │ - google.maps.event.trigger(this.mapObject, "resize"); │ │ │ │ - } │ │ │ │ + toShortString: function() { │ │ │ │ + return (this.x + ", " + this.y); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setGMapVisibility │ │ │ │ - * Display the GMap container and associated elements. │ │ │ │ - * │ │ │ │ + * 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: │ │ │ │ - * visible - {Boolean} Display the GMap elements. │ │ │ │ + * x - {Float} Distance to move geometry in positive x direction. │ │ │ │ + * y - {Float} Distance to move geometry in positive y direction. │ │ │ │ */ │ │ │ │ - setGMapVisibility: function(visible) { │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - var map = this.map; │ │ │ │ - if (cache) { │ │ │ │ - var type = this.type; │ │ │ │ - var layers = map.layers; │ │ │ │ - var layer; │ │ │ │ - for (var i = layers.length - 1; i >= 0; --i) { │ │ │ │ - layer = layers[i]; │ │ │ │ - if (layer instanceof OpenLayers.Layer.Google && │ │ │ │ - layer.visibility === true && layer.inRange === true) { │ │ │ │ - type = layer.type; │ │ │ │ - visible = true; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var container = this.mapObject.getDiv(); │ │ │ │ - if (visible === true) { │ │ │ │ - if (container.parentNode !== map.div) { │ │ │ │ - if (!cache.rendered) { │ │ │ │ - var me = this; │ │ │ │ - google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() { │ │ │ │ - cache.rendered = true; │ │ │ │ - me.setGMapVisibility(me.getVisibility()); │ │ │ │ - me.moveTo(me.map.getCenter()); │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - map.div.appendChild(container); │ │ │ │ - cache.googleControl.appendChild(map.viewPortDiv); │ │ │ │ - google.maps.event.trigger(this.mapObject, 'resize'); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.mapObject.setMapTypeId(type); │ │ │ │ - } else if (cache.googleControl.hasChildNodes()) { │ │ │ │ - map.div.appendChild(map.viewPortDiv); │ │ │ │ - map.div.removeChild(container); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + move: function(x, y) { │ │ │ │ + this.x = this.x + x; │ │ │ │ + this.y = this.y + y; │ │ │ │ + this.clearBounds(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getMapContainer │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} the GMap container's div │ │ │ │ + * APIMethod: rotate │ │ │ │ + * Rotate a point around another. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * angle - {Float} Rotation angle in degrees (measured counterclockwise │ │ │ │ + * from the positive x-axis) │ │ │ │ + * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation │ │ │ │ */ │ │ │ │ - getMapContainer: function() { │ │ │ │ - return this.mapObject.getDiv(); │ │ │ │ + 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(); │ │ │ │ }, │ │ │ │ │ │ │ │ - // │ │ │ │ - // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds │ │ │ │ - // │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIMethod: getMapObjectBoundsFromOLBounds │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * olBounds - {<OpenLayers.Bounds>} │ │ │ │ - * │ │ │ │ + * APIMethod: getCentroid │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Object} A MapObject Bounds, translated from olBounds │ │ │ │ - * Returns null if null value is passed in │ │ │ │ + * {<OpenLayers.Geometry.Point>} The centroid of the collection │ │ │ │ */ │ │ │ │ - getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ - var moBounds = null; │ │ │ │ - if (olBounds != null) { │ │ │ │ - var sw = this.sphericalMercator ? │ │ │ │ - this.inverseMercator(olBounds.bottom, olBounds.left) : │ │ │ │ - new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ - var ne = this.sphericalMercator ? │ │ │ │ - this.inverseMercator(olBounds.top, olBounds.right) : │ │ │ │ - new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ - moBounds = new google.maps.LatLngBounds( │ │ │ │ - new google.maps.LatLng(sw.lat, sw.lon), │ │ │ │ - new google.maps.LatLng(ne.lat, ne.lon) │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - return moBounds; │ │ │ │ + getCentroid: function() { │ │ │ │ + return new OpenLayers.Geometry.Point(this.x, this.y); │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ - /************************************ │ │ │ │ - * * │ │ │ │ - * MapObject Interface Controls * │ │ │ │ - * * │ │ │ │ - ************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // LonLat - Pixel Translation │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIMethod: getMapObjectLonLatFromMapObjectPixel │ │ │ │ - * │ │ │ │ + * 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: │ │ │ │ - * moPixel - {Object} MapObject Pixel format │ │ │ │ + * scale - {Float} Ratio of the new distance from the origin to the old │ │ │ │ + * distance from the origin. A scale of 2 doubles the │ │ │ │ + * distance between the point and origin. │ │ │ │ + * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing │ │ │ │ + * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} MapObject LonLat translated from MapObject Pixel │ │ │ │ + * {<OpenLayers.Geometry>} - The current geometry. │ │ │ │ */ │ │ │ │ - getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ - var size = this.map.getSize(); │ │ │ │ - var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ - var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ - var res = this.map.getResolution(); │ │ │ │ - │ │ │ │ - var delta_x = moPixel.x - (size.w / 2); │ │ │ │ - var delta_y = moPixel.y - (size.h / 2); │ │ │ │ - │ │ │ │ - var lonlat = new OpenLayers.LonLat( │ │ │ │ - lon + delta_x * res, │ │ │ │ - lat - delta_y * res │ │ │ │ - ); │ │ │ │ - │ │ │ │ - if (this.wrapDateLine) { │ │ │ │ - lonlat = lonlat.wrapDateLine(this.maxExtent); │ │ │ │ - } │ │ │ │ - return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat); │ │ │ │ + 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: getMapObjectPixelFromMapObjectLonLat │ │ │ │ - * │ │ │ │ + * APIMethod: intersects │ │ │ │ + * Determine if the input geometry intersects this one. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * moLonLat - {Object} MapObject LonLat format │ │ │ │ - * │ │ │ │ + * geometry - {<OpenLayers.Geometry>} Any type of geometry. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Object} MapObject Pixel transtlated from MapObject LonLat │ │ │ │ - */ │ │ │ │ - getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - var res = this.map.getResolution(); │ │ │ │ - var extent = this.map.getExtent(); │ │ │ │ - return this.getMapObjectPixelFromXY((1 / res * (lon - extent.left)), │ │ │ │ - (1 / res * (extent.top - lat))); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setMapObjectCenter │ │ │ │ - * Set the mapObject to the specified center and zoom │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * center - {Object} MapObject LonLat format │ │ │ │ - * zoom - {int} MapObject zoom format │ │ │ │ + * {Boolean} The input geometry intersects this one. │ │ │ │ */ │ │ │ │ - setMapObjectCenter: function(center, zoom) { │ │ │ │ - if (this.animationEnabled === false && zoom != this.mapObject.zoom) { │ │ │ │ - var mapContainer = this.getMapContainer(); │ │ │ │ - google.maps.event.addListenerOnce( │ │ │ │ - this.mapObject, │ │ │ │ - "idle", │ │ │ │ - function() { │ │ │ │ - mapContainer.style.visibility = ""; │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - mapContainer.style.visibility = "hidden"; │ │ │ │ + intersects: function(geometry) { │ │ │ │ + var intersect = false; │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ + intersect = this.equals(geometry); │ │ │ │ + } else { │ │ │ │ + intersect = geometry.intersects(this); │ │ │ │ } │ │ │ │ - this.mapObject.setOptions({ │ │ │ │ - center: center, │ │ │ │ - zoom: zoom │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - // Bounds │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: getMapObjectZoomFromMapObjectBounds │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * moBounds - {Object} MapObject Bounds format │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} MapObject Zoom for specified MapObject Bounds │ │ │ │ - */ │ │ │ │ - getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ - return this.mapObject.getBoundsZoomLevel(moBounds); │ │ │ │ + return intersect; │ │ │ │ }, │ │ │ │ │ │ │ │ - /************************************ │ │ │ │ - * * │ │ │ │ - * MapObject Primitives * │ │ │ │ - * * │ │ │ │ - ************************************/ │ │ │ │ - │ │ │ │ - │ │ │ │ - // LonLat │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIMethod: getMapObjectLonLatFromLonLat │ │ │ │ + * APIMethod: transform │ │ │ │ + * Translate the x,y properties of the point from source to dest. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * lon - {Float} │ │ │ │ - * lat - {Float} │ │ │ │ + * source - {<OpenLayers.Projection>} │ │ │ │ + * dest - {<OpenLayers.Projection>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} MapObject LonLat built from lon and lat params │ │ │ │ + * {<OpenLayers.Geometry>} │ │ │ │ */ │ │ │ │ - getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ - var gLatLng; │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - var lonlat = this.inverseMercator(lon, lat); │ │ │ │ - gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon); │ │ │ │ - } else { │ │ │ │ - gLatLng = new google.maps.LatLng(lat, lon); │ │ │ │ + transform: function(source, dest) { │ │ │ │ + if ((source && dest)) { │ │ │ │ + OpenLayers.Projection.transform( │ │ │ │ + this, source, dest); │ │ │ │ + this.bounds = null; │ │ │ │ } │ │ │ │ - return gLatLng; │ │ │ │ + return this; │ │ │ │ }, │ │ │ │ │ │ │ │ - // Pixel │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIMethod: getMapObjectPixelFromXY │ │ │ │ - * │ │ │ │ + * APIMethod: getVertices │ │ │ │ + * Return a list of all points in this geometry. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * x - {Integer} │ │ │ │ - * y - {Integer} │ │ │ │ - * │ │ │ │ + * 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: │ │ │ │ - * {Object} MapObject Pixel from x and y parameters │ │ │ │ + * {Array} A list of all vertices in the geometry. │ │ │ │ */ │ │ │ │ - getMapObjectPixelFromXY: function(x, y) { │ │ │ │ - return new google.maps.Point(x, y); │ │ │ │ - } │ │ │ │ + getVertices: function(nodes) { │ │ │ │ + return [this]; │ │ │ │ + }, │ │ │ │ │ │ │ │ -}; │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.Point" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control.js │ │ │ │ + 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/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Geometry.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control │ │ │ │ - * Controls affect the display or behavior of the map. They allow everything │ │ │ │ - * from panning and zooming to displaying a scale indicator. Controls by │ │ │ │ - * default are added to the map they are contained within however it is │ │ │ │ - * possible to add a control to an external div by passing the div in the │ │ │ │ - * options parameter. │ │ │ │ + * Class: OpenLayers.Geometry.Collection │ │ │ │ + * A Collection is exactly what it sounds like: A collection of different │ │ │ │ + * Geometries. These are stored in the local parameter <components> (which │ │ │ │ + * can be passed as a parameter to the constructor). │ │ │ │ * │ │ │ │ - * Example: │ │ │ │ - * The following example shows how to add many of the common controls │ │ │ │ - * to a map. │ │ │ │ + * 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). │ │ │ │ * │ │ │ │ - * > var map = new OpenLayers.Map('map', { controls: [] }); │ │ │ │ - * > │ │ │ │ - * > map.addControl(new OpenLayers.Control.PanZoomBar()); │ │ │ │ - * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false})); │ │ │ │ - * > map.addControl(new OpenLayers.Control.Permalink()); │ │ │ │ - * > map.addControl(new OpenLayers.Control.Permalink('permalink')); │ │ │ │ - * > map.addControl(new OpenLayers.Control.MousePosition()); │ │ │ │ - * > map.addControl(new OpenLayers.Control.OverviewMap()); │ │ │ │ - * > map.addControl(new OpenLayers.Control.KeyboardDefaults()); │ │ │ │ + * The <getArea> and <getLength> functions here merely iterate through │ │ │ │ + * the components, summing their respective areas and lengths. │ │ │ │ * │ │ │ │ - * The next code fragment is a quick example of how to intercept │ │ │ │ - * shift-mouse click to display the extent of the bounding box │ │ │ │ - * dragged out by the user. Usually controls are not created │ │ │ │ - * in exactly this manner. See the source for a more complete │ │ │ │ - * example: │ │ │ │ + * Create a new instance with the <OpenLayers.Geometry.Collection> constructor. │ │ │ │ * │ │ │ │ - * > var control = new OpenLayers.Control(); │ │ │ │ - * > OpenLayers.Util.extend(control, { │ │ │ │ - * > draw: function () { │ │ │ │ - * > // this Handler.Box will intercept the shift-mousedown │ │ │ │ - * > // before Control.MouseDefault gets to see it │ │ │ │ - * > this.box = new OpenLayers.Handler.Box( control, │ │ │ │ - * > {"done": this.notice}, │ │ │ │ - * > {keyMask: OpenLayers.Handler.MOD_SHIFT}); │ │ │ │ - * > this.box.activate(); │ │ │ │ - * > }, │ │ │ │ - * > │ │ │ │ - * > notice: function (bounds) { │ │ │ │ - * > OpenLayers.Console.userError(bounds); │ │ │ │ - * > } │ │ │ │ - * > }); │ │ │ │ - * > map.addControl(control); │ │ │ │ - * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Geometry> │ │ │ │ */ │ │ │ │ -OpenLayers.Control = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: id │ │ │ │ - * {String} │ │ │ │ - */ │ │ │ │ - id: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: map │ │ │ │ - * {<OpenLayers.Map>} this gets set in the addControl() function in │ │ │ │ - * OpenLayers.Map │ │ │ │ - */ │ │ │ │ - map: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: div │ │ │ │ - * {DOMElement} The element that contains the control, if not present the │ │ │ │ - * control is placed inside the map. │ │ │ │ - */ │ │ │ │ - div: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: type │ │ │ │ - * {Number} Controls can have a 'type'. The type determines the type of │ │ │ │ - * interactions which are possible with them when they are placed in an │ │ │ │ - * <OpenLayers.Control.Panel>. │ │ │ │ - */ │ │ │ │ - type: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: allowSelection │ │ │ │ - * {Boolean} By default, controls do not allow selection, because │ │ │ │ - * it may interfere with map dragging. If this is true, OpenLayers │ │ │ │ - * will not prevent selection of the control. │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - allowSelection: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: displayClass │ │ │ │ - * {string} This property is used for CSS related to the drawing of the │ │ │ │ - * Control. │ │ │ │ - */ │ │ │ │ - displayClass: "", │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: title │ │ │ │ - * {string} This property is used for showing a tooltip over the │ │ │ │ - * Control. │ │ │ │ - */ │ │ │ │ - title: "", │ │ │ │ +OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * false. │ │ │ │ - */ │ │ │ │ - autoActivate: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: active │ │ │ │ - * {Boolean} The control is active (read-only). Use <activate> and │ │ │ │ - * <deactivate> to change control state. │ │ │ │ + * APIProperty: components │ │ │ │ + * {Array(<OpenLayers.Geometry>)} The component parts of this geometry │ │ │ │ */ │ │ │ │ - active: null, │ │ │ │ + components: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: handlerOptions │ │ │ │ - * {Object} Used to set non-default properties on the control's handler │ │ │ │ - */ │ │ │ │ - handlerOptions: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: handler │ │ │ │ - * {<OpenLayers.Handler>} 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. │ │ │ │ */ │ │ │ │ - handler: null, │ │ │ │ + componentTypes: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: eventListeners │ │ │ │ - * {Object} If set as an option at construction, the eventListeners │ │ │ │ - * object will be registered with <OpenLayers.Events.on>. Object │ │ │ │ - * structure must be a listeners object as shown in the example for │ │ │ │ - * the events.on method. │ │ │ │ - */ │ │ │ │ - eventListeners: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Listeners will be called with a reference to an event object. The │ │ │ │ - * properties of this event depends on exactly what happened. │ │ │ │ - * │ │ │ │ - * All event objects have at least the following properties: │ │ │ │ - * object - {Object} A reference to control.events.object (a reference │ │ │ │ - * to the control). │ │ │ │ - * element - {DOMElement} A reference to control.events.element (which │ │ │ │ - * will be null unless documented otherwise). │ │ │ │ + * Constructor: OpenLayers.Geometry.Collection │ │ │ │ + * Creates a Geometry Collection -- a list of geoms. │ │ │ │ * │ │ │ │ - * Supported map event types: │ │ │ │ - * activate - Triggered when activated. │ │ │ │ - * deactivate - Triggered when deactivated. │ │ │ │ - */ │ │ │ │ - events: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control │ │ │ │ - * Create an OpenLayers Control. The options passed as a parameter │ │ │ │ - * directly extend the control. For example passing the following: │ │ │ │ - * │ │ │ │ - * > var control = new OpenLayers.Control({div: myDiv}); │ │ │ │ + * Parameters: │ │ │ │ + * components - {Array(<OpenLayers.Geometry>)} Optional array of geometries │ │ │ │ * │ │ │ │ - * Overrides the default div attribute value of null. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - // We do this before the extend so that instances can override │ │ │ │ - // className in options. │ │ │ │ - this.displayClass = │ │ │ │ - this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, ""); │ │ │ │ - │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - │ │ │ │ - this.events = new OpenLayers.Events(this); │ │ │ │ - if (this.eventListeners instanceof Object) { │ │ │ │ - this.events.on(this.eventListeners); │ │ │ │ - } │ │ │ │ - if (this.id == null) { │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ + initialize: function(components) { │ │ │ │ + OpenLayers.Geometry.prototype.initialize.apply(this, arguments); │ │ │ │ + this.components = []; │ │ │ │ + if (components != null) { │ │ │ │ + this.addComponents(components); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ - * The destroy method is used to perform any clean up before the control │ │ │ │ - * is dereferenced. Typically this is where event listeners are removed │ │ │ │ - * to prevent memory leaks. │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Destroy this geometry. │ │ │ │ */ │ │ │ │ destroy: function() { │ │ │ │ - if (this.events) { │ │ │ │ - if (this.eventListeners) { │ │ │ │ - this.events.un(this.eventListeners); │ │ │ │ - } │ │ │ │ - this.events.destroy(); │ │ │ │ - this.events = null; │ │ │ │ - } │ │ │ │ - this.eventListeners = null; │ │ │ │ - │ │ │ │ - // eliminate circular references │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.destroy(); │ │ │ │ - this.handler = null; │ │ │ │ - } │ │ │ │ - if (this.handlers) { │ │ │ │ - for (var key in this.handlers) { │ │ │ │ - if (this.handlers.hasOwnProperty(key) && │ │ │ │ - typeof this.handlers[key].destroy == "function") { │ │ │ │ - this.handlers[key].destroy(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.handlers = null; │ │ │ │ - } │ │ │ │ - if (this.map) { │ │ │ │ - this.map.removeControl(this); │ │ │ │ - this.map = null; │ │ │ │ - } │ │ │ │ - this.div = null; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the control. This is done through an accessor │ │ │ │ - * so that subclasses can override this and take special action once │ │ │ │ - * they have their map variable set. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - setMap: function(map) { │ │ │ │ - this.map = map; │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.setMap(map); │ │ │ │ - } │ │ │ │ + this.components.length = 0; │ │ │ │ + this.components = null; │ │ │ │ + OpenLayers.Geometry.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ - * The draw method is called when the control is ready to be displayed │ │ │ │ - * on the page. If a div has not been created one is created. Controls │ │ │ │ - * with a visual component will almost always want to override this method │ │ │ │ - * to customize the look of control. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} The top-left pixel position of the control │ │ │ │ - * or null. │ │ │ │ + * APIMethod: clone │ │ │ │ + * Clone this geometry. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} A reference to the DIV DOMElement containing the control │ │ │ │ + * {<OpenLayers.Geometry.Collection>} An exact clone of this collection │ │ │ │ */ │ │ │ │ - draw: function(px) { │ │ │ │ - if (this.div == null) { │ │ │ │ - this.div = OpenLayers.Util.createDiv(this.id); │ │ │ │ - this.div.className = this.displayClass; │ │ │ │ - if (!this.allowSelection) { │ │ │ │ - this.div.className += " olControlNoSelect"; │ │ │ │ - this.div.setAttribute("unselectable", "on", 0); │ │ │ │ - this.div.onselectstart = OpenLayers.Function.False; │ │ │ │ - } │ │ │ │ - if (this.title != "") { │ │ │ │ - this.div.title = this.title; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (px != null) { │ │ │ │ - this.position = px.clone(); │ │ │ │ + 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()); │ │ │ │ } │ │ │ │ - this.moveTo(this.position); │ │ │ │ - return this.div; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: moveTo │ │ │ │ - * Sets the left and top style attributes to the passed in pixel │ │ │ │ - * coordinates. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ - */ │ │ │ │ - moveTo: function(px) { │ │ │ │ - if ((px != null) && (this.div != null)) { │ │ │ │ - this.div.style.left = px.x + "px"; │ │ │ │ - this.div.style.top = px.y + "px"; │ │ │ │ - } │ │ │ │ + // catch any randomly tagged-on properties │ │ │ │ + OpenLayers.Util.applyDefaults(geometry, this); │ │ │ │ + │ │ │ │ + return geometry; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Explicitly activates a control and it's associated │ │ │ │ - * handler if one has been set. Controls can be │ │ │ │ - * deactivated by calling the deactivate() method. │ │ │ │ + * Method: getComponentsString │ │ │ │ + * Get a string representing the components for this collection │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} True if the control was successfully activated or │ │ │ │ - * false if the control was already active. │ │ │ │ + * {String} A string representation of the components of this geometry │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.activate(); │ │ │ │ - } │ │ │ │ - this.active = true; │ │ │ │ - if (this.map) { │ │ │ │ - OpenLayers.Element.addClass( │ │ │ │ - this.map.viewPortDiv, │ │ │ │ - this.displayClass.replace(/ /g, "") + "Active" │ │ │ │ - ); │ │ │ │ + getComponentsString: function() { │ │ │ │ + var strings = []; │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ + strings.push(this.components[i].toShortString()); │ │ │ │ } │ │ │ │ - this.events.triggerEvent("activate"); │ │ │ │ - return true; │ │ │ │ + return strings.join(","); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivates a control and it's associated handler if any. The exact │ │ │ │ - * effect of this depends on the control itself. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} True if the control was effectively deactivated or false │ │ │ │ - * if the control was already inactive. │ │ │ │ + * APIMethod: calculateBounds │ │ │ │ + * Recalculate the bounds by iterating through the components and │ │ │ │ + * calling calling extendBounds() on each item. │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.deactivate(); │ │ │ │ - } │ │ │ │ - this.active = false; │ │ │ │ - if (this.map) { │ │ │ │ - OpenLayers.Element.removeClass( │ │ │ │ - this.map.viewPortDiv, │ │ │ │ - this.displayClass.replace(/ /g, "") + "Active" │ │ │ │ - ); │ │ │ │ + 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()); │ │ │ │ } │ │ │ │ - this.events.triggerEvent("deactivate"); │ │ │ │ - return true; │ │ │ │ } │ │ │ │ - return false; │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control" │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Control.TYPE_BUTTON │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.TYPE_BUTTON = 1; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Control.TYPE_TOGGLE │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.TYPE_TOGGLE = 2; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Control.TYPE_TOOL │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.TYPE_TOOL = 3; │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Events/buttonclick.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Events.buttonclick │ │ │ │ - * Extension event type for handling buttons on top of a dom element. This │ │ │ │ - * event type fires "buttonclick" on its <target> when a button was │ │ │ │ - * clicked. Buttons are detected by the "olButton" class. │ │ │ │ - * │ │ │ │ - * This event type makes sure that button clicks do not interfere with other │ │ │ │ - * events that are registered on the same <element>. │ │ │ │ - * │ │ │ │ - * Event types provided by this extension: │ │ │ │ - * - *buttonclick* Triggered when a button is clicked. Listeners receive an │ │ │ │ - * object with a *buttonElement* property referencing the dom element of │ │ │ │ - * the clicked button, and an *buttonXY* property with the click position │ │ │ │ - * relative to the button. │ │ │ │ - */ │ │ │ │ -OpenLayers.Events.buttonclick = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: target │ │ │ │ - * {<OpenLayers.Events>} The events instance that the buttonclick event will │ │ │ │ - * be triggered on. │ │ │ │ - */ │ │ │ │ - target: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: events │ │ │ │ - * {Array} Events to observe and conditionally stop from propagating when │ │ │ │ - * an element with the olButton class (or its olAlphaImg child) is │ │ │ │ - * clicked. │ │ │ │ - */ │ │ │ │ - events: [ │ │ │ │ - 'mousedown', 'mouseup', 'click', 'dblclick', │ │ │ │ - 'touchstart', 'touchmove', 'touchend', 'keydown' │ │ │ │ - ], │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: startRegEx │ │ │ │ - * {RegExp} Regular expression to test Event.type for events that start │ │ │ │ - * a buttonclick sequence. │ │ │ │ - */ │ │ │ │ - startRegEx: /^mousedown|touchstart$/, │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Property: cancelRegEx │ │ │ │ - * {RegExp} Regular expression to test Event.type for events that cancel │ │ │ │ - * a buttonclick sequence. │ │ │ │ - */ │ │ │ │ - cancelRegEx: /^touchmove$/, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: completeRegEx │ │ │ │ - * {RegExp} Regular expression to test Event.type for events that complete │ │ │ │ - * a buttonclick sequence. │ │ │ │ - */ │ │ │ │ - completeRegEx: /^mouseup|touchend$/, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: startEvt │ │ │ │ - * {Event} The event that started the click sequence │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Events.buttonclick │ │ │ │ - * Construct a buttonclick event type. Applications are not supposed to │ │ │ │ - * create instances of this class - they are created on demand by │ │ │ │ - * <OpenLayers.Events> instances. │ │ │ │ + * APIMethod: addComponents │ │ │ │ + * Add components to this geometry. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * target - {<OpenLayers.Events>} The events instance that the buttonclick │ │ │ │ - * event will be triggered on. │ │ │ │ + * components - {Array(<OpenLayers.Geometry>)} An array of geometries to add │ │ │ │ */ │ │ │ │ - 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 │ │ │ │ - }); │ │ │ │ + addComponents: function(components) { │ │ │ │ + if (!(OpenLayers.Util.isArray(components))) { │ │ │ │ + components = [components]; │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - for (var i = this.events.length - 1; i >= 0; --i) { │ │ │ │ - this.target.unregister(this.events[i], this, this.buttonClick); │ │ │ │ + for (var i = 0, len = components.length; i < len; i++) { │ │ │ │ + this.addComponent(components[i]); │ │ │ │ } │ │ │ │ - delete this.target; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getPressedButton │ │ │ │ - * Get the pressed button, if any. Returns undefined if no button │ │ │ │ - * was pressed. │ │ │ │ + * 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. │ │ │ │ * │ │ │ │ - * Arguments: │ │ │ │ - * element - {DOMElement} The event target. │ │ │ │ + * The bounds cache is reset. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * component - {<OpenLayers.Geometry>} A geometry to add │ │ │ │ + * index - {int} Optional index into the array to insert the component │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} The button element, or undefined. │ │ │ │ + * {Boolean} The component geometry was successfully added │ │ │ │ */ │ │ │ │ - getPressedButton: function(element) { │ │ │ │ - var depth = 3, // limit the search depth │ │ │ │ - button; │ │ │ │ - do { │ │ │ │ - if (OpenLayers.Element.hasClass(element, "olButton")) { │ │ │ │ - // hit! │ │ │ │ - button = element; │ │ │ │ - break; │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - element = element.parentNode; │ │ │ │ - } while (--depth > 0 && element); │ │ │ │ - return button; │ │ │ │ + } │ │ │ │ + return added; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: ignore │ │ │ │ - * Check for event target elements that should be ignored by OpenLayers. │ │ │ │ + * APIMethod: removeComponents │ │ │ │ + * Remove components from this geometry. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * element - {DOMElement} The event target. │ │ │ │ + * components - {Array(<OpenLayers.Geometry>)} The components to be removed │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} A component was removed. │ │ │ │ */ │ │ │ │ - ignore: function(element) { │ │ │ │ - var depth = 3, │ │ │ │ - ignore = false; │ │ │ │ - do { │ │ │ │ - if (element.nodeName.toLowerCase() === 'a') { │ │ │ │ - ignore = true; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - element = element.parentNode; │ │ │ │ - } while (--depth > 0 && element); │ │ │ │ - return ignore; │ │ │ │ + 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: buttonClick │ │ │ │ - * Check if a button was clicked, and fire the buttonclick event │ │ │ │ + * Method: removeComponent │ │ │ │ + * Remove a component from this geometry. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * component - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The component was removed. │ │ │ │ */ │ │ │ │ - buttonClick: function(evt) { │ │ │ │ - var propagate = true, │ │ │ │ - element = OpenLayers.Event.element(evt); │ │ │ │ - if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) { │ │ │ │ - // was a button pressed? │ │ │ │ - var button = this.getPressedButton(element); │ │ │ │ - if (button) { │ │ │ │ - if (evt.type === "keydown") { │ │ │ │ - switch (evt.keyCode) { │ │ │ │ - case OpenLayers.Event.KEY_RETURN: │ │ │ │ - case OpenLayers.Event.KEY_SPACE: │ │ │ │ - this.target.triggerEvent("buttonclick", { │ │ │ │ - buttonElement: button │ │ │ │ - }); │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - propagate = false; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } else if (this.startEvt) { │ │ │ │ - if (this.completeRegEx.test(evt.type)) { │ │ │ │ - var pos = OpenLayers.Util.pagePosition(button); │ │ │ │ - var viewportElement = OpenLayers.Util.getViewportElement(); │ │ │ │ - var scrollTop = window.pageYOffset || viewportElement.scrollTop; │ │ │ │ - var scrollLeft = window.pageXOffset || viewportElement.scrollLeft; │ │ │ │ - pos[0] = pos[0] - scrollLeft; │ │ │ │ - pos[1] = pos[1] - scrollTop; │ │ │ │ - │ │ │ │ - this.target.triggerEvent("buttonclick", { │ │ │ │ - buttonElement: button, │ │ │ │ - buttonXY: { │ │ │ │ - x: this.startEvt.clientX - pos[0], │ │ │ │ - y: this.startEvt.clientY - pos[1] │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - if (this.cancelRegEx.test(evt.type)) { │ │ │ │ - delete this.startEvt; │ │ │ │ - } │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - propagate = false; │ │ │ │ - } │ │ │ │ - if (this.startRegEx.test(evt.type)) { │ │ │ │ - this.startEvt = evt; │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - propagate = false; │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - propagate = !this.ignore(OpenLayers.Event.element(evt)); │ │ │ │ - delete this.startEvt; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return propagate; │ │ │ │ - } │ │ │ │ - │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/Panel.js │ │ │ │ - ====================================================================== */ │ │ │ │ + removeComponent: function(component) { │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + OpenLayers.Util.removeItem(this.components, component); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Events/buttonclick.js │ │ │ │ - */ │ │ │ │ + // clearBounds() so that it gets recalculated on the next call │ │ │ │ + // to this.getBounds(); │ │ │ │ + this.clearBounds(); │ │ │ │ + return true; │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.Panel │ │ │ │ - * The Panel control is a container for other controls. With it toolbars │ │ │ │ - * may be composed. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ /** │ │ │ │ - * Property: controls │ │ │ │ - * {Array(<OpenLayers.Control>)} │ │ │ │ + * APIMethod: getLength │ │ │ │ + * Calculate the length of this geometry │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} The length of the geometry │ │ │ │ */ │ │ │ │ - controls: null, │ │ │ │ + getLength: function() { │ │ │ │ + var length = 0.0; │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ + length += this.components[i].getLength(); │ │ │ │ + } │ │ │ │ + return length; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * true. │ │ │ │ + * APIMethod: getArea │ │ │ │ + * Calculate the area of this geometry. Note how this function is overridden │ │ │ │ + * in <OpenLayers.Geometry.Polygon>. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} The area of the collection by summing its parts │ │ │ │ */ │ │ │ │ - autoActivate: true, │ │ │ │ + getArea: function() { │ │ │ │ + var area = 0.0; │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ + area += this.components[i].getArea(); │ │ │ │ + } │ │ │ │ + return area; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: defaultControl │ │ │ │ - * {<OpenLayers.Control>} The control which is activated when the control is │ │ │ │ - * activated (turned on), which also happens at instantiation. │ │ │ │ - * If <saveState> is true, <defaultControl> will be nullified after the │ │ │ │ - * first activation of the panel. │ │ │ │ - */ │ │ │ │ - defaultControl: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: saveState │ │ │ │ - * {Boolean} If set to true, the active state of this panel's controls will │ │ │ │ - * be stored on panel deactivation, and restored on reactivation. Default │ │ │ │ - * is false. │ │ │ │ - */ │ │ │ │ - saveState: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: allowDepress │ │ │ │ - * {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can │ │ │ │ - * be deactivated by clicking the icon that represents them. Default │ │ │ │ - * is false. │ │ │ │ - */ │ │ │ │ - allowDepress: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: activeState │ │ │ │ - * {Object} stores the active state of this panel's controls. │ │ │ │ - */ │ │ │ │ - activeState: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.Panel │ │ │ │ - * Create a new control panel. │ │ │ │ - * │ │ │ │ - * Each control in the panel is represented by an icon. When clicking │ │ │ │ - * on an icon, the <activateControl> method is called. │ │ │ │ - * │ │ │ │ - * Specific properties for controls on a panel: │ │ │ │ - * type - {Number} One of <OpenLayers.Control.TYPE_TOOL>, │ │ │ │ - * <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>. │ │ │ │ - * If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed. │ │ │ │ - * title - {string} Text displayed when mouse is over the icon that │ │ │ │ - * represents the control. │ │ │ │ - * │ │ │ │ - * The <OpenLayers.Control.type> of a control determines the behavior when │ │ │ │ - * clicking its icon: │ │ │ │ - * <OpenLayers.Control.TYPE_TOOL> - The control is activated and other │ │ │ │ - * controls of this type in the same panel are deactivated. This is │ │ │ │ - * the default type. │ │ │ │ - * <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is │ │ │ │ - * toggled. │ │ │ │ - * <OpenLayers.Control.TYPE_BUTTON> - The │ │ │ │ - * <OpenLayers.Control.Button.trigger> method of the control is called, │ │ │ │ - * but its active state is not changed. │ │ │ │ - * │ │ │ │ - * If a control is <OpenLayers.Control.active>, it will be drawn with the │ │ │ │ - * olControl[Name]ItemActive class, otherwise with the │ │ │ │ - * olControl[Name]ItemInactive class. │ │ │ │ + * APIMethod: getGeodesicArea │ │ │ │ + * Calculate the approximate area of the polygon were it projected onto │ │ │ │ + * the earth. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be used │ │ │ │ - * to extend the control. │ │ │ │ + * projection - {<OpenLayers.Projection>} The spatial reference system │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ + * assumed. │ │ │ │ + * │ │ │ │ + * Reference: │ │ │ │ + * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for │ │ │ │ + * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion │ │ │ │ + * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {float} The approximate geodesic area of the geometry in square meters. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.controls = []; │ │ │ │ - this.activeState = {}; │ │ │ │ + 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: destroy │ │ │ │ + * APIMethod: getCentroid │ │ │ │ + * │ │ │ │ + * Compute the centroid for this geometry collection. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * weighted - {Boolean} Perform the getCentroid computation recursively, │ │ │ │ + * returning an area weighted average of all geometries in this collection. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry.Point>} The centroid of the collection │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ + getCentroid: function(weighted) { │ │ │ │ + if (!weighted) { │ │ │ │ + return this.components.length && this.components[0].getCentroid(); │ │ │ │ } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - for (var ctl, i = this.controls.length - 1; i >= 0; i--) { │ │ │ │ - ctl = this.controls[i]; │ │ │ │ - if (ctl.events) { │ │ │ │ - ctl.events.un({ │ │ │ │ - activate: this.iconOn, │ │ │ │ - deactivate: this.iconOff │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - ctl.panel_div = null; │ │ │ │ + var len = this.components.length; │ │ │ │ + if (!len) { │ │ │ │ + return false; │ │ │ │ } │ │ │ │ - this.activeState = null; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - var control; │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - control = this.controls[i]; │ │ │ │ - if (control === this.defaultControl || │ │ │ │ - (this.saveState && this.activeState[control.id])) { │ │ │ │ - control.activate(); │ │ │ │ - } │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - if (this.saveState === true) { │ │ │ │ - this.defaultControl = null; │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - this.redraw(); │ │ │ │ - return true; │ │ │ │ + areaSum = areas.length; │ │ │ │ } else { │ │ │ │ - return false; │ │ │ │ + // 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; │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - var control; │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - control = this.controls[i]; │ │ │ │ - this.activeState[control.id] = control.deactivate(); │ │ │ │ - } │ │ │ │ - this.redraw(); │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ + 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); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ + * APIMethod: getGeodesicLength │ │ │ │ + * Calculate the approximate length of the geometry were it projected onto │ │ │ │ + * the earth. │ │ │ │ * │ │ │ │ + * projection - {<OpenLayers.Projection>} The spatial reference system │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ + * assumed. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (this.outsideViewport) { │ │ │ │ - this.events.attachToElement(this.div); │ │ │ │ - this.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ - } else { │ │ │ │ - this.map.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ - } │ │ │ │ - this.addControlsToMap(this.controls); │ │ │ │ - return this.div; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: redraw │ │ │ │ + * {Float} The appoximate geodesic length of the geometry in meters. │ │ │ │ */ │ │ │ │ - redraw: function() { │ │ │ │ - for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) { │ │ │ │ - this.div.removeChild(this.div.childNodes[i]); │ │ │ │ - } │ │ │ │ - this.div.innerHTML = ""; │ │ │ │ - if (this.active) { │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - this.div.appendChild(this.controls[i].panel_div); │ │ │ │ - } │ │ │ │ + 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: activateControl │ │ │ │ - * This method is called when the user click on the icon representing a │ │ │ │ - * control in the panel. │ │ │ │ + * 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: │ │ │ │ - * control - {<OpenLayers.Control>} │ │ │ │ + * x - {Float} Distance to move geometry in positive x direction. │ │ │ │ + * y - {Float} Distance to move geometry in positive y direction. │ │ │ │ */ │ │ │ │ - activateControl: function(control) { │ │ │ │ - if (!this.active) { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - if (control.type == OpenLayers.Control.TYPE_BUTTON) { │ │ │ │ - control.trigger(); │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (control.type == OpenLayers.Control.TYPE_TOGGLE) { │ │ │ │ - if (control.active) { │ │ │ │ - control.deactivate(); │ │ │ │ - } else { │ │ │ │ - control.activate(); │ │ │ │ - } │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (this.allowDepress && control.active) { │ │ │ │ - control.deactivate(); │ │ │ │ - } else { │ │ │ │ - var c; │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - c = this.controls[i]; │ │ │ │ - if (c != control && │ │ │ │ - (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) { │ │ │ │ - c.deactivate(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - control.activate(); │ │ │ │ + move: function(x, y) { │ │ │ │ + for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ + this.components[i].move(x, y); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: addControls │ │ │ │ - * To build a toolbar, you add a set of controls to it. addControls │ │ │ │ - * lets you add a single control or a list of controls to the │ │ │ │ - * Control Panel. │ │ │ │ + * APIMethod: rotate │ │ │ │ + * Rotate a geometry around some origin │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * controls - {<OpenLayers.Control>} Controls to add in the panel. │ │ │ │ + * angle - {Float} Rotation angle in degrees (measured counterclockwise │ │ │ │ + * from the positive x-axis) │ │ │ │ + * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation │ │ │ │ */ │ │ │ │ - addControls: function(controls) { │ │ │ │ - if (!(OpenLayers.Util.isArray(controls))) { │ │ │ │ - controls = [controls]; │ │ │ │ - } │ │ │ │ - this.controls = this.controls.concat(controls); │ │ │ │ - │ │ │ │ - for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ - var control = controls[i], │ │ │ │ - element = this.createControlMarkup(control); │ │ │ │ - OpenLayers.Element.addClass(element, │ │ │ │ - control.displayClass + "ItemInactive"); │ │ │ │ - OpenLayers.Element.addClass(element, "olButton"); │ │ │ │ - if (control.title != "" && !element.title) { │ │ │ │ - element.title = control.title; │ │ │ │ - } │ │ │ │ - control.panel_div = element; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.map) { // map.addControl() has already been called on the panel │ │ │ │ - this.addControlsToMap(controls); │ │ │ │ - this.redraw(); │ │ │ │ + rotate: function(angle, origin) { │ │ │ │ + for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ + this.components[i].rotate(angle, origin); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: createControlMarkup │ │ │ │ - * This function just creates a div for the control. If specific HTML │ │ │ │ - * markup is needed this function can be overridden in specific classes, │ │ │ │ - * or at panel instantiation time: │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * var panel = new OpenLayers.Control.Panel({ │ │ │ │ - * defaultControl: control, │ │ │ │ - * // ovverride createControlMarkup to create actual buttons │ │ │ │ - * // including texts wrapped into span elements. │ │ │ │ - * createControlMarkup: function(control) { │ │ │ │ - * var button = document.createElement('button'), │ │ │ │ - * span = document.createElement('span'); │ │ │ │ - * if (control.text) { │ │ │ │ - * span.innerHTML = control.text; │ │ │ │ - * } │ │ │ │ - * return button; │ │ │ │ - * } │ │ │ │ - * }); │ │ │ │ - * (end) │ │ │ │ + * APIMethod: resize │ │ │ │ + * Resize a geometry relative to some origin. Use this method to apply │ │ │ │ + * a uniform scaling to a geometry. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} The control to create the HTML │ │ │ │ - * markup for. │ │ │ │ - * │ │ │ │ + * scale - {Float} Factor by which to scale the geometry. A scale of 2 │ │ │ │ + * doubles the size of the geometry in each dimension │ │ │ │ + * (lines, for example, will be twice as long, and polygons │ │ │ │ + * will have four times the area). │ │ │ │ + * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing │ │ │ │ + * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} The markup. │ │ │ │ + * {<OpenLayers.Geometry>} - The current geometry. │ │ │ │ */ │ │ │ │ - createControlMarkup: function(control) { │ │ │ │ - return document.createElement("div"); │ │ │ │ + resize: function(scale, origin, ratio) { │ │ │ │ + for (var i = 0; i < this.components.length; ++i) { │ │ │ │ + this.components[i].resize(scale, origin, ratio); │ │ │ │ + } │ │ │ │ + return this; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: addControlsToMap │ │ │ │ - * Only for internal use in draw() and addControls() methods. │ │ │ │ + * APIMethod: distanceTo │ │ │ │ + * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * controls - {Array(<OpenLayers.Control>)} Controls to add into map. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ + * options - {Object} Optional properties for configuring the distance │ │ │ │ + * calculation. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * details - {Boolean} Return details from the distance calculation. │ │ │ │ + * Default is false. │ │ │ │ + * edge - {Boolean} Calculate the distance from this geometry to the │ │ │ │ + * nearest edge of the target geometry. Default is true. If true, │ │ │ │ + * calling distanceTo from a geometry that is wholly contained within │ │ │ │ + * the target will result in a non-zero distance. If false, whenever │ │ │ │ + * geometries intersect, calling distanceTo will return 0. If false, │ │ │ │ + * details cannot be returned. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Number | Object} The distance between this geometry and the target. │ │ │ │ + * If details is true, the return will be an object with distance, │ │ │ │ + * x0, y0, x1, and y1 properties. The x0 and y0 properties represent │ │ │ │ + * the coordinates of the closest point on this geometry. The x1 and y1 │ │ │ │ + * properties represent the coordinates of the closest point on the │ │ │ │ + * target geometry. │ │ │ │ */ │ │ │ │ - addControlsToMap: function(controls) { │ │ │ │ - var control; │ │ │ │ - for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ - control = controls[i]; │ │ │ │ - if (control.autoActivate === true) { │ │ │ │ - control.autoActivate = false; │ │ │ │ - this.map.addControl(control); │ │ │ │ - control.autoActivate = true; │ │ │ │ - } else { │ │ │ │ - this.map.addControl(control); │ │ │ │ - control.deactivate(); │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ } │ │ │ │ - control.events.on({ │ │ │ │ - activate: this.iconOn, │ │ │ │ - deactivate: this.iconOff │ │ │ │ - }); │ │ │ │ } │ │ │ │ + return best; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: iconOn │ │ │ │ - * Internal use, for use only with "controls[i].events.on/un". │ │ │ │ - */ │ │ │ │ - iconOn: function() { │ │ │ │ - var d = this.panel_div; // "this" refers to a control on panel! │ │ │ │ - var re = new RegExp("\\b(" + this.displayClass + "Item)Inactive\\b"); │ │ │ │ - d.className = d.className.replace(re, "$1Active"); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: iconOff │ │ │ │ - * Internal use, for use only with "controls[i].events.on/un". │ │ │ │ - */ │ │ │ │ - iconOff: function() { │ │ │ │ - var d = this.panel_div; // "this" refers to a control on panel! │ │ │ │ - var re = new RegExp("\\b(" + this.displayClass + "Item)Active\\b"); │ │ │ │ - d.className = d.className.replace(re, "$1Inactive"); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: onButtonClick │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * APIMethod: equals │ │ │ │ + * Determine whether another geometry is equivalent to this one. Geometries │ │ │ │ + * are considered equivalent if all components have the same coordinates. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} The geometry to test. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The supplied geometry is equivalent to this geometry. │ │ │ │ */ │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - var controls = this.controls, │ │ │ │ - button = evt.buttonElement; │ │ │ │ - for (var i = controls.length - 1; i >= 0; --i) { │ │ │ │ - if (controls[i].panel_div === button) { │ │ │ │ - this.activateControl(controls[i]); │ │ │ │ - break; │ │ │ │ + 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: getControlsBy │ │ │ │ - * Get a list of controls with properties matching the given criteria. │ │ │ │ - * │ │ │ │ + * APIMethod: transform │ │ │ │ + * Reproject the components geometry from source to dest. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * property - {String} A control property to be matched. │ │ │ │ - * match - {String | Object} A string to match. Can also be a regular │ │ │ │ - * expression literal or object. In addition, it can be any object │ │ │ │ - * with a method named test. For reqular expressions or other, if │ │ │ │ - * match.test(control[property]) evaluates to true, the control will be │ │ │ │ - * included in the array returned. If no controls are found, an empty │ │ │ │ - * array is returned. │ │ │ │ - * │ │ │ │ + * source - {<OpenLayers.Projection>} │ │ │ │ + * dest - {<OpenLayers.Projection>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Array(<OpenLayers.Control>)} A list of controls matching the given criteria. │ │ │ │ - * An empty array is returned if no matches are found. │ │ │ │ + * {<OpenLayers.Geometry>} │ │ │ │ */ │ │ │ │ - getControlsBy: function(property, match) { │ │ │ │ - var test = (typeof match.test == "function"); │ │ │ │ - var found = OpenLayers.Array.filter(this.controls, function(item) { │ │ │ │ - return item[property] == match || (test && match.test(item[property])); │ │ │ │ - }); │ │ │ │ - return found; │ │ │ │ + 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: getControlsByName │ │ │ │ - * Get a list of contorls with names matching the given name. │ │ │ │ + * APIMethod: intersects │ │ │ │ + * Determine if the input geometry intersects this one. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * match - {String | Object} A control name. The name can also be a regular │ │ │ │ - * expression literal or object. In addition, it can be any object │ │ │ │ - * with a method named test. For reqular expressions or other, if │ │ │ │ - * name.test(control.name) evaluates to true, the control will be included │ │ │ │ - * in the list of controls returned. If no controls are found, an empty │ │ │ │ - * array is returned. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} Any type of geometry. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array(<OpenLayers.Control>)} A list of controls matching the given name. │ │ │ │ - * An empty array is returned if no matches are found. │ │ │ │ + * {Boolean} The input geometry intersects this one. │ │ │ │ */ │ │ │ │ - getControlsByName: function(match) { │ │ │ │ - return this.getControlsBy("name", match); │ │ │ │ + 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: getControlsByClass │ │ │ │ - * Get a list of controls of a given type (CLASS_NAME). │ │ │ │ + * APIMethod: getVertices │ │ │ │ + * Return a list of all points in this geometry. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * match - {String | Object} A control class name. The type can also be a │ │ │ │ - * regular expression literal or object. In addition, it can be any │ │ │ │ - * object with a method named test. For reqular expressions or other, │ │ │ │ - * if type.test(control.CLASS_NAME) evaluates to true, the control will │ │ │ │ - * be included in the list of controls returned. If no controls are │ │ │ │ - * found, an empty array is returned. │ │ │ │ + * 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(<OpenLayers.Control>)} A list of controls matching the given type. │ │ │ │ - * An empty array is returned if no matches are found. │ │ │ │ + * {Array} A list of all vertices in the geometry. │ │ │ │ */ │ │ │ │ - getControlsByClass: function(match) { │ │ │ │ - return this.getControlsBy("CLASS_NAME", match); │ │ │ │ + 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.Control.Panel" │ │ │ │ -}); │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.Collection" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/Zoom.js │ │ │ │ + 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/Control.js │ │ │ │ - * @requires OpenLayers/Events/buttonclick.js │ │ │ │ + * @requires OpenLayers/Geometry/Collection.js │ │ │ │ + * @requires OpenLayers/Geometry/Point.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.Zoom │ │ │ │ - * The Zoom control is a pair of +/- links for zooming in and out. │ │ │ │ + * Class: OpenLayers.Geometry.MultiPoint │ │ │ │ + * MultiPoint is a collection of Points. Create a new instance with the │ │ │ │ + * <OpenLayers.Geometry.MultiPoint> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Geometry.Collection> │ │ │ │ + * - <OpenLayers.Geometry> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ +OpenLayers.Geometry.MultiPoint = OpenLayers.Class( │ │ │ │ + OpenLayers.Geometry.Collection, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomInText │ │ │ │ - * {String} │ │ │ │ - * Text for zoom-in link. Default is "+". │ │ │ │ - */ │ │ │ │ - zoomInText: "+", │ │ │ │ + /** │ │ │ │ + * 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"], │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomInId │ │ │ │ - * {String} │ │ │ │ - * Instead of having the control create a zoom in link, you can provide │ │ │ │ - * the identifier for an anchor element already added to the document. │ │ │ │ - * By default, an element with id "olZoomInLink" will be searched for │ │ │ │ - * and used if it exists. │ │ │ │ - */ │ │ │ │ - zoomInId: "olZoomInLink", │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Geometry.MultiPoint │ │ │ │ + * Create a new MultiPoint Geometry │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * components - {Array(<OpenLayers.Geometry.Point>)} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry.MultiPoint>} │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomOutText │ │ │ │ - * {String} │ │ │ │ - * Text for zoom-out link. Default is "\u2212". │ │ │ │ - */ │ │ │ │ - zoomOutText: "\u2212", │ │ │ │ + /** │ │ │ │ + * APIMethod: addPoint │ │ │ │ + * Wrapper for <OpenLayers.Geometry.Collection.addComponent> │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} Point to be added │ │ │ │ + * index - {Integer} Optional index │ │ │ │ + */ │ │ │ │ + addPoint: function(point, index) { │ │ │ │ + this.addComponent(point, index); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomOutId │ │ │ │ - * {String} │ │ │ │ - * Instead of having the control create a zoom out link, you can provide │ │ │ │ - * the identifier for an anchor element already added to the document. │ │ │ │ - * By default, an element with id "olZoomOutLink" will be searched for │ │ │ │ - * and used if it exists. │ │ │ │ - */ │ │ │ │ - zoomOutId: "olZoomOutLink", │ │ │ │ + /** │ │ │ │ + * APIMethod: removePoint │ │ │ │ + * Wrapper for <OpenLayers.Geometry.Collection.removeComponent> │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} Point to be removed │ │ │ │ + */ │ │ │ │ + removePoint: function(point) { │ │ │ │ + this.removeComponent(point); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A reference to the DOMElement containing the zoom links. │ │ │ │ - */ │ │ │ │ - draw: function() { │ │ │ │ - var div = OpenLayers.Control.prototype.draw.apply(this), │ │ │ │ - links = this.getOrCreateLinks(div), │ │ │ │ - zoomIn = links.zoomIn, │ │ │ │ - zoomOut = links.zoomOut, │ │ │ │ - eventsInstance = this.map.events; │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.MultiPoint" │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Geometry/Curve.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - if (zoomOut.parentNode !== div) { │ │ │ │ - eventsInstance = this.events; │ │ │ │ - eventsInstance.attachToElement(zoomOut.parentNode); │ │ │ │ - } │ │ │ │ - eventsInstance.register("buttonclick", this, this.onZoomClick); │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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.zoomInLink = zoomIn; │ │ │ │ - this.zoomOutLink = zoomOut; │ │ │ │ - return div; │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Geometry/MultiPoint.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Geometry.Curve │ │ │ │ + * A Curve is a MultiPoint, whose points are assumed to be connected. To │ │ │ │ + * this end, we provide a "getLength()" function, which iterates through │ │ │ │ + * the points, summing the distances between them. │ │ │ │ + * │ │ │ │ + * Inherits: │ │ │ │ + * - <OpenLayers.Geometry.MultiPoint> │ │ │ │ + */ │ │ │ │ +OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getOrCreateLinks │ │ │ │ + * 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: │ │ │ │ - * el - {DOMElement} │ │ │ │ - * │ │ │ │ - * Return: │ │ │ │ - * {Object} Object with zoomIn and zoomOut properties referencing links. │ │ │ │ + * point - {Array(<OpenLayers.Geometry.Point>)} │ │ │ │ */ │ │ │ │ - getOrCreateLinks: function(el) { │ │ │ │ - var zoomIn = document.getElementById(this.zoomInId), │ │ │ │ - zoomOut = document.getElementById(this.zoomOutId); │ │ │ │ - if (!zoomIn) { │ │ │ │ - zoomIn = document.createElement("a"); │ │ │ │ - zoomIn.href = "#zoomIn"; │ │ │ │ - zoomIn.appendChild(document.createTextNode(this.zoomInText)); │ │ │ │ - zoomIn.className = "olControlZoomIn"; │ │ │ │ - el.appendChild(zoomIn); │ │ │ │ - } │ │ │ │ - OpenLayers.Element.addClass(zoomIn, "olButton"); │ │ │ │ - if (!zoomOut) { │ │ │ │ - zoomOut = document.createElement("a"); │ │ │ │ - zoomOut.href = "#zoomOut"; │ │ │ │ - zoomOut.appendChild(document.createTextNode(this.zoomOutText)); │ │ │ │ - zoomOut.className = "olControlZoomOut"; │ │ │ │ - el.appendChild(zoomOut); │ │ │ │ - } │ │ │ │ - OpenLayers.Element.addClass(zoomOut, "olButton"); │ │ │ │ - return { │ │ │ │ - zoomIn: zoomIn, │ │ │ │ - zoomOut: zoomOut │ │ │ │ - }; │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onZoomClick │ │ │ │ - * Called when zoomin/out link is clicked. │ │ │ │ + * APIMethod: getLength │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} The length of the curve │ │ │ │ */ │ │ │ │ - onZoomClick: function(evt) { │ │ │ │ - var button = evt.buttonElement; │ │ │ │ - if (button === this.zoomInLink) { │ │ │ │ - this.map.zoomIn(); │ │ │ │ - } else if (button === this.zoomOutLink) { │ │ │ │ - this.map.zoomOut(); │ │ │ │ + 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; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * Clean up. │ │ │ │ + /** │ │ │ │ + * APIMethod: getGeodesicLength │ │ │ │ + * Calculate the approximate length of the geometry were it projected onto │ │ │ │ + * the earth. │ │ │ │ + * │ │ │ │ + * projection - {<OpenLayers.Projection>} The spatial reference system │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ + * assumed. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} The appoximate geodesic length of the geometry in meters. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.unregister("buttonclick", this, this.onZoomClick); │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ } │ │ │ │ - delete this.zoomInLink; │ │ │ │ - delete this.zoomOutLink; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this); │ │ │ │ + var length = 0.0; │ │ │ │ + if (geom.components && (geom.components.length > 1)) { │ │ │ │ + var p1, p2; │ │ │ │ + for (var i = 1, len = geom.components.length; i < len; i++) { │ │ │ │ + p1 = geom.components[i - 1]; │ │ │ │ + p2 = geom.components[i]; │ │ │ │ + // this returns km and requires lon/lat properties │ │ │ │ + length += OpenLayers.Util.distVincenty({ │ │ │ │ + lon: p1.x, │ │ │ │ + lat: p1.y │ │ │ │ + }, { │ │ │ │ + lon: p2.x, │ │ │ │ + lat: p2.y │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + // convert to m │ │ │ │ + return length * 1000; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Zoom" │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.Curve" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/LayerSwitcher.js │ │ │ │ + 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/Control.js │ │ │ │ - * @requires OpenLayers/Lang.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ - * @requires OpenLayers/Events/buttonclick.js │ │ │ │ + * @requires OpenLayers/Geometry/Curve.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.LayerSwitcher │ │ │ │ - * The LayerSwitcher control displays a table of contents for the map. This │ │ │ │ - * allows the user interface to switch between BaseLasyers and to show or hide │ │ │ │ - * Overlays. By default the switcher is shown minimized on the right edge of │ │ │ │ - * the map, the user may expand it by clicking on the handle. │ │ │ │ - * │ │ │ │ - * To create the LayerSwitcher outside of the map, pass the Id of a html div │ │ │ │ - * as the first argument to the constructor. │ │ │ │ - * │ │ │ │ + * 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.Control> │ │ │ │ + * - <OpenLayers.Geometry.Curve> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: layerStates │ │ │ │ - * {Array(Object)} Basically a copy of the "state" of the map's layers │ │ │ │ - * the last time the control was drawn. We have this in order to avoid │ │ │ │ - * unnecessarily redrawing the control. │ │ │ │ - */ │ │ │ │ - layerStates: null, │ │ │ │ - │ │ │ │ - // DOM Elements │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: layersDiv │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - layersDiv: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: baseLayersDiv │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - baseLayersDiv: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: baseLayers │ │ │ │ - * {Array(Object)} │ │ │ │ - */ │ │ │ │ - baseLayers: null, │ │ │ │ - │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: dataLbl │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - dataLbl: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: dataLayersDiv │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - dataLayersDiv: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: dataLayers │ │ │ │ - * {Array(Object)} │ │ │ │ - */ │ │ │ │ - dataLayers: null, │ │ │ │ - │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: minimizeDiv │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - minimizeDiv: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: maximizeDiv │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - maximizeDiv: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: ascending │ │ │ │ - * {Boolean} │ │ │ │ - */ │ │ │ │ - ascending: true, │ │ │ │ +OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.LayerSwitcher │ │ │ │ + * Constructor: OpenLayers.Geometry.LineString │ │ │ │ + * Create a new LineString geometry │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ - this.layerStates = []; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - │ │ │ │ - //clear out layers info and unregister their events │ │ │ │ - this.clearLayersArray("base"); │ │ │ │ - this.clearLayersArray("data"); │ │ │ │ - │ │ │ │ - this.map.events.un({ │ │ │ │ - buttonclick: this.onButtonClick, │ │ │ │ - addlayer: this.redraw, │ │ │ │ - changelayer: this.redraw, │ │ │ │ - removelayer: this.redraw, │ │ │ │ - changebaselayer: this.redraw, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ - │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ + * points - {Array(<OpenLayers.Geometry.Point>)} An array of points used to │ │ │ │ + * generate the linestring │ │ │ │ * │ │ │ │ - * Properties: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - │ │ │ │ - this.map.events.on({ │ │ │ │ - addlayer: this.redraw, │ │ │ │ - changelayer: this.redraw, │ │ │ │ - removelayer: this.redraw, │ │ │ │ - changebaselayer: this.redraw, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - if (this.outsideViewport) { │ │ │ │ - this.events.attachToElement(this.div); │ │ │ │ - this.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ - } else { │ │ │ │ - this.map.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ + * 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) │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A reference to the DIV DOMElement containing the │ │ │ │ - * switcher tabs. │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} The point to be removed │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The component was removed. │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this); │ │ │ │ - │ │ │ │ - // create layout divs │ │ │ │ - this.loadContents(); │ │ │ │ - │ │ │ │ - // set mode to minimize │ │ │ │ - if (!this.outsideViewport) { │ │ │ │ - this.minimizeControl(); │ │ │ │ + removeComponent: function(point) { │ │ │ │ + var removed = this.components && (this.components.length > 2); │ │ │ │ + if (removed) { │ │ │ │ + OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, │ │ │ │ + arguments); │ │ │ │ } │ │ │ │ - │ │ │ │ - // populate div with current info │ │ │ │ - this.redraw(); │ │ │ │ - │ │ │ │ - return this.div; │ │ │ │ + return removed; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: onButtonClick │ │ │ │ + * 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: │ │ │ │ - * evt - {Event} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The input geometry intersects this geometry. │ │ │ │ */ │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - var button = evt.buttonElement; │ │ │ │ - if (button === this.minimizeDiv) { │ │ │ │ - this.minimizeControl(); │ │ │ │ - } else if (button === this.maximizeDiv) { │ │ │ │ - this.maximizeControl(); │ │ │ │ - } else if (button._layerSwitcher === this.id) { │ │ │ │ - if (button["for"]) { │ │ │ │ - button = document.getElementById(button["for"]); │ │ │ │ + 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(); │ │ │ │ } │ │ │ │ - if (!button.disabled) { │ │ │ │ - if (button.type == "radio") { │ │ │ │ - button.checked = true; │ │ │ │ - this.map.setBaseLayer(this.map.getLayer(button._layer)); │ │ │ │ - } else { │ │ │ │ - button.checked = !button.checked; │ │ │ │ - this.updateMap(); │ │ │ │ + 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: clearLayersArray │ │ │ │ - * User specifies either "base" or "data". we then clear all the │ │ │ │ - * corresponding listeners, the div, and reinitialize a new array. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layersType - {String} │ │ │ │ - */ │ │ │ │ - clearLayersArray: function(layersType) { │ │ │ │ - this[layersType + "LayersDiv"].innerHTML = ""; │ │ │ │ - this[layersType + "Layers"] = []; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: checkRedraw │ │ │ │ - * Checks if the layer state has changed since the last redraw() call. │ │ │ │ + * Method: getSortedSegments │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The layer state changed since the last redraw() call. │ │ │ │ + * {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. │ │ │ │ */ │ │ │ │ - checkRedraw: function() { │ │ │ │ - if (!this.layerStates.length || │ │ │ │ - (this.map.layers.length != this.layerStates.length)) { │ │ │ │ - return true; │ │ │ │ - } │ │ │ │ - │ │ │ │ - for (var i = 0, len = this.layerStates.length; i < len; i++) { │ │ │ │ - var layerState = this.layerStates[i]; │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - if ((layerState.name != layer.name) || │ │ │ │ - (layerState.inRange != layer.inRange) || │ │ │ │ - (layerState.id != layer.id) || │ │ │ │ - (layerState.visibility != layer.visibility)) { │ │ │ │ - return true; │ │ │ │ + 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 │ │ │ │ + }; │ │ │ │ } │ │ │ │ } │ │ │ │ - │ │ │ │ - return false; │ │ │ │ + // more efficient to define this somewhere static │ │ │ │ + function byX1(seg1, seg2) { │ │ │ │ + return seg1.x1 - seg2.x1; │ │ │ │ + } │ │ │ │ + return segments.sort(byX1); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: redraw │ │ │ │ - * Goes through and takes the current state of the Map and rebuilds the │ │ │ │ - * control to display that state. Groups base layers into a │ │ │ │ - * radio-button group and lists each data layer with a checkbox. │ │ │ │ + * Method: splitWithSegment │ │ │ │ + * Split this geometry with the given segment. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * seg - {Object} An object with x1, y1, x2, and y2 properties referencing │ │ │ │ + * segment endpoint coordinates. │ │ │ │ + * options - {Object} Properties of this object will be used to determine │ │ │ │ + * how the split is conducted. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ + * true. If false, a vertex on the source segment must be within the │ │ │ │ + * tolerance distance of the intersection to be considered a split. │ │ │ │ + * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ + * within the tolerance distance of one of the source segment's │ │ │ │ + * endpoints will be assumed to occur at the endpoint. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} A reference to the DIV DOMElement containing the control │ │ │ │ + * {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). │ │ │ │ */ │ │ │ │ - redraw: function() { │ │ │ │ - //if the state hasn't changed since last redraw, no need │ │ │ │ - // to do anything. Just return the existing div. │ │ │ │ - if (!this.checkRedraw()) { │ │ │ │ - return this.div; │ │ │ │ - } │ │ │ │ - │ │ │ │ - //clear out previous layers │ │ │ │ - this.clearLayersArray("base"); │ │ │ │ - this.clearLayersArray("data"); │ │ │ │ - │ │ │ │ - var containsOverlays = false; │ │ │ │ - var containsBaseLayers = false; │ │ │ │ - │ │ │ │ - // Save state -- for checking layer if the map state changed. │ │ │ │ - // We save this before redrawing, because in the process of redrawing │ │ │ │ - // we will trigger more visibility changes, and we want to not redraw │ │ │ │ - // and enter an infinite loop. │ │ │ │ - var len = this.map.layers.length; │ │ │ │ - this.layerStates = new Array(len); │ │ │ │ - for (var i = 0; i < len; i++) { │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - this.layerStates[i] = { │ │ │ │ - 'name': layer.name, │ │ │ │ - 'visibility': layer.visibility, │ │ │ │ - 'inRange': layer.inRange, │ │ │ │ - 'id': layer.id │ │ │ │ + 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 │ │ │ │ }; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var layers = this.map.layers.slice(); │ │ │ │ - if (!this.ascending) { │ │ │ │ - layers.reverse(); │ │ │ │ - } │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - var layer = layers[i]; │ │ │ │ - var baseLayer = layer.isBaseLayer; │ │ │ │ - │ │ │ │ - if (layer.displayInLayerSwitcher) { │ │ │ │ - │ │ │ │ - if (baseLayer) { │ │ │ │ - containsBaseLayers = true; │ │ │ │ + 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 { │ │ │ │ - containsOverlays = true; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // only check a baselayer if it is *the* baselayer, check data │ │ │ │ - // layers if they are visible │ │ │ │ - var checked = (baseLayer) ? (layer == this.map.baseLayer) : │ │ │ │ - layer.getVisibility(); │ │ │ │ - │ │ │ │ - // create input element │ │ │ │ - var inputElem = document.createElement("input"), │ │ │ │ - // The input shall have an id attribute so we can use │ │ │ │ - // labels to interact with them. │ │ │ │ - inputId = OpenLayers.Util.createUniqueID( │ │ │ │ - this.id + "_input_" │ │ │ │ - ); │ │ │ │ - │ │ │ │ - inputElem.id = inputId; │ │ │ │ - inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name; │ │ │ │ - inputElem.type = (baseLayer) ? "radio" : "checkbox"; │ │ │ │ - inputElem.value = layer.name; │ │ │ │ - inputElem.checked = checked; │ │ │ │ - inputElem.defaultChecked = checked; │ │ │ │ - inputElem.className = "olButton"; │ │ │ │ - inputElem._layer = layer.id; │ │ │ │ - inputElem._layerSwitcher = this.id; │ │ │ │ - │ │ │ │ - if (!baseLayer && !layer.inRange) { │ │ │ │ - inputElem.disabled = true; │ │ │ │ + vertex = false; │ │ │ │ } │ │ │ │ - │ │ │ │ - // create span │ │ │ │ - var labelSpan = document.createElement("label"); │ │ │ │ - // this isn't the DOM attribute 'for', but an arbitrary name we │ │ │ │ - // use to find the appropriate input element in <onButtonClick> │ │ │ │ - labelSpan["for"] = inputElem.id; │ │ │ │ - OpenLayers.Element.addClass(labelSpan, "labelSpan olButton"); │ │ │ │ - labelSpan._layer = layer.id; │ │ │ │ - labelSpan._layerSwitcher = this.id; │ │ │ │ - if (!baseLayer && !layer.inRange) { │ │ │ │ - labelSpan.style.color = "gray"; │ │ │ │ + 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()]; │ │ │ │ } │ │ │ │ - labelSpan.innerHTML = layer.name; │ │ │ │ - labelSpan.style.verticalAlign = (baseLayer) ? "bottom" : │ │ │ │ - "baseline"; │ │ │ │ - // create line break │ │ │ │ - var br = document.createElement("br"); │ │ │ │ - │ │ │ │ - │ │ │ │ - var groupArray = (baseLayer) ? this.baseLayers : │ │ │ │ - this.dataLayers; │ │ │ │ - groupArray.push({ │ │ │ │ - 'layer': layer, │ │ │ │ - 'inputElem': inputElem, │ │ │ │ - 'labelSpan': labelSpan │ │ │ │ - }); │ │ │ │ - │ │ │ │ - │ │ │ │ - var groupDiv = (baseLayer) ? this.baseLayersDiv : │ │ │ │ - this.dataLayersDiv; │ │ │ │ - groupDiv.appendChild(inputElem); │ │ │ │ - groupDiv.appendChild(labelSpan); │ │ │ │ - groupDiv.appendChild(br); │ │ │ │ } │ │ │ │ } │ │ │ │ - │ │ │ │ - // if no overlays, dont display the overlay label │ │ │ │ - this.dataLbl.style.display = (containsOverlays) ? "" : "none"; │ │ │ │ - │ │ │ │ - // if no baselayers, dont display the baselayer label │ │ │ │ - this.baseLbl.style.display = (containsBaseLayers) ? "" : "none"; │ │ │ │ - │ │ │ │ - return this.div; │ │ │ │ + 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: updateMap │ │ │ │ - * Cycles through the loaded data and base layer input arrays and makes │ │ │ │ - * the necessary calls to the Map object such that that the map's │ │ │ │ - * visual state corresponds to what the user has selected in │ │ │ │ - * the control. │ │ │ │ + * Method: split │ │ │ │ + * Use this geometry (the source) to attempt to split a target geometry. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * target - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ + * options - {Object} Properties of this object will be used to determine │ │ │ │ + * how the split is conducted. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * mutual - {Boolean} Split the source geometry in addition to the target │ │ │ │ + * geometry. Default is false. │ │ │ │ + * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ + * true. If false, a vertex on the source must be within the tolerance │ │ │ │ + * distance of the intersection to be considered a split. │ │ │ │ + * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ + * within the tolerance distance of an existing vertex on the source │ │ │ │ + * will be assumed to occur at the vertex. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} A list of geometries (of this same type as the target) that │ │ │ │ + * result from splitting the target with the source geometry. The │ │ │ │ + * source and target geometry will remain unmodified. If no split │ │ │ │ + * results, null will be returned. If mutual is true and a split │ │ │ │ + * results, return will be an array of two arrays - the first will be │ │ │ │ + * all geometries that result from splitting the source geometry and │ │ │ │ + * the second will be all geometries that result from splitting the │ │ │ │ + * target geometry. │ │ │ │ */ │ │ │ │ - updateMap: function() { │ │ │ │ - │ │ │ │ - // set the newly selected base layer │ │ │ │ - for (var i = 0, len = this.baseLayers.length; i < len; i++) { │ │ │ │ - var layerEntry = this.baseLayers[i]; │ │ │ │ - if (layerEntry.inputElem.checked) { │ │ │ │ - this.map.setBaseLayer(layerEntry.layer, false); │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - │ │ │ │ - // set the correct visibilities for the overlays │ │ │ │ - for (var i = 0, len = this.dataLayers.length; i < len; i++) { │ │ │ │ - var layerEntry = this.dataLayers[i]; │ │ │ │ - layerEntry.layer.setVisibility(layerEntry.inputElem.checked); │ │ │ │ + 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: maximizeControl │ │ │ │ - * Set up the labels and divs for the control │ │ │ │ + * Method: splitWith │ │ │ │ + * Split this geometry (the target) with the given geometry (the source). │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * e - {Event} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} A geometry used to split this │ │ │ │ + * geometry (the source). │ │ │ │ + * options - {Object} Properties of this object will be used to determine │ │ │ │ + * how the split is conducted. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * mutual - {Boolean} Split the source geometry in addition to the target │ │ │ │ + * geometry. Default is false. │ │ │ │ + * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ + * true. If false, a vertex on the source must be within the tolerance │ │ │ │ + * distance of the intersection to be considered a split. │ │ │ │ + * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ + * within the tolerance distance of an existing vertex on the source │ │ │ │ + * will be assumed to occur at the vertex. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} A list of geometries (of this same type as the target) that │ │ │ │ + * result from splitting the target with the source geometry. The │ │ │ │ + * source and target geometry will remain unmodified. If no split │ │ │ │ + * results, null will be returned. If mutual is true and a split │ │ │ │ + * results, return will be an array of two arrays - the first will be │ │ │ │ + * all geometries that result from splitting the source geometry and │ │ │ │ + * the second will be all geometries that result from splitting the │ │ │ │ + * target geometry. │ │ │ │ */ │ │ │ │ - maximizeControl: function(e) { │ │ │ │ - │ │ │ │ - // set the div's width and height to empty values, so │ │ │ │ - // the div dimensions can be controlled by CSS │ │ │ │ - this.div.style.width = ""; │ │ │ │ - this.div.style.height = ""; │ │ │ │ - │ │ │ │ - this.showControls(false); │ │ │ │ + splitWith: function(geometry, options) { │ │ │ │ + return geometry.split(this, options); │ │ │ │ │ │ │ │ - if (e != null) { │ │ │ │ - OpenLayers.Event.stop(e); │ │ │ │ - } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: minimizeControl │ │ │ │ - * Hide all the contents of the control, shrink the size, │ │ │ │ - * add the maximize icon │ │ │ │ + * APIMethod: getVertices │ │ │ │ + * Return a list of all points in this geometry. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * e - {Event} │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - minimizeControl: function(e) { │ │ │ │ - │ │ │ │ - // to minimize the control we set its div's width │ │ │ │ - // and height to 0px, we cannot just set "display" │ │ │ │ - // to "none" because it would hide the maximize │ │ │ │ - // div │ │ │ │ - this.div.style.width = "0px"; │ │ │ │ - this.div.style.height = "0px"; │ │ │ │ - │ │ │ │ - this.showControls(true); │ │ │ │ - │ │ │ │ - if (e != null) { │ │ │ │ - OpenLayers.Event.stop(e); │ │ │ │ + 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; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: showControls │ │ │ │ - * Hide/Show all LayerSwitcher controls depending on whether we are │ │ │ │ - * minimized or not │ │ │ │ + * APIMethod: distanceTo │ │ │ │ + * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * minimize - {Boolean} │ │ │ │ + * geometry - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ + * options - {Object} Optional properties for configuring the distance │ │ │ │ + * calculation. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * details - {Boolean} Return details from the distance calculation. │ │ │ │ + * Default is false. │ │ │ │ + * edge - {Boolean} Calculate the distance from this geometry to the │ │ │ │ + * nearest edge of the target geometry. Default is true. If true, │ │ │ │ + * calling distanceTo from a geometry that is wholly contained within │ │ │ │ + * the target will result in a non-zero distance. If false, whenever │ │ │ │ + * geometries intersect, calling distanceTo will return 0. If false, │ │ │ │ + * details cannot be returned. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Number | Object} The distance between this geometry and the target. │ │ │ │ + * If details is true, the return will be an object with distance, │ │ │ │ + * x0, y0, x1, and x2 properties. The x0 and y0 properties represent │ │ │ │ + * the coordinates of the closest point on this geometry. The x1 and y1 │ │ │ │ + * properties represent the coordinates of the closest point on the │ │ │ │ + * target geometry. │ │ │ │ */ │ │ │ │ - showControls: function(minimize) { │ │ │ │ - │ │ │ │ - this.maximizeDiv.style.display = minimize ? "" : "none"; │ │ │ │ - this.minimizeDiv.style.display = minimize ? "none" : ""; │ │ │ │ - │ │ │ │ - this.layersDiv.style.display = minimize ? "none" : ""; │ │ │ │ + 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; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: loadContents │ │ │ │ - * Set up the labels and divs for the control │ │ │ │ + * 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 │ │ │ │ */ │ │ │ │ - loadContents: function() { │ │ │ │ + simplify: function(tolerance) { │ │ │ │ + if (this && this !== null) { │ │ │ │ + var points = this.getVertices(); │ │ │ │ + if (points.length < 3) { │ │ │ │ + return this; │ │ │ │ + } │ │ │ │ │ │ │ │ - // layers list div │ │ │ │ - this.layersDiv = document.createElement("div"); │ │ │ │ - this.layersDiv.id = this.id + "_layersDiv"; │ │ │ │ - OpenLayers.Element.addClass(this.layersDiv, "layersDiv"); │ │ │ │ + var compareNumbers = function(a, b) { │ │ │ │ + return (a - b); │ │ │ │ + }; │ │ │ │ │ │ │ │ - this.baseLbl = document.createElement("div"); │ │ │ │ - this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer"); │ │ │ │ - OpenLayers.Element.addClass(this.baseLbl, "baseLbl"); │ │ │ │ + /** │ │ │ │ + * Private function doing the Douglas-Peucker reduction │ │ │ │ + */ │ │ │ │ + var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance) { │ │ │ │ + var maxDistance = 0; │ │ │ │ + var indexFarthest = 0; │ │ │ │ │ │ │ │ - this.baseLayersDiv = document.createElement("div"); │ │ │ │ - OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv"); │ │ │ │ + for (var index = firstPoint, distance; index < lastPoint; index++) { │ │ │ │ + distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]); │ │ │ │ + if (distance > maxDistance) { │ │ │ │ + maxDistance = distance; │ │ │ │ + indexFarthest = index; │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - this.dataLbl = document.createElement("div"); │ │ │ │ - this.dataLbl.innerHTML = OpenLayers.i18n("Overlays"); │ │ │ │ - OpenLayers.Element.addClass(this.dataLbl, "dataLbl"); │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ + }; │ │ │ │ │ │ │ │ - this.dataLayersDiv = document.createElement("div"); │ │ │ │ - OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv"); │ │ │ │ + /** │ │ │ │ + * 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 │ │ │ │ │ │ │ │ - if (this.ascending) { │ │ │ │ - this.layersDiv.appendChild(this.baseLbl); │ │ │ │ - this.layersDiv.appendChild(this.baseLayersDiv); │ │ │ │ - this.layersDiv.appendChild(this.dataLbl); │ │ │ │ - this.layersDiv.appendChild(this.dataLayersDiv); │ │ │ │ - } else { │ │ │ │ - this.layersDiv.appendChild(this.dataLbl); │ │ │ │ - this.layersDiv.appendChild(this.dataLayersDiv); │ │ │ │ - this.layersDiv.appendChild(this.baseLbl); │ │ │ │ - this.layersDiv.appendChild(this.baseLayersDiv); │ │ │ │ - } │ │ │ │ + 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.div.appendChild(this.layersDiv); │ │ │ │ + return height; │ │ │ │ + }; │ │ │ │ │ │ │ │ - // maximize button div │ │ │ │ - var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png'); │ │ │ │ - this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ - "OpenLayers_Control_MaximizeDiv", │ │ │ │ - null, │ │ │ │ - null, │ │ │ │ - img, │ │ │ │ - "absolute"); │ │ │ │ - OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton"); │ │ │ │ - this.maximizeDiv.style.display = "none"; │ │ │ │ + var firstPoint = 0; │ │ │ │ + var lastPoint = points.length - 1; │ │ │ │ + var pointIndexsToKeep = []; │ │ │ │ │ │ │ │ - this.div.appendChild(this.maximizeDiv); │ │ │ │ + //Add the first and last index to the keepers │ │ │ │ + pointIndexsToKeep.push(firstPoint); │ │ │ │ + pointIndexsToKeep.push(lastPoint); │ │ │ │ │ │ │ │ - // minimize button div │ │ │ │ - var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png'); │ │ │ │ - this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ - "OpenLayers_Control_MinimizeDiv", │ │ │ │ - null, │ │ │ │ - null, │ │ │ │ - img, │ │ │ │ - "absolute"); │ │ │ │ - OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton"); │ │ │ │ - this.minimizeDiv.style.display = "none"; │ │ │ │ + //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); │ │ │ │ + } │ │ │ │ │ │ │ │ - this.div.appendChild(this.minimizeDiv); │ │ │ │ + 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.Control.LayerSwitcher" │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.LineString" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Control/Attribution.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/Control.js │ │ │ │ + * @requires OpenLayers/Geometry/Collection.js │ │ │ │ + * @requires OpenLayers/Geometry/LineString.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Control.Attribution │ │ │ │ - * The attribution control adds attribution from layers to the map display. │ │ │ │ - * It uses 'attribution' property of each layer. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Geometry.MultiLineString │ │ │ │ + * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString> │ │ │ │ + * components. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ + * - <OpenLayers.Geometry.Collection> │ │ │ │ + * - <OpenLayers.Geometry> │ │ │ │ */ │ │ │ │ -OpenLayers.Control.Attribution = │ │ │ │ - OpenLayers.Class(OpenLayers.Control, { │ │ │ │ +OpenLayers.Geometry.MultiLineString = OpenLayers.Class( │ │ │ │ + OpenLayers.Geometry.Collection, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: separator │ │ │ │ - * {String} String used to separate layers. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - separator: ", ", │ │ │ │ + componentTypes: ["OpenLayers.Geometry.LineString"], │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: template │ │ │ │ - * {String} Template for the attribution. This has to include the substring │ │ │ │ - * "${layers}", which will be replaced by the layer specific │ │ │ │ - * attributions, separated by <separator>. The default is "${layers}". │ │ │ │ + * Constructor: OpenLayers.Geometry.MultiLineString │ │ │ │ + * Constructor for a MultiLineString Geometry. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * components - {Array(<OpenLayers.Geometry.LineString>)} │ │ │ │ + * │ │ │ │ */ │ │ │ │ - template: "${layers}", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Control.Attribution │ │ │ │ + * Method: split │ │ │ │ + * Use this geometry (the source) to attempt to split a target geometry. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Options for control. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * Destroy control. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - this.map.events.un({ │ │ │ │ - "removelayer": this.updateAttribution, │ │ │ │ - "addlayer": this.updateAttribution, │ │ │ │ - "changelayer": this.updateAttribution, │ │ │ │ - "changebaselayer": this.updateAttribution, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ - * Initialize control. │ │ │ │ + * geometry - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ + * options - {Object} Properties of this object will be used to determine │ │ │ │ + * how the split is conducted. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * mutual - {Boolean} Split the source geometry in addition to the target │ │ │ │ + * geometry. Default is false. │ │ │ │ + * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ + * true. If false, a vertex on the source must be within the tolerance │ │ │ │ + * distance of the intersection to be considered a split. │ │ │ │ + * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ + * within the tolerance distance of an existing vertex on the source │ │ │ │ + * will be assumed to occur at the vertex. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A reference to the DIV DOMElement containing the control │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - │ │ │ │ - this.map.events.on({ │ │ │ │ - 'changebaselayer': this.updateAttribution, │ │ │ │ - 'changelayer': this.updateAttribution, │ │ │ │ - 'addlayer': this.updateAttribution, │ │ │ │ - 'removelayer': this.updateAttribution, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.updateAttribution(); │ │ │ │ - │ │ │ │ - return this.div; │ │ │ │ + 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: updateAttribution │ │ │ │ - * Update attribution string. │ │ │ │ + * Method: splitWith │ │ │ │ + * Split this geometry (the target) with the given geometry (the source). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} A geometry used to split this │ │ │ │ + * geometry (the source). │ │ │ │ + * options - {Object} Properties of this object will be used to determine │ │ │ │ + * how the split is conducted. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * mutual - {Boolean} Split the source geometry in addition to the target │ │ │ │ + * geometry. Default is false. │ │ │ │ + * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ + * true. If false, a vertex on the source must be within the tolerance │ │ │ │ + * distance of the intersection to be considered a split. │ │ │ │ + * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ + * within the tolerance distance of an existing vertex on the source │ │ │ │ + * will be assumed to occur at the vertex. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} A list of geometries (of this same type as the target) that │ │ │ │ + * result from splitting the target with the source geometry. The │ │ │ │ + * source and target geometry will remain unmodified. If no split │ │ │ │ + * results, null will be returned. If mutual is true and a split │ │ │ │ + * results, return will be an array of two arrays - the first will be │ │ │ │ + * all geometries that result from splitting the source geometry and │ │ │ │ + * the second will be all geometries that result from splitting the │ │ │ │ + * target geometry. │ │ │ │ */ │ │ │ │ - updateAttribution: function() { │ │ │ │ - var attributions = []; │ │ │ │ - if (this.map && this.map.layers) { │ │ │ │ - for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - if (layer.attribution && layer.getVisibility()) { │ │ │ │ - // add attribution only if attribution text is unique │ │ │ │ - if (OpenLayers.Util.indexOf( │ │ │ │ - attributions, layer.attribution) === -1) { │ │ │ │ - attributions.push(layer.attribution); │ │ │ │ + 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() │ │ │ │ + ]) │ │ │ │ + ]; │ │ │ │ } │ │ │ │ + │ │ │ │ } │ │ │ │ } │ │ │ │ - this.div.innerHTML = OpenLayers.String.format(this.template, { │ │ │ │ - layers: attributions.join(this.separator) │ │ │ │ - }); │ │ │ │ + } 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.Control.Attribution" │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.MultiLineString" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Handler.js │ │ │ │ + OpenLayers/Geometry/LinearRing.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ + * @requires OpenLayers/Geometry/LineString.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Handler │ │ │ │ - * Base class to construct a higher-level handler for event sequences. All │ │ │ │ - * handlers have activate and deactivate methods. In addition, they have │ │ │ │ - * methods named like browser events. When a handler is activated, any │ │ │ │ - * additional methods named like a browser event is registered as a │ │ │ │ - * listener for the corresponding event. When a handler is deactivated, │ │ │ │ - * those same methods are unregistered as event listeners. │ │ │ │ - * │ │ │ │ - * Handlers also typically have a callbacks object with keys named like │ │ │ │ - * the abstracted events or event sequences that they are in charge of │ │ │ │ - * handling. The controls that wrap handlers define the methods that │ │ │ │ - * correspond to these abstract events - so instead of listening for │ │ │ │ - * individual browser events, they only listen for the abstract events │ │ │ │ - * defined by the handler. │ │ │ │ - * │ │ │ │ - * Handlers are created by controls, which ultimately have the responsibility │ │ │ │ - * of making changes to the the state of the application. Handlers │ │ │ │ - * themselves may make temporary changes, but in general are expected to │ │ │ │ - * return the application in the same state that they found it. │ │ │ │ + * Class: OpenLayers.Geometry.LinearRing │ │ │ │ + * │ │ │ │ + * A Linear Ring is a special LineString which is closed. It closes itself │ │ │ │ + * automatically on every addPoint/removePoint by adding a copy of the first │ │ │ │ + * point as the last point. │ │ │ │ + * │ │ │ │ + * Also, as it is the first in the line family to close itself, a getArea() │ │ │ │ + * function is defined to calculate the enclosed area of the linearRing │ │ │ │ + * │ │ │ │ + * Inherits: │ │ │ │ + * - <OpenLayers.Geometry.LineString> │ │ │ │ */ │ │ │ │ -OpenLayers.Handler = OpenLayers.Class({ │ │ │ │ +OpenLayers.Geometry.LinearRing = OpenLayers.Class( │ │ │ │ + OpenLayers.Geometry.LineString, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: id │ │ │ │ - * {String} │ │ │ │ - */ │ │ │ │ - id: 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"], │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: control │ │ │ │ - * {<OpenLayers.Control>}. The control that initialized this handler. The │ │ │ │ - * control is assumed to have a valid map property - that map is used │ │ │ │ - * in the handler's own setMap method. │ │ │ │ - */ │ │ │ │ - control: null, │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Geometry.LinearRing │ │ │ │ + * Linear rings are constructed with an array of points. This array │ │ │ │ + * can represent a closed or open ring. If the ring is open (the last │ │ │ │ + * point does not equal the first point), the constructor will close │ │ │ │ + * the ring. If the ring is already closed (the last point does equal │ │ │ │ + * the first point), it will be left closed. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * points - {Array(<OpenLayers.Geometry.Point>)} points │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: map │ │ │ │ - * {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - map: 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 - {<OpenLayers.Geometry.Point>} │ │ │ │ + * index - {Integer} Index into the array to insert the component │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Was the Point successfully added? │ │ │ │ + */ │ │ │ │ + addComponent: function(point, index) { │ │ │ │ + var added = false; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: keyMask │ │ │ │ - * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler │ │ │ │ - * constants to construct a keyMask. The keyMask is used by │ │ │ │ - * <checkModifiers>. If the keyMask matches the combination of keys │ │ │ │ - * down on an event, checkModifiers returns true. │ │ │ │ - * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * // handler only responds if the Shift key is down │ │ │ │ - * handler.keyMask = OpenLayers.Handler.MOD_SHIFT; │ │ │ │ - * │ │ │ │ - * // handler only responds if Ctrl-Shift is down │ │ │ │ - * handler.keyMask = OpenLayers.Handler.MOD_SHIFT | │ │ │ │ - * OpenLayers.Handler.MOD_CTRL; │ │ │ │ - * (end) │ │ │ │ - */ │ │ │ │ - keyMask: null, │ │ │ │ + //remove last point │ │ │ │ + var lastPoint = this.components.pop(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: active │ │ │ │ - * {Boolean} │ │ │ │ - */ │ │ │ │ - active: false, │ │ │ │ + // 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: evt │ │ │ │ - * {Event} This property references the last event handled by the handler. │ │ │ │ - * Note that this property is not part of the stable API. Use of the │ │ │ │ - * evt property should be restricted to controls in the library │ │ │ │ - * or other applications that are willing to update with changes to │ │ │ │ - * the OpenLayers code. │ │ │ │ - */ │ │ │ │ - evt: null, │ │ │ │ + //append copy of first point │ │ │ │ + var firstPoint = this.components[0]; │ │ │ │ + OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, │ │ │ │ + [firstPoint]); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: touch │ │ │ │ - * {Boolean} Indicates the support of touch events. When touch events are │ │ │ │ - * started touch will be true and all mouse related listeners will do │ │ │ │ - * nothing. │ │ │ │ - */ │ │ │ │ - touch: false, │ │ │ │ + return added; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Handler │ │ │ │ - * Construct a handler. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} The control that initialized this │ │ │ │ - * handler. The control is assumed to have a valid map property; that │ │ │ │ - * map is used in the handler's own setMap method. If a map property │ │ │ │ - * is present in the options argument it will be used instead. │ │ │ │ - * callbacks - {Object} An object whose properties correspond to abstracted │ │ │ │ - * events or sequences of browser events. The values for these │ │ │ │ - * properties are functions defined by the control that get called by │ │ │ │ - * the handler. │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * the handler. │ │ │ │ - */ │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.control = control; │ │ │ │ - this.callbacks = callbacks; │ │ │ │ + /** │ │ │ │ + * APIMethod: removeComponent │ │ │ │ + * Removes a point from geometry components. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The component was removed. │ │ │ │ + */ │ │ │ │ + removeComponent: function(point) { │ │ │ │ + var removed = this.components && (this.components.length > 3); │ │ │ │ + if (removed) { │ │ │ │ + //remove last point │ │ │ │ + this.components.pop(); │ │ │ │ │ │ │ │ - var map = this.map || control.map; │ │ │ │ - if (map) { │ │ │ │ - this.setMap(map); │ │ │ │ - } │ │ │ │ + //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; │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * 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); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setMap │ │ │ │ - */ │ │ │ │ - setMap: function(map) { │ │ │ │ - this.map = map; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIMethod: rotate │ │ │ │ + * Rotate a geometry around some origin │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * angle - {Float} Rotation angle in degrees (measured counterclockwise │ │ │ │ + * from the positive x-axis) │ │ │ │ + * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation │ │ │ │ + */ │ │ │ │ + rotate: function(angle, origin) { │ │ │ │ + for (var i = 0, len = this.components.length; i < len - 1; ++i) { │ │ │ │ + this.components[i].rotate(angle, origin); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: checkModifiers │ │ │ │ - * Check the keyMask on the handler. If no <keyMask> is set, this always │ │ │ │ - * returns true. If a <keyMask> is set and it matches the combination │ │ │ │ - * of keys down on an event, this returns true. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The keyMask matches the keys down on an event. │ │ │ │ - */ │ │ │ │ - checkModifiers: function(evt) { │ │ │ │ - if (this.keyMask == null) { │ │ │ │ - return true; │ │ │ │ - } │ │ │ │ - /* calculate the keyboard modifier mask for this event */ │ │ │ │ - var keyModifiers = │ │ │ │ - (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) | │ │ │ │ - (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) | │ │ │ │ - (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) | │ │ │ │ - (evt.metaKey ? OpenLayers.Handler.MOD_META : 0); │ │ │ │ + /** │ │ │ │ + * APIMethod: resize │ │ │ │ + * Resize a geometry relative to some origin. Use this method to apply │ │ │ │ + * a uniform scaling to a geometry. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * scale - {Float} Factor by which to scale the geometry. A scale of 2 │ │ │ │ + * doubles the size of the geometry in each dimension │ │ │ │ + * (lines, for example, will be twice as long, and polygons │ │ │ │ + * will have four times the area). │ │ │ │ + * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing │ │ │ │ + * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} - The current geometry. │ │ │ │ + */ │ │ │ │ + resize: function(scale, origin, ratio) { │ │ │ │ + for (var i = 0, len = this.components.length; i < len - 1; ++i) { │ │ │ │ + this.components[i].resize(scale, origin, ratio); │ │ │ │ + } │ │ │ │ + return this; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /* if it differs from the handler object's key mask, │ │ │ │ - bail out of the event handler */ │ │ │ │ - return (keyModifiers == this.keyMask); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIMethod: transform │ │ │ │ + * Reproject the components geometry from source to dest. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * source - {<OpenLayers.Projection>} │ │ │ │ + * dest - {<OpenLayers.Projection>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} │ │ │ │ + */ │ │ │ │ + transform: function(source, dest) { │ │ │ │ + if (source && dest) { │ │ │ │ + for (var i = 0, len = this.components.length; i < len - 1; i++) { │ │ │ │ + var component = this.components[i]; │ │ │ │ + component.transform(source, dest); │ │ │ │ + } │ │ │ │ + this.bounds = null; │ │ │ │ + } │ │ │ │ + return this; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: activate │ │ │ │ - * Turn on the handler. Returns false if the handler was already active. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The handler was activated. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - // register for event handlers defined on this class. │ │ │ │ - var events = OpenLayers.Events.prototype.BROWSER_EVENTS; │ │ │ │ - for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ - if (this[events[i]]) { │ │ │ │ - this.register(events[i], this[events[i]]); │ │ │ │ + /** │ │ │ │ + * APIMethod: getCentroid │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry.Point>} The centroid of the collection │ │ │ │ + */ │ │ │ │ + getCentroid: function() { │ │ │ │ + if (this.components) { │ │ │ │ + var len = this.components.length; │ │ │ │ + if (len > 0 && len <= 2) { │ │ │ │ + return this.components[0].clone(); │ │ │ │ + } else if (len > 2) { │ │ │ │ + var sumX = 0.0; │ │ │ │ + var sumY = 0.0; │ │ │ │ + var x0 = this.components[0].x; │ │ │ │ + var y0 = this.components[0].y; │ │ │ │ + var area = -1 * this.getArea(); │ │ │ │ + if (area != 0) { │ │ │ │ + for (var i = 0; i < len - 1; i++) { │ │ │ │ + var b = this.components[i]; │ │ │ │ + var c = this.components[i + 1]; │ │ │ │ + sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0)); │ │ │ │ + sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0)); │ │ │ │ + } │ │ │ │ + var x = x0 + sumX / (6 * area); │ │ │ │ + var y = y0 + sumY / (6 * area); │ │ │ │ + } else { │ │ │ │ + for (var i = 0; i < len - 1; i++) { │ │ │ │ + sumX += this.components[i].x; │ │ │ │ + sumY += this.components[i].y; │ │ │ │ + } │ │ │ │ + var x = sumX / (len - 1); │ │ │ │ + var y = sumY / (len - 1); │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.Point(x, y); │ │ │ │ + } else { │ │ │ │ + return null; │ │ │ │ + } │ │ │ │ } │ │ │ │ - } │ │ │ │ - this.active = true; │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Turn off the handler. Returns false if the handler was already inactive. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The handler was deactivated. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (!this.active) { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - // unregister event handlers defined on this class. │ │ │ │ - var events = OpenLayers.Events.prototype.BROWSER_EVENTS; │ │ │ │ - for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ - if (this[events[i]]) { │ │ │ │ - this.unregister(events[i], this[events[i]]); │ │ │ │ + /** │ │ │ │ + * 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; │ │ │ │ } │ │ │ │ - } │ │ │ │ - this.touch = false; │ │ │ │ - this.active = false; │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ + return area; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: startTouch │ │ │ │ - * Start touch events, this method must be called by subclasses in │ │ │ │ - * "touchstart" method. When touch events are started <touch> will be │ │ │ │ - * true and all mouse related listeners will do nothing. │ │ │ │ - */ │ │ │ │ - startTouch: function() { │ │ │ │ - if (!this.touch) { │ │ │ │ - this.touch = true; │ │ │ │ - var events = [ │ │ │ │ - "mousedown", "mouseup", "mousemove", "click", "dblclick", │ │ │ │ - "mouseout" │ │ │ │ - ]; │ │ │ │ - for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ - if (this[events[i]]) { │ │ │ │ - this.unregister(events[i], this[events[i]]); │ │ │ │ + /** │ │ │ │ + * APIMethod: getGeodesicArea │ │ │ │ + * Calculate the approximate area of the polygon were it projected onto │ │ │ │ + * the earth. Note that this area will be positive if ring is oriented │ │ │ │ + * clockwise, otherwise it will be negative. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * projection - {<OpenLayers.Projection>} The spatial reference system │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ + * assumed. │ │ │ │ + * │ │ │ │ + * Reference: │ │ │ │ + * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for │ │ │ │ + * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion │ │ │ │ + * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {float} The approximate signed geodesic area of the polygon in square │ │ │ │ + * meters. │ │ │ │ + */ │ │ │ │ + getGeodesicArea: function(projection) { │ │ │ │ + var ring = this; // so we can work with a clone if needed │ │ │ │ + if (projection) { │ │ │ │ + var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + if (!gg.equals(projection)) { │ │ │ │ + ring = this.clone().transform(projection, gg); │ │ │ │ } │ │ │ │ } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + var area = 0.0; │ │ │ │ + var len = ring.components && ring.components.length; │ │ │ │ + if (len > 2) { │ │ │ │ + var p1, p2; │ │ │ │ + for (var i = 0; i < len - 1; i++) { │ │ │ │ + p1 = ring.components[i]; │ │ │ │ + p2 = ring.components[i + 1]; │ │ │ │ + area += OpenLayers.Util.rad(p2.x - p1.x) * │ │ │ │ + (2 + Math.sin(OpenLayers.Util.rad(p1.y)) + │ │ │ │ + Math.sin(OpenLayers.Util.rad(p2.y))); │ │ │ │ + } │ │ │ │ + area = area * 6378137.0 * 6378137.0 / 2.0; │ │ │ │ + } │ │ │ │ + return area; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: callback │ │ │ │ - * Trigger the control's named callback with the given arguments │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} The key for the callback that is one of the properties │ │ │ │ - * of the handler's callbacks object. │ │ │ │ - * args - {Array(*)} An array of arguments (any type) with which to call │ │ │ │ - * the callback (defined by the control). │ │ │ │ - */ │ │ │ │ - callback: function(name, args) { │ │ │ │ - if (name && this.callbacks[name]) { │ │ │ │ - this.callbacks[name].apply(this.control, args); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: containsPoint │ │ │ │ + * Test if a point is inside a linear ring. For the case where a point │ │ │ │ + * is coincident with a linear ring edge, returns 1. Otherwise, │ │ │ │ + * returns boolean. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean | Number} The point is inside the linear ring. Returns 1 if │ │ │ │ + * the point is coincident with an edge. Returns boolean otherwise. │ │ │ │ + */ │ │ │ │ + containsPoint: function(point) { │ │ │ │ + var approx = OpenLayers.Number.limitSigDigs; │ │ │ │ + var digs = 14; │ │ │ │ + var px = approx(point.x, digs); │ │ │ │ + var py = approx(point.y, digs); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: register │ │ │ │ - * register an event on the map │ │ │ │ - */ │ │ │ │ - register: function(name, method) { │ │ │ │ - // TODO: deal with registerPriority in 3.0 │ │ │ │ - this.map.events.registerPriority(name, this, method); │ │ │ │ - this.map.events.registerPriority(name, this, this.setEvent); │ │ │ │ - }, │ │ │ │ + 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); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: unregister │ │ │ │ - * unregister an event from the map │ │ │ │ - */ │ │ │ │ - unregister: function(name, method) { │ │ │ │ - this.map.events.unregister(name, this, method); │ │ │ │ - this.map.events.unregister(name, this, this.setEvent); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * The following conditions enforce five edge-crossing rules: │ │ │ │ + * 1. points coincident with edges are considered contained; │ │ │ │ + * 2. an upward edge includes its starting endpoint, and │ │ │ │ + * excludes its final endpoint; │ │ │ │ + * 3. a downward edge excludes its starting endpoint, and │ │ │ │ + * includes its final endpoint; │ │ │ │ + * 4. horizontal edges are excluded; and │ │ │ │ + * 5. the edge-ray intersection point must be strictly right │ │ │ │ + * of the point P. │ │ │ │ + */ │ │ │ │ + if (y1 == y2) { │ │ │ │ + // horizontal edge │ │ │ │ + if (py == y1) { │ │ │ │ + // point on horizontal line │ │ │ │ + if (x1 <= x2 && (px >= x1 && px <= x2) || // right or vert │ │ │ │ + x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert │ │ │ │ + // point on edge │ │ │ │ + crosses = -1; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + // ignore other horizontal edges │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ + cx = approx(getX(py, x1, y1, x2, y2), digs); │ │ │ │ + if (cx == px) { │ │ │ │ + // point on line │ │ │ │ + if (y1 < y2 && (py >= y1 && py <= y2) || // upward │ │ │ │ + y1 > y2 && (py <= y1 && py >= y2)) { // downward │ │ │ │ + // point on edge │ │ │ │ + crosses = -1; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (cx <= px) { │ │ │ │ + // no crossing to the right │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ + if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) { │ │ │ │ + // no crossing │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ + if (y1 < y2 && (py >= y1 && py < y2) || // upward │ │ │ │ + y1 > y2 && (py < y1 && py >= y2)) { // downward │ │ │ │ + ++crosses; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var contained = (crosses == -1) ? │ │ │ │ + // on edge │ │ │ │ + 1 : │ │ │ │ + // even (out) or odd (in) │ │ │ │ + !!(crosses & 1); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setEvent │ │ │ │ - * With each registered browser event, the handler sets its own evt │ │ │ │ - * property. This property can be accessed by controls if needed │ │ │ │ - * to get more information about the event that the handler is │ │ │ │ - * processing. │ │ │ │ - * │ │ │ │ - * This allows modifier keys on the event to be checked (alt, shift, ctrl, │ │ │ │ - * and meta cannot be checked with the keyboard handler). For a │ │ │ │ - * control to determine which modifier keys are associated with the │ │ │ │ - * event that a handler is currently processing, it should access │ │ │ │ - * (code)handler.evt.altKey || handler.evt.shiftKey || │ │ │ │ - * handler.evt.ctrlKey || handler.evt.metaKey(end). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} The browser event. │ │ │ │ - */ │ │ │ │ - setEvent: function(evt) { │ │ │ │ - this.evt = evt; │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ + return contained; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * Deconstruct the handler. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - // unregister event listeners │ │ │ │ - this.deactivate(); │ │ │ │ - // eliminate circular references │ │ │ │ - this.control = this.map = null; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIMethod: intersects │ │ │ │ + * Determine if the input geometry intersects this one. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} Any type of geometry. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The input geometry intersects this one. │ │ │ │ + */ │ │ │ │ + intersects: function(geometry) { │ │ │ │ + var intersect = false; │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ + intersect = this.containsPoint(geometry); │ │ │ │ + } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") { │ │ │ │ + intersect = geometry.intersects(this); │ │ │ │ + } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { │ │ │ │ + intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply( │ │ │ │ + this, [geometry] │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + // check for component intersections │ │ │ │ + for (var i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ + intersect = geometry.components[i].intersects(this); │ │ │ │ + if (intersect) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return intersect; │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Handler" │ │ │ │ -}); │ │ │ │ + /** │ │ │ │ + * 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. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: OpenLayers.Handler.MOD_NONE │ │ │ │ - * If set as the <keyMask>, <checkModifiers> returns false if any key is down. │ │ │ │ + * @requires OpenLayers/Geometry/Collection.js │ │ │ │ + * @requires OpenLayers/Geometry/LinearRing.js │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.MOD_NONE = 0; │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: OpenLayers.Handler.MOD_SHIFT │ │ │ │ - * If set as the <keyMask>, <checkModifiers> returns false if Shift is down. │ │ │ │ + * Class: OpenLayers.Geometry.Polygon │ │ │ │ + * Polygon is a collection of Geometry.LinearRings. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Geometry.Collection> │ │ │ │ + * - <OpenLayers.Geometry> │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.MOD_SHIFT = 1; │ │ │ │ +OpenLayers.Geometry.Polygon = OpenLayers.Class( │ │ │ │ + OpenLayers.Geometry.Collection, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: componentTypes │ │ │ │ + * {Array(String)} An array of class names representing the types of │ │ │ │ + * components that the collection can include. A null value means the │ │ │ │ + * component types are not restricted. │ │ │ │ + */ │ │ │ │ + componentTypes: ["OpenLayers.Geometry.LinearRing"], │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Geometry.Polygon │ │ │ │ + * Constructor for a Polygon geometry. │ │ │ │ + * The first ring (this.component[0])is the outer bounds of the polygon and │ │ │ │ + * all subsequent rings (this.component[1-n]) are internal holes. │ │ │ │ + * │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * components - {Array(<OpenLayers.Geometry.LinearRing>)} │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getArea │ │ │ │ + * Calculated by subtracting the areas of the internal holes from the │ │ │ │ + * area of the outer hole. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {float} The area of the geometry │ │ │ │ + */ │ │ │ │ + getArea: function() { │ │ │ │ + var area = 0.0; │ │ │ │ + if (this.components && (this.components.length > 0)) { │ │ │ │ + area += Math.abs(this.components[0].getArea()); │ │ │ │ + for (var i = 1, len = this.components.length; i < len; i++) { │ │ │ │ + area -= Math.abs(this.components[i].getArea()); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return area; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getGeodesicArea │ │ │ │ + * Calculate the approximate area of the polygon were it projected onto │ │ │ │ + * the earth. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * projection - {<OpenLayers.Projection>} The spatial reference system │ │ │ │ + * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ + * assumed. │ │ │ │ + * │ │ │ │ + * Reference: │ │ │ │ + * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for │ │ │ │ + * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion │ │ │ │ + * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409 │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {float} The approximate geodesic area of the polygon in square meters. │ │ │ │ + */ │ │ │ │ + getGeodesicArea: function(projection) { │ │ │ │ + var area = 0.0; │ │ │ │ + if (this.components && (this.components.length > 0)) { │ │ │ │ + area += Math.abs(this.components[0].getGeodesicArea(projection)); │ │ │ │ + for (var i = 1, len = this.components.length; i < len; i++) { │ │ │ │ + area -= Math.abs(this.components[i].getGeodesicArea(projection)); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return area; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: containsPoint │ │ │ │ + * Test if a point is inside a polygon. Points on a polygon edge are │ │ │ │ + * considered inside. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean | Number} The point is inside the polygon. Returns 1 if the │ │ │ │ + * point is on an edge. Returns boolean otherwise. │ │ │ │ + */ │ │ │ │ + containsPoint: function(point) { │ │ │ │ + var numRings = this.components.length; │ │ │ │ + var contained = false; │ │ │ │ + if (numRings > 0) { │ │ │ │ + // check exterior ring - 1 means on edge, boolean otherwise │ │ │ │ + contained = this.components[0].containsPoint(point); │ │ │ │ + if (contained !== 1) { │ │ │ │ + if (contained && numRings > 1) { │ │ │ │ + // check interior rings │ │ │ │ + var hole; │ │ │ │ + for (var i = 1; i < numRings; ++i) { │ │ │ │ + hole = this.components[i].containsPoint(point); │ │ │ │ + if (hole) { │ │ │ │ + if (hole === 1) { │ │ │ │ + // on edge │ │ │ │ + contained = 1; │ │ │ │ + } else { │ │ │ │ + // in hole │ │ │ │ + contained = false; │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return contained; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: intersects │ │ │ │ + * Determine if the input geometry intersects this one. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} Any type of geometry. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The input geometry intersects this one. │ │ │ │ + */ │ │ │ │ + intersects: function(geometry) { │ │ │ │ + var intersect = false; │ │ │ │ + var i, len; │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ + intersect = this.containsPoint(geometry); │ │ │ │ + } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" || │ │ │ │ + geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { │ │ │ │ + // check if rings/linestrings intersect │ │ │ │ + for (i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ + intersect = geometry.intersects(this.components[i]); │ │ │ │ + if (intersect) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (!intersect) { │ │ │ │ + // check if this poly contains points of the ring/linestring │ │ │ │ + for (i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ + intersect = this.containsPoint(geometry.components[i]); │ │ │ │ + if (intersect) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + for (i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ + intersect = this.intersects(geometry.components[i]); │ │ │ │ + if (intersect) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + // check case where this poly is wholly contained by another │ │ │ │ + if (!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") { │ │ │ │ + // exterior ring points will be contained in the other geometry │ │ │ │ + var ring = this.components[0]; │ │ │ │ + for (i = 0, len = ring.components.length; i < len; ++i) { │ │ │ │ + intersect = geometry.containsPoint(ring.components[i]); │ │ │ │ + if (intersect) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return intersect; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: distanceTo │ │ │ │ + * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ + * options - {Object} Optional properties for configuring the distance │ │ │ │ + * calculation. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * details - {Boolean} Return details from the distance calculation. │ │ │ │ + * Default is false. │ │ │ │ + * edge - {Boolean} Calculate the distance from this geometry to the │ │ │ │ + * nearest edge of the target geometry. Default is true. If true, │ │ │ │ + * calling distanceTo from a geometry that is wholly contained within │ │ │ │ + * the target will result in a non-zero distance. If false, whenever │ │ │ │ + * geometries intersect, calling distanceTo will return 0. If false, │ │ │ │ + * details cannot be returned. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Number | Object} The distance between this geometry and the target. │ │ │ │ + * If details is true, the return will be an object with distance, │ │ │ │ + * x0, y0, x1, and y1 properties. The x0 and y0 properties represent │ │ │ │ + * the coordinates of the closest point on this geometry. The x1 and y1 │ │ │ │ + * properties represent the coordinates of the closest point on the │ │ │ │ + * target geometry. │ │ │ │ + */ │ │ │ │ + distanceTo: function(geometry, options) { │ │ │ │ + var edge = !(options && options.edge === false); │ │ │ │ + var result; │ │ │ │ + // this is the case where we might not be looking for distance to edge │ │ │ │ + if (!edge && this.intersects(geometry)) { │ │ │ │ + result = 0; │ │ │ │ + } else { │ │ │ │ + result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply( │ │ │ │ + this, [geometry, options] │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return result; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.Polygon" │ │ │ │ + }); │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: OpenLayers.Handler.MOD_CTRL │ │ │ │ - * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down. │ │ │ │ + * APIMethod: createRegularPolygon │ │ │ │ + * Create a regular polygon around a radius. Useful for creating circles │ │ │ │ + * and the like. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * origin - {<OpenLayers.Geometry.Point>} center of polygon. │ │ │ │ + * radius - {Float} distance to vertex, in map units. │ │ │ │ + * sides - {Integer} Number of sides. 20 approximates a circle. │ │ │ │ + * rotation - {Float} original angle of rotation, in degrees. │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.MOD_CTRL = 2; │ │ │ │ +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. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: OpenLayers.Handler.MOD_ALT │ │ │ │ - * If set as the <keyMask>, <checkModifiers> returns false if Alt is down. │ │ │ │ + * @requires OpenLayers/Geometry/Collection.js │ │ │ │ + * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.MOD_ALT = 4; │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: OpenLayers.Handler.MOD_META │ │ │ │ - * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down. │ │ │ │ + * Class: OpenLayers.Geometry.MultiPolygon │ │ │ │ + * MultiPolygon is a geometry with multiple <OpenLayers.Geometry.Polygon> │ │ │ │ + * components. Create a new instance with the <OpenLayers.Geometry.MultiPolygon> │ │ │ │ + * constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Geometry.Collection> │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.MOD_META = 8; │ │ │ │ +OpenLayers.Geometry.MultiPolygon = OpenLayers.Class( │ │ │ │ + OpenLayers.Geometry.Collection, { │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Property: componentTypes │ │ │ │ + * {Array(String)} An array of class names representing the types of │ │ │ │ + * components that the collection can include. A null value means the │ │ │ │ + * component types are not restricted. │ │ │ │ + */ │ │ │ │ + componentTypes: ["OpenLayers.Geometry.Polygon"], │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Geometry.MultiPolygon │ │ │ │ + * Create a new MultiPolygon geometry │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * components - {Array(<OpenLayers.Geometry.Polygon>)} An array of polygons │ │ │ │ + * used to generate the MultiPolygon │ │ │ │ + * │ │ │ │ + */ │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.MultiPolygon" │ │ │ │ + }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Handler/Drag.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/Handler.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.Handler.Drag │ │ │ │ - * The drag handler is used to deal with sequences of browser events related │ │ │ │ - * to dragging. The handler is used by controls that want to know when │ │ │ │ - * a drag sequence begins, when a drag is happening, and when it has │ │ │ │ - * finished. │ │ │ │ - * │ │ │ │ - * Controls that use the drag handler typically construct it with callbacks │ │ │ │ - * for 'down', 'move', and 'done'. Callbacks for these keys are called │ │ │ │ - * when the drag begins, with each move, and when the drag is done. In │ │ │ │ - * addition, controls can have callbacks keyed to 'up' and 'out' if they │ │ │ │ - * care to differentiate between the types of events that correspond with │ │ │ │ - * the end of a drag sequence. If no drag actually occurs (no mouse move) │ │ │ │ - * the 'down' and 'up' callbacks will be called, but not the 'done' │ │ │ │ - * callback. │ │ │ │ - * │ │ │ │ - * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor. │ │ │ │ + * Class: OpenLayers.Format.GeoJSON │ │ │ │ + * Read and write GeoJSON. Create a new parser with the │ │ │ │ + * <OpenLayers.Format.GeoJSON> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Handler> │ │ │ │ + * - <OpenLayers.Format.JSON> │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: started │ │ │ │ - * {Boolean} When a mousedown or touchstart event is received, we want to │ │ │ │ - * record it, but not set 'dragging' until the mouse moves after starting. │ │ │ │ - */ │ │ │ │ - started: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: stopDown │ │ │ │ - * {Boolean} Stop propagation of mousedown events from getting to listeners │ │ │ │ - * on the same element. Default is true. │ │ │ │ - */ │ │ │ │ - stopDown: true, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: dragging │ │ │ │ - * {Boolean} │ │ │ │ - */ │ │ │ │ - dragging: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: last │ │ │ │ - * {<OpenLayers.Pixel>} The last pixel location of the drag. │ │ │ │ - */ │ │ │ │ - last: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: start │ │ │ │ - * {<OpenLayers.Pixel>} The first pixel location of the drag. │ │ │ │ - */ │ │ │ │ - start: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: lastMoveEvt │ │ │ │ - * {Object} The last mousemove event that occurred. Used to │ │ │ │ - * position the map correctly when our "delay drag" │ │ │ │ - * timeout expired. │ │ │ │ - */ │ │ │ │ - lastMoveEvt: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: oldOnselectstart │ │ │ │ - * {Function} │ │ │ │ - */ │ │ │ │ - oldOnselectstart: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: interval │ │ │ │ - * {Integer} In order to increase performance, an interval (in │ │ │ │ - * milliseconds) can be set to reduce the number of drag events │ │ │ │ - * called. If set, a new drag event will not be set until the │ │ │ │ - * interval has passed. │ │ │ │ - * Defaults to 0, meaning no interval. │ │ │ │ - */ │ │ │ │ - interval: 0, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: timeoutId │ │ │ │ - * {String} The id of the timeout used for the mousedown interval. │ │ │ │ - * This is "private", and should be left alone. │ │ │ │ - */ │ │ │ │ - timeoutId: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: documentDrag │ │ │ │ - * {Boolean} If set to true, the handler will also handle mouse moves when │ │ │ │ - * the cursor has moved out of the map viewport. Default is false. │ │ │ │ - */ │ │ │ │ - documentDrag: false, │ │ │ │ +OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: documentEvents │ │ │ │ - * {Boolean} Are we currently observing document events? │ │ │ │ + * APIProperty: ignoreExtraDims │ │ │ │ + * {Boolean} Ignore dimensions higher than 2 when reading geometry │ │ │ │ + * coordinates. │ │ │ │ */ │ │ │ │ - documentEvents: null, │ │ │ │ + ignoreExtraDims: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Handler.Drag │ │ │ │ - * Returns OpenLayers.Handler.Drag │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Format.GeoJSON │ │ │ │ + * Create a new parser for GeoJSON. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} The control that is making use of │ │ │ │ - * this handler. If a handler is being used without a control, the │ │ │ │ - * handlers setMap method must be overridden to deal properly with │ │ │ │ - * the map. │ │ │ │ - * callbacks - {Object} An object containing a single function to be │ │ │ │ - * called when the drag operation is finished. The callback should │ │ │ │ - * expect to recieve a single argument, the pixel location of the event. │ │ │ │ - * Callbacks for 'move' and 'done' are supported. You can also speficy │ │ │ │ - * callbacks for 'down', 'up', and 'out' to respond to those events. │ │ │ │ - * options - {Object} │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * this instance. │ │ │ │ */ │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - │ │ │ │ - if (this.documentDrag === true) { │ │ │ │ - var me = this; │ │ │ │ - this._docMove = function(evt) { │ │ │ │ - me.mousemove({ │ │ │ │ - xy: { │ │ │ │ - x: evt.clientX, │ │ │ │ - y: evt.clientY │ │ │ │ - }, │ │ │ │ - element: document │ │ │ │ - }); │ │ │ │ - }; │ │ │ │ - this._docUp = function(evt) { │ │ │ │ - me.mouseup({ │ │ │ │ - xy: { │ │ │ │ - x: evt.clientX, │ │ │ │ - y: evt.clientY │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: dragstart │ │ │ │ - * This private method is factorized from mousedown and touchstart methods │ │ │ │ + * APIMethod: read │ │ │ │ + * Deserialize a GeoJSON string. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} The event │ │ │ │ + * json - {String} A GeoJSON string │ │ │ │ + * type - {String} Optional string that determines the structure of │ │ │ │ + * the output. Supported values are "Geometry", "Feature", and │ │ │ │ + * "FeatureCollection". If absent or null, a default of │ │ │ │ + * "FeatureCollection" is assumed. │ │ │ │ + * filter - {Function} A function which will be called for every key and │ │ │ │ + * value at every level of the final result. Each value will be │ │ │ │ + * replaced by the result of the filter function. This can be used to │ │ │ │ + * reform generic objects into instances of classes, or to transform │ │ │ │ + * date strings into Date objects. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ + * Returns: │ │ │ │ + * {Object} The return depends on the value of the type argument. If type │ │ │ │ + * is "FeatureCollection" (the default), the return will be an array │ │ │ │ + * of <OpenLayers.Feature.Vector>. If type is "Geometry", the input json │ │ │ │ + * must represent a single geometry, and the return will be an │ │ │ │ + * <OpenLayers.Geometry>. If type is "Feature", the input json must │ │ │ │ + * represent a single feature, and the return will be an │ │ │ │ + * <OpenLayers.Feature.Vector>. │ │ │ │ */ │ │ │ │ - dragstart: function(evt) { │ │ │ │ - var propagate = true; │ │ │ │ - this.dragging = false; │ │ │ │ - if (this.checkModifiers(evt) && │ │ │ │ - (OpenLayers.Event.isLeftClick(evt) || │ │ │ │ - OpenLayers.Event.isSingleTouch(evt))) { │ │ │ │ - this.started = true; │ │ │ │ - this.start = evt.xy; │ │ │ │ - this.last = evt.xy; │ │ │ │ - OpenLayers.Element.addClass( │ │ │ │ - this.map.viewPortDiv, "olDragDown" │ │ │ │ - ); │ │ │ │ - this.down(evt); │ │ │ │ - this.callback("down", [evt.xy]); │ │ │ │ - │ │ │ │ - // prevent document dragging │ │ │ │ - OpenLayers.Event.preventDefault(evt); │ │ │ │ - │ │ │ │ - if (!this.oldOnselectstart) { │ │ │ │ - this.oldOnselectstart = document.onselectstart ? │ │ │ │ - document.onselectstart : OpenLayers.Function.True; │ │ │ │ - } │ │ │ │ - document.onselectstart = OpenLayers.Function.False; │ │ │ │ - │ │ │ │ - propagate = !this.stopDown; │ │ │ │ + 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 { │ │ │ │ - this.started = false; │ │ │ │ - this.start = null; │ │ │ │ - this.last = null; │ │ │ │ + obj = json; │ │ │ │ } │ │ │ │ - return propagate; │ │ │ │ + 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: dragmove │ │ │ │ - * This private method is factorized from mousemove and touchmove methods │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} The event │ │ │ │ + * Method: isValidType │ │ │ │ + * Check if a GeoJSON object is a valid representative of the given type. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ + * {Boolean} The object is valid GeoJSON object of the given type. │ │ │ │ */ │ │ │ │ - dragmove: function(evt) { │ │ │ │ - this.lastMoveEvt = evt; │ │ │ │ - if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || │ │ │ │ - evt.xy.y != this.last.y)) { │ │ │ │ - if (this.documentDrag === true && this.documentEvents) { │ │ │ │ - if (evt.element === document) { │ │ │ │ - this.adjustXY(evt); │ │ │ │ - // do setEvent manually because the documentEvents are not │ │ │ │ - // registered with the map │ │ │ │ - this.setEvent(evt); │ │ │ │ + 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 { │ │ │ │ - this.removeDocumentEvents(); │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - } │ │ │ │ - if (this.interval > 0) { │ │ │ │ - this.timeoutId = setTimeout( │ │ │ │ - OpenLayers.Function.bind(this.removeTimeout, this), │ │ │ │ - this.interval); │ │ │ │ - } │ │ │ │ - this.dragging = true; │ │ │ │ - │ │ │ │ - this.move(evt); │ │ │ │ - this.callback("move", [evt.xy]); │ │ │ │ - if (!this.oldOnselectstart) { │ │ │ │ - this.oldOnselectstart = document.onselectstart; │ │ │ │ - document.onselectstart = OpenLayers.Function.False; │ │ │ │ - } │ │ │ │ - this.last = evt.xy; │ │ │ │ } │ │ │ │ - return true; │ │ │ │ + return valid; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: dragend │ │ │ │ - * This private method is factorized from mouseup and touchend methods │ │ │ │ + * Method: parseFeature │ │ │ │ + * Convert a feature object from GeoJSON into an │ │ │ │ + * <OpenLayers.Feature.Vector>. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} The event │ │ │ │ + * obj - {Object} An object created from a GeoJSON object │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ + * {<OpenLayers.Feature.Vector>} A feature. │ │ │ │ */ │ │ │ │ - dragend: function(evt) { │ │ │ │ - if (this.started) { │ │ │ │ - if (this.documentDrag === true && this.documentEvents) { │ │ │ │ - this.adjustXY(evt); │ │ │ │ - this.removeDocumentEvents(); │ │ │ │ - } │ │ │ │ - var dragged = (this.start != this.last); │ │ │ │ - this.started = false; │ │ │ │ - this.dragging = false; │ │ │ │ - OpenLayers.Element.removeClass( │ │ │ │ - this.map.viewPortDiv, "olDragDown" │ │ │ │ - ); │ │ │ │ - this.up(evt); │ │ │ │ - this.callback("up", [evt.xy]); │ │ │ │ - if (dragged) { │ │ │ │ - this.callback("done", [evt.xy]); │ │ │ │ - } │ │ │ │ - document.onselectstart = this.oldOnselectstart; │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - return true; │ │ │ │ + feature = new OpenLayers.Feature.Vector(geometry, attributes); │ │ │ │ + if (bbox) { │ │ │ │ + feature.bounds = OpenLayers.Bounds.fromArray(bbox); │ │ │ │ + } │ │ │ │ + if (obj.id) { │ │ │ │ + feature.fid = obj.id; │ │ │ │ + } │ │ │ │ + return feature; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * The four methods below (down, move, up, and out) are used by subclasses │ │ │ │ - * to do their own processing related to these mouse events. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: down │ │ │ │ - * This method is called during the handling of the mouse down event. │ │ │ │ - * Subclasses can do their own processing here. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} The mouse down event │ │ │ │ - */ │ │ │ │ - down: function(evt) {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: move │ │ │ │ - * This method is called during the handling of the mouse move event. │ │ │ │ - * Subclasses can do their own processing here. │ │ │ │ + * Method: parseGeometry │ │ │ │ + * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} The mouse move event │ │ │ │ - * │ │ │ │ - */ │ │ │ │ - move: function(evt) {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: up │ │ │ │ - * This method is called during the handling of the mouse up event. │ │ │ │ - * Subclasses can do their own processing here. │ │ │ │ + * obj - {Object} An object created from a GeoJSON object │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} The mouse up event │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} A geometry. │ │ │ │ */ │ │ │ │ - up: function(evt) {}, │ │ │ │ + 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; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: out │ │ │ │ - * This method is called during the handling of the mouse out event. │ │ │ │ - * Subclasses can do their own processing here. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} The mouse out event │ │ │ │ + * Property: parseCoords │ │ │ │ + * Object with properties corresponding to the GeoJSON geometry types. │ │ │ │ + * Property values are functions that do the actual parsing. │ │ │ │ */ │ │ │ │ - out: function(evt) {}, │ │ │ │ + parseCoords: { │ │ │ │ + /** │ │ │ │ + * Method: parseCoords.point │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ + * <OpenLayers.Geometry>. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * array - {Object} The coordinates array from the GeoJSON fragment. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} A geometry. │ │ │ │ + */ │ │ │ │ + "point": function(array) { │ │ │ │ + if (this.ignoreExtraDims == false && │ │ │ │ + array.length != 2) { │ │ │ │ + throw "Only 2D points are supported: " + array; │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.Point(array[0], array[1]); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * The methods below are part of the magic of event handling. Because │ │ │ │ - * they are named like browser events, they are registered as listeners │ │ │ │ - * for the events they represent. │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * Method: parseCoords.multipoint │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ + * <OpenLayers.Geometry>. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * array - {Object} The coordinates array from the GeoJSON fragment. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} A geometry. │ │ │ │ + */ │ │ │ │ + "multipoint": function(array) { │ │ │ │ + var points = []; │ │ │ │ + var p = null; │ │ │ │ + for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ + try { │ │ │ │ + p = this.parseCoords["point"].apply(this, [array[i]]); │ │ │ │ + } catch (err) { │ │ │ │ + throw err; │ │ │ │ + } │ │ │ │ + points.push(p); │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.MultiPoint(points); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: mousedown │ │ │ │ - * Handle mousedown events │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ - */ │ │ │ │ - mousedown: function(evt) { │ │ │ │ - return this.dragstart(evt); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: parseCoords.linestring │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ + * <OpenLayers.Geometry>. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * array - {Object} The coordinates array from the GeoJSON fragment. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} A geometry. │ │ │ │ + */ │ │ │ │ + "linestring": function(array) { │ │ │ │ + var points = []; │ │ │ │ + var p = null; │ │ │ │ + for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ + try { │ │ │ │ + p = this.parseCoords["point"].apply(this, [array[i]]); │ │ │ │ + } catch (err) { │ │ │ │ + throw err; │ │ │ │ + } │ │ │ │ + points.push(p); │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.LineString(points); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: touchstart │ │ │ │ - * Handle touchstart events │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ - */ │ │ │ │ - touchstart: function(evt) { │ │ │ │ - this.startTouch(); │ │ │ │ - return this.dragstart(evt); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: parseCoords.multilinestring │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ + * <OpenLayers.Geometry>. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * array - {Object} The coordinates array from the GeoJSON fragment. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} A geometry. │ │ │ │ + */ │ │ │ │ + "multilinestring": function(array) { │ │ │ │ + var lines = []; │ │ │ │ + var l = null; │ │ │ │ + for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ + try { │ │ │ │ + l = this.parseCoords["linestring"].apply(this, [array[i]]); │ │ │ │ + } catch (err) { │ │ │ │ + throw err; │ │ │ │ + } │ │ │ │ + lines.push(l); │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.MultiLineString(lines); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: mousemove │ │ │ │ - * Handle mousemove events │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ - */ │ │ │ │ - mousemove: function(evt) { │ │ │ │ - return this.dragmove(evt); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: parseCoords.polygon │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ + * <OpenLayers.Geometry>. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} A geometry. │ │ │ │ + */ │ │ │ │ + "polygon": function(array) { │ │ │ │ + var rings = []; │ │ │ │ + var r, l; │ │ │ │ + for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ + try { │ │ │ │ + l = this.parseCoords["linestring"].apply(this, [array[i]]); │ │ │ │ + } catch (err) { │ │ │ │ + throw err; │ │ │ │ + } │ │ │ │ + r = new OpenLayers.Geometry.LinearRing(l.components); │ │ │ │ + rings.push(r); │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.Polygon(rings); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: touchmove │ │ │ │ - * Handle touchmove events │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ - */ │ │ │ │ - touchmove: function(evt) { │ │ │ │ - return this.dragmove(evt); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: parseCoords.multipolygon │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ + * <OpenLayers.Geometry>. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * array - {Object} The coordinates array from the GeoJSON fragment. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} A geometry. │ │ │ │ + */ │ │ │ │ + "multipolygon": function(array) { │ │ │ │ + var polys = []; │ │ │ │ + var p = null; │ │ │ │ + for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ + try { │ │ │ │ + p = this.parseCoords["polygon"].apply(this, [array[i]]); │ │ │ │ + } catch (err) { │ │ │ │ + throw err; │ │ │ │ + } │ │ │ │ + polys.push(p); │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.MultiPolygon(polys); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: removeTimeout │ │ │ │ - * Private. Called by mousemove() to remove the drag timeout. │ │ │ │ - */ │ │ │ │ - removeTimeout: function() { │ │ │ │ - this.timeoutId = null; │ │ │ │ - // if timeout expires while we're still dragging (mouseup │ │ │ │ - // hasn't occurred) then call mousemove to move to the │ │ │ │ - // correct position │ │ │ │ - if (this.dragging) { │ │ │ │ - this.mousemove(this.lastMoveEvt); │ │ │ │ + /** │ │ │ │ + * Method: parseCoords.box │ │ │ │ + * Convert a coordinate array from GeoJSON into an │ │ │ │ + * <OpenLayers.Geometry>. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * array - {Object} The coordinates array from the GeoJSON fragment. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Geometry>} A geometry. │ │ │ │ + */ │ │ │ │ + "box": function(array) { │ │ │ │ + if (array.length != 2) { │ │ │ │ + throw "GeoJSON box coordinates must have 2 elements"; │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.Polygon([ │ │ │ │ + new OpenLayers.Geometry.LinearRing([ │ │ │ │ + new OpenLayers.Geometry.Point(array[0][0], array[0][1]), │ │ │ │ + new OpenLayers.Geometry.Point(array[1][0], array[0][1]), │ │ │ │ + new OpenLayers.Geometry.Point(array[1][0], array[1][1]), │ │ │ │ + new OpenLayers.Geometry.Point(array[0][0], array[1][1]), │ │ │ │ + new OpenLayers.Geometry.Point(array[0][0], array[0][1]) │ │ │ │ + ]) │ │ │ │ + ]); │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: mouseup │ │ │ │ - * Handle mouseup events │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ - */ │ │ │ │ - mouseup: function(evt) { │ │ │ │ - return this.dragend(evt); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: touchend │ │ │ │ - * Handle touchend events │ │ │ │ + * APIMethod: write │ │ │ │ + * Serialize a feature, geometry, array of features into a GeoJSON string. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>, │ │ │ │ + * or an array of features. │ │ │ │ + * pretty - {Boolean} Structure the output with newlines and indentation. │ │ │ │ + * Default is false. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ + * {String} The GeoJSON string representation of the input geometry, │ │ │ │ + * features, or array of features. │ │ │ │ */ │ │ │ │ - touchend: function(evt) { │ │ │ │ - // override evt.xy with last position since touchend does not have │ │ │ │ - // any touch position │ │ │ │ - evt.xy = this.last; │ │ │ │ - return this.dragend(evt); │ │ │ │ + write: function(obj, pretty) { │ │ │ │ + var geojson = { │ │ │ │ + "type": null │ │ │ │ + }; │ │ │ │ + if (OpenLayers.Util.isArray(obj)) { │ │ │ │ + geojson.type = "FeatureCollection"; │ │ │ │ + var numFeatures = obj.length; │ │ │ │ + geojson.features = new Array(numFeatures); │ │ │ │ + for (var i = 0; i < numFeatures; ++i) { │ │ │ │ + var element = obj[i]; │ │ │ │ + if (!element instanceof OpenLayers.Feature.Vector) { │ │ │ │ + var msg = "FeatureCollection only supports collections " + │ │ │ │ + "of features: " + element; │ │ │ │ + throw msg; │ │ │ │ + } │ │ │ │ + geojson.features[i] = this.extract.feature.apply( │ │ │ │ + this, [element] │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } else if (obj.CLASS_NAME.indexOf("OpenLayers.Geometry") == 0) { │ │ │ │ + geojson = this.extract.geometry.apply(this, [obj]); │ │ │ │ + } else if (obj instanceof OpenLayers.Feature.Vector) { │ │ │ │ + geojson = this.extract.feature.apply(this, [obj]); │ │ │ │ + if (obj.layer && obj.layer.projection) { │ │ │ │ + geojson.crs = this.createCRSObject(obj); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ + [geojson, pretty]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: mouseout │ │ │ │ - * Handle mouseout events │ │ │ │ + * Method: createCRSObject │ │ │ │ + * Create the CRS object for an object. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * object - {<OpenLayers.Feature.Vector>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ + * {Object} An object which can be assigned to the crs property │ │ │ │ + * of a GeoJSON object. │ │ │ │ */ │ │ │ │ - mouseout: function(evt) { │ │ │ │ - if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) { │ │ │ │ - if (this.documentDrag === true) { │ │ │ │ - this.addDocumentEvents(); │ │ │ │ + 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 { │ │ │ │ - var dragged = (this.start != this.last); │ │ │ │ - this.started = false; │ │ │ │ - this.dragging = false; │ │ │ │ - OpenLayers.Element.removeClass( │ │ │ │ - this.map.viewPortDiv, "olDragDown" │ │ │ │ - ); │ │ │ │ - this.out(evt); │ │ │ │ - this.callback("out", []); │ │ │ │ - if (dragged) { │ │ │ │ - this.callback("done", [evt.xy]); │ │ │ │ - } │ │ │ │ - if (document.onselectstart) { │ │ │ │ - document.onselectstart = this.oldOnselectstart; │ │ │ │ - } │ │ │ │ + crs = { │ │ │ │ + "type": "name", │ │ │ │ + "properties": { │ │ │ │ + "name": "EPSG:" + code │ │ │ │ + } │ │ │ │ + }; │ │ │ │ } │ │ │ │ } │ │ │ │ - return true; │ │ │ │ + return crs; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: click │ │ │ │ - * The drag handler captures the click event. If something else registers │ │ │ │ - * for clicks on the same element, its listener will not be called │ │ │ │ - * after a drag. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ + * Property: extract │ │ │ │ + * Object with properties corresponding to the GeoJSON types. │ │ │ │ + * Property values are functions that do the actual value extraction. │ │ │ │ */ │ │ │ │ - click: function(evt) { │ │ │ │ - // let the click event propagate only if the mouse moved │ │ │ │ - return (this.start == this.last); │ │ │ │ - }, │ │ │ │ + extract: { │ │ │ │ + /** │ │ │ │ + * Method: extract.feature │ │ │ │ + * Return a partial GeoJSON object representing a single feature. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An object representing the point. │ │ │ │ + */ │ │ │ │ + 'feature': function(feature) { │ │ │ │ + var geom = this.extract.geometry.apply(this, [feature.geometry]); │ │ │ │ + var json = { │ │ │ │ + "type": "Feature", │ │ │ │ + "properties": feature.attributes, │ │ │ │ + "geometry": geom │ │ │ │ + }; │ │ │ │ + if (feature.fid != null) { │ │ │ │ + json.id = feature.fid; │ │ │ │ + } │ │ │ │ + return json; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: activate │ │ │ │ - * Activate the handler. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The handler was successfully activated. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.dragging = false; │ │ │ │ - activated = true; │ │ │ │ - } │ │ │ │ - return activated; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: extract.geometry │ │ │ │ + * Return a GeoJSON object representing a single geometry. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} An object representing the geometry. │ │ │ │ + */ │ │ │ │ + 'geometry': function(geometry) { │ │ │ │ + if (geometry == null) { │ │ │ │ + return null; │ │ │ │ + } │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry = geometry.clone(); │ │ │ │ + geometry.transform(this.internalProjection, │ │ │ │ + this.externalProjection); │ │ │ │ + } │ │ │ │ + var geometryType = geometry.CLASS_NAME.split('.')[2]; │ │ │ │ + var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]); │ │ │ │ + var json; │ │ │ │ + if (geometryType == "Collection") { │ │ │ │ + json = { │ │ │ │ + "type": "GeometryCollection", │ │ │ │ + "geometries": data │ │ │ │ + }; │ │ │ │ + } else { │ │ │ │ + json = { │ │ │ │ + "type": geometryType, │ │ │ │ + "coordinates": data │ │ │ │ + }; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Deactivate the handler. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The handler was successfully deactivated. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.started = false; │ │ │ │ - this.dragging = false; │ │ │ │ - this.start = null; │ │ │ │ - this.last = null; │ │ │ │ - deactivated = true; │ │ │ │ - OpenLayers.Element.removeClass( │ │ │ │ - this.map.viewPortDiv, "olDragDown" │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - return deactivated; │ │ │ │ - }, │ │ │ │ + return json; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: extract.point │ │ │ │ + * Return an array of coordinates from a point. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * point - {<OpenLayers.Geometry.Point>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} An array of coordinates representing the point. │ │ │ │ + */ │ │ │ │ + 'point': function(point) { │ │ │ │ + return [point.x, point.y]; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: extract.multipoint │ │ │ │ + * Return an array of point coordinates from a multipoint. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * multipoint - {<OpenLayers.Geometry.MultiPoint>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} An array of point coordinate arrays representing │ │ │ │ + * the multipoint. │ │ │ │ + */ │ │ │ │ + 'multipoint': function(multipoint) { │ │ │ │ + var array = []; │ │ │ │ + for (var i = 0, len = multipoint.components.length; i < len; ++i) { │ │ │ │ + array.push(this.extract.point.apply(this, [multipoint.components[i]])); │ │ │ │ + } │ │ │ │ + return array; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: extract.linestring │ │ │ │ + * Return an array of coordinate arrays from a linestring. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * linestring - {<OpenLayers.Geometry.LineString>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} An array of coordinate arrays representing │ │ │ │ + * the linestring. │ │ │ │ + */ │ │ │ │ + 'linestring': function(linestring) { │ │ │ │ + var array = []; │ │ │ │ + for (var i = 0, len = linestring.components.length; i < len; ++i) { │ │ │ │ + array.push(this.extract.point.apply(this, [linestring.components[i]])); │ │ │ │ + } │ │ │ │ + return array; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: extract.multilinestring │ │ │ │ + * Return an array of linestring arrays from a linestring. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * multilinestring - {<OpenLayers.Geometry.MultiLineString>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} An array of linestring arrays representing │ │ │ │ + * the multilinestring. │ │ │ │ + */ │ │ │ │ + 'multilinestring': function(multilinestring) { │ │ │ │ + var array = []; │ │ │ │ + for (var i = 0, len = multilinestring.components.length; i < len; ++i) { │ │ │ │ + array.push(this.extract.linestring.apply(this, [multilinestring.components[i]])); │ │ │ │ + } │ │ │ │ + return array; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: extract.polygon │ │ │ │ + * Return an array of linear ring arrays from a polygon. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * polygon - {<OpenLayers.Geometry.Polygon>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} An array of linear ring arrays representing the polygon. │ │ │ │ + */ │ │ │ │ + 'polygon': function(polygon) { │ │ │ │ + var array = []; │ │ │ │ + for (var i = 0, len = polygon.components.length; i < len; ++i) { │ │ │ │ + array.push(this.extract.linestring.apply(this, [polygon.components[i]])); │ │ │ │ + } │ │ │ │ + return array; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: extract.multipolygon │ │ │ │ + * Return an array of polygon arrays from a multipolygon. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * multipolygon - {<OpenLayers.Geometry.MultiPolygon>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} An array of polygon arrays representing │ │ │ │ + * the multipolygon │ │ │ │ + */ │ │ │ │ + 'multipolygon': function(multipolygon) { │ │ │ │ + var array = []; │ │ │ │ + for (var i = 0, len = multipolygon.components.length; i < len; ++i) { │ │ │ │ + array.push(this.extract.polygon.apply(this, [multipolygon.components[i]])); │ │ │ │ + } │ │ │ │ + return array; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: extract.collection │ │ │ │ + * Return an array of geometries from a geometry collection. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * collection - {<OpenLayers.Geometry.Collection>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array} An array of geometry objects representing the geometry │ │ │ │ + * collection. │ │ │ │ + */ │ │ │ │ + 'collection': function(collection) { │ │ │ │ + var len = collection.components.length; │ │ │ │ + var array = new Array(len); │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ + array[i] = this.extract.geometry.apply( │ │ │ │ + this, [collection.components[i]] │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return array; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: adjustXY │ │ │ │ - * Converts event coordinates that are relative to the document body to │ │ │ │ - * ones that are relative to the map viewport. The latter is the default in │ │ │ │ - * OpenLayers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} │ │ │ │ - */ │ │ │ │ - adjustXY: function(evt) { │ │ │ │ - var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv); │ │ │ │ - evt.xy.x -= pos[0]; │ │ │ │ - evt.xy.y -= pos[1]; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: addDocumentEvents │ │ │ │ - * Start observing document events when documentDrag is true and the mouse │ │ │ │ - * cursor leaves the map viewport while dragging. │ │ │ │ - */ │ │ │ │ - addDocumentEvents: function() { │ │ │ │ - OpenLayers.Element.addClass(document.body, "olDragDown"); │ │ │ │ - this.documentEvents = true; │ │ │ │ - OpenLayers.Event.observe(document, "mousemove", this._docMove); │ │ │ │ - OpenLayers.Event.observe(document, "mouseup", this._docUp); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: removeDocumentEvents │ │ │ │ - * Stops observing document events when documentDrag is true and the mouse │ │ │ │ - * cursor re-enters the map viewport while dragging. │ │ │ │ - */ │ │ │ │ - removeDocumentEvents: function() { │ │ │ │ - OpenLayers.Element.removeClass(document.body, "olDragDown"); │ │ │ │ - this.documentEvents = false; │ │ │ │ - OpenLayers.Event.stopObserving(document, "mousemove", this._docMove); │ │ │ │ - OpenLayers.Event.stopObserving(document, "mouseup", this._docUp); │ │ │ │ - }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.GeoJSON" │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Drag" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Handler/Box.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/Handler.js │ │ │ │ - * @requires OpenLayers/Handler/Drag.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Map.js │ │ │ │ + * @requires OpenLayers/Projection.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Handler.Box │ │ │ │ - * Handler for dragging a rectangle across the map. Box is displayed │ │ │ │ - * on mouse down, moves on mouse move, and is finished on mouse up. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Handler> │ │ │ │ + * Class: OpenLayers.Layer │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ +OpenLayers.Layer = OpenLayers.Class({ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: id │ │ │ │ + * {String} │ │ │ │ + */ │ │ │ │ + id: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: dragHandler │ │ │ │ - * {<OpenLayers.Handler.Drag>} │ │ │ │ + * APIProperty: name │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - dragHandler: null, │ │ │ │ + name: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: div │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + div: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: boxDivClassName │ │ │ │ - * {String} The CSS class to use for drawing the box. Default is │ │ │ │ - * olHandlerBoxZoomBox │ │ │ │ + * APIProperty: opacity │ │ │ │ + * {Float} The layer's opacity. Float number between 0.0 and 1.0. Default │ │ │ │ + * is 1. │ │ │ │ */ │ │ │ │ - boxDivClassName: 'olHandlerBoxZoomBox', │ │ │ │ + opacity: 1, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: boxOffsets │ │ │ │ - * {Object} Caches box offsets from css. This is used by the getBoxOffsets │ │ │ │ - * method. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - boxOffsets: null, │ │ │ │ + alwaysInRange: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Handler.Box │ │ │ │ + * Constant: RESOLUTION_PROPERTIES │ │ │ │ + * {Array} The properties that are used for calculating resolutions │ │ │ │ + * information. │ │ │ │ + */ │ │ │ │ + RESOLUTION_PROPERTIES: [ │ │ │ │ + 'scales', 'resolutions', │ │ │ │ + 'maxScale', 'minScale', │ │ │ │ + 'maxResolution', 'minResolution', │ │ │ │ + 'numZoomLevels', 'maxZoomLevel' │ │ │ │ + ], │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} │ │ │ │ - * callbacks - {Object} An object with a properties whose values are │ │ │ │ - * functions. Various callbacks described below. │ │ │ │ - * options - {Object} │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * layer.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ * │ │ │ │ - * Named callbacks: │ │ │ │ - * start - Called when the box drag operation starts. │ │ │ │ - * done - Called when the box drag operation is finished. │ │ │ │ - * The callback should expect to receive a single argument, the box │ │ │ │ - * bounds or a pixel. If the box dragging didn't span more than a 5 │ │ │ │ - * pixel distance, a pixel will be returned instead of a bounds object. │ │ │ │ + * Listeners will be called with a reference to an event object. The │ │ │ │ + * properties of this event depends on exactly what happened. │ │ │ │ + * │ │ │ │ + * All event objects have at least the following properties: │ │ │ │ + * object - {Object} A reference to layer.events.object. │ │ │ │ + * element - {DOMElement} A reference to layer.events.element. │ │ │ │ + * │ │ │ │ + * Supported map event types: │ │ │ │ + * loadstart - Triggered when layer loading starts. When using a Vector │ │ │ │ + * layer with a Fixed or BBOX strategy, the event object includes │ │ │ │ + * a *filter* property holding the OpenLayers.Filter used when │ │ │ │ + * calling read on the protocol. │ │ │ │ + * loadend - Triggered when layer loading ends. When using a Vector layer │ │ │ │ + * with a Fixed or BBOX strategy, the event object includes a │ │ │ │ + * *response* property holding an OpenLayers.Protocol.Response object. │ │ │ │ + * visibilitychanged - Triggered when the layer's visibility property is │ │ │ │ + * changed, e.g. by turning the layer on or off in the layer switcher. │ │ │ │ + * Note that the actual visibility of the layer can also change if it │ │ │ │ + * gets out of range (see <calculateInRange>). If you also want to catch │ │ │ │ + * these cases, register for the map's 'changelayer' event instead. │ │ │ │ + * move - Triggered when layer moves (triggered with every mousemove │ │ │ │ + * during a drag). │ │ │ │ + * moveend - Triggered when layer is done moving, object passed as │ │ │ │ + * argument has a zoomChanged boolean property which tells that the │ │ │ │ + * zoom has changed. │ │ │ │ + * added - Triggered after the layer is added to a map. Listeners will │ │ │ │ + * receive an object with a *map* property referencing the map and a │ │ │ │ + * *layer* property referencing the layer. │ │ │ │ + * removed - Triggered after the layer is removed from the map. Listeners │ │ │ │ + * will receive an object with a *map* property referencing the map and │ │ │ │ + * a *layer* property referencing the layer. │ │ │ │ */ │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - this.dragHandler = new OpenLayers.Handler.Drag( │ │ │ │ - this, { │ │ │ │ - down: this.startBox, │ │ │ │ - move: this.moveBox, │ │ │ │ - out: this.removeBox, │ │ │ │ - up: this.endBox │ │ │ │ - }, { │ │ │ │ - keyMask: this.keyMask │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ + events: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: destroy │ │ │ │ + * APIProperty: map │ │ │ │ + * {<OpenLayers.Map>} This variable is set when the layer is added to │ │ │ │ + * the map, via the accessor function setMap(). │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ - if (this.dragHandler) { │ │ │ │ - this.dragHandler.destroy(); │ │ │ │ - this.dragHandler = null; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + map: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setMap │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} Whether or not the layer is a base layer. This should be set │ │ │ │ + * individually by all subclasses. Default is false │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Handler.prototype.setMap.apply(this, arguments); │ │ │ │ - if (this.dragHandler) { │ │ │ │ - this.dragHandler.setMap(map); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + isBaseLayer: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: startBox │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * xy - {<OpenLayers.Pixel>} │ │ │ │ + * Property: alpha │ │ │ │ + * {Boolean} The layer's images have an alpha channel. Default is false. │ │ │ │ */ │ │ │ │ - startBox: function(xy) { │ │ │ │ - this.callback("start", []); │ │ │ │ - this.zoomBox = OpenLayers.Util.createDiv('zoomBox', { │ │ │ │ - x: -9999, │ │ │ │ - y: -9999 │ │ │ │ - }); │ │ │ │ - this.zoomBox.className = this.boxDivClassName; │ │ │ │ - this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1; │ │ │ │ - │ │ │ │ - this.map.viewPortDiv.appendChild(this.zoomBox); │ │ │ │ + alpha: false, │ │ │ │ │ │ │ │ - OpenLayers.Element.addClass( │ │ │ │ - this.map.viewPortDiv, "olDrawBox" │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: displayInLayerSwitcher │ │ │ │ + * {Boolean} Display the layer's name in the layer switcher. Default is │ │ │ │ + * true. │ │ │ │ + */ │ │ │ │ + displayInLayerSwitcher: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveBox │ │ │ │ + * APIProperty: visibility │ │ │ │ + * {Boolean} The layer should be displayed in the map. Default is true. │ │ │ │ */ │ │ │ │ - moveBox: function(xy) { │ │ │ │ - var startX = this.dragHandler.start.x; │ │ │ │ - var startY = this.dragHandler.start.y; │ │ │ │ - var deltaX = Math.abs(startX - xy.x); │ │ │ │ - var deltaY = Math.abs(startY - xy.y); │ │ │ │ - │ │ │ │ - var offset = this.getBoxOffsets(); │ │ │ │ - this.zoomBox.style.width = (deltaX + offset.width + 1) + "px"; │ │ │ │ - this.zoomBox.style.height = (deltaY + offset.height + 1) + "px"; │ │ │ │ - this.zoomBox.style.left = (xy.x < startX ? │ │ │ │ - startX - deltaX - offset.left : startX - offset.left) + "px"; │ │ │ │ - this.zoomBox.style.top = (xy.y < startY ? │ │ │ │ - startY - deltaY - offset.top : startY - offset.top) + "px"; │ │ │ │ - }, │ │ │ │ + visibility: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: endBox │ │ │ │ + * APIProperty: attribution │ │ │ │ + * {String} Attribution string, displayed when an │ │ │ │ + * <OpenLayers.Control.Attribution> has been added to the map. │ │ │ │ */ │ │ │ │ - endBox: function(end) { │ │ │ │ - var result; │ │ │ │ - if (Math.abs(this.dragHandler.start.x - end.x) > 5 || │ │ │ │ - Math.abs(this.dragHandler.start.y - end.y) > 5) { │ │ │ │ - var start = this.dragHandler.start; │ │ │ │ - var top = Math.min(start.y, end.y); │ │ │ │ - var bottom = Math.max(start.y, end.y); │ │ │ │ - var left = Math.min(start.x, end.x); │ │ │ │ - var right = Math.max(start.x, end.x); │ │ │ │ - result = new OpenLayers.Bounds(left, bottom, right, top); │ │ │ │ - } else { │ │ │ │ - result = this.dragHandler.start.clone(); // i.e. OL.Pixel │ │ │ │ - } │ │ │ │ - this.removeBox(); │ │ │ │ + attribution: null, │ │ │ │ │ │ │ │ - this.callback("done", [result]); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: inRange │ │ │ │ + * {Boolean} The current map resolution is within the layer's min/max │ │ │ │ + * range. This is set in <OpenLayers.Map.setCenter> whenever the zoom │ │ │ │ + * changes. │ │ │ │ + */ │ │ │ │ + inRange: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: removeBox │ │ │ │ - * Remove the zoombox from the screen and nullify our reference to it. │ │ │ │ + * Propery: imageSize │ │ │ │ + * {<OpenLayers.Size>} For layers with a gutter, the image is larger than │ │ │ │ + * the tile by twice the gutter in each dimension. │ │ │ │ */ │ │ │ │ - removeBox: function() { │ │ │ │ - this.map.viewPortDiv.removeChild(this.zoomBox); │ │ │ │ - this.zoomBox = null; │ │ │ │ - this.boxOffsets = null; │ │ │ │ - OpenLayers.Element.removeClass( │ │ │ │ - this.map.viewPortDiv, "olDrawBox" │ │ │ │ - ); │ │ │ │ + imageSize: null, │ │ │ │ │ │ │ │ - }, │ │ │ │ + // OPTIONS │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: options │ │ │ │ + * {Object} An optional object whose properties will be set on the layer. │ │ │ │ + * Any of the layer properties can be set as a property of the options │ │ │ │ + * object and sent to the constructor when the layer is created. │ │ │ │ + */ │ │ │ │ + options: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: activate │ │ │ │ + * APIProperty: eventListeners │ │ │ │ + * {Object} If set as an option at construction, the eventListeners │ │ │ │ + * object will be registered with <OpenLayers.Events.on>. Object │ │ │ │ + * structure must be a listeners object as shown in the example for │ │ │ │ + * the events.on method. │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.dragHandler.activate(); │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + eventListeners: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: deactivate │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - if (this.dragHandler.deactivate()) { │ │ │ │ - if (this.zoomBox) { │ │ │ │ - this.removeBox(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + gutter: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getBoxOffsets │ │ │ │ - * Determines border offsets for a box, according to the box model. │ │ │ │ + * APIProperty: projection │ │ │ │ + * {<OpenLayers.Projection>} or {<String>} Specifies the projection of the layer. │ │ │ │ + * Can be set in the layer options. If not specified in the layer options, │ │ │ │ + * it is set to the default projection specified in the map, │ │ │ │ + * when the layer is added to the map. │ │ │ │ + * Projection along with default maxExtent and resolutions │ │ │ │ + * are set automatically with commercial baselayers in EPSG:3857, │ │ │ │ + * such as Google, Bing and OpenStreetMap, and do not need to be specified. │ │ │ │ + * Otherwise, if specifying projection, also set maxExtent, │ │ │ │ + * maxResolution or resolutions as appropriate. │ │ │ │ + * When using vector layers with strategies, layer projection should be set │ │ │ │ + * to the projection of the source data if that is different from the map default. │ │ │ │ + * │ │ │ │ + * Can be either a string or an <OpenLayers.Projection> object; │ │ │ │ + * if a string is passed, will be converted to an object when │ │ │ │ + * the layer is added to the map. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Object} an object with the following offsets: │ │ │ │ - * - left │ │ │ │ - * - right │ │ │ │ - * - top │ │ │ │ - * - bottom │ │ │ │ - * - width │ │ │ │ - * - height │ │ │ │ */ │ │ │ │ - getBoxOffsets: function() { │ │ │ │ - if (!this.boxOffsets) { │ │ │ │ - // Determine the box model. If the testDiv's clientWidth is 3, then │ │ │ │ - // the borders are outside and we are dealing with the w3c box │ │ │ │ - // model. Otherwise, the browser uses the traditional box model and │ │ │ │ - // the borders are inside the box bounds, leaving us with a │ │ │ │ - // clientWidth of 1. │ │ │ │ - var testDiv = document.createElement("div"); │ │ │ │ - //testDiv.style.visibility = "hidden"; │ │ │ │ - testDiv.style.position = "absolute"; │ │ │ │ - testDiv.style.border = "1px solid black"; │ │ │ │ - testDiv.style.width = "3px"; │ │ │ │ - document.body.appendChild(testDiv); │ │ │ │ - var w3cBoxModel = testDiv.clientWidth == 3; │ │ │ │ - document.body.removeChild(testDiv); │ │ │ │ - │ │ │ │ - var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, │ │ │ │ - "border-left-width")); │ │ │ │ - var right = parseInt(OpenLayers.Element.getStyle( │ │ │ │ - this.zoomBox, "border-right-width")); │ │ │ │ - var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, │ │ │ │ - "border-top-width")); │ │ │ │ - var bottom = parseInt(OpenLayers.Element.getStyle( │ │ │ │ - this.zoomBox, "border-bottom-width")); │ │ │ │ - this.boxOffsets = { │ │ │ │ - left: left, │ │ │ │ - right: right, │ │ │ │ - top: top, │ │ │ │ - bottom: bottom, │ │ │ │ - width: w3cBoxModel === false ? left + right : 0, │ │ │ │ - height: w3cBoxModel === false ? top + bottom : 0 │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - return this.boxOffsets; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Box" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/ZoomBox.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Handler/Box.js │ │ │ │ - */ │ │ │ │ + projection: null, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.ZoomBox │ │ │ │ - * The ZoomBox control enables zooming directly to a given extent, by drawing │ │ │ │ - * a box on the map. The box is drawn by holding down shift, whilst dragging │ │ │ │ - * the mouse. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ /** │ │ │ │ - * Property: type │ │ │ │ - * {OpenLayers.Control.TYPE} │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ + units: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: out │ │ │ │ - * {Boolean} Should the control be used for zooming out? │ │ │ │ + * APIProperty: scales │ │ │ │ + * {Array} An array of map scales in descending order. The values in the │ │ │ │ + * array correspond to the map scale denominator. Note that these │ │ │ │ + * values only make sense if the display (monitor) resolution of the │ │ │ │ + * client is correctly guessed by whomever is configuring the │ │ │ │ + * application. In addition, the units property must also be set. │ │ │ │ + * Use <resolutions> instead wherever possible. │ │ │ │ */ │ │ │ │ - out: false, │ │ │ │ + scales: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: keyMask │ │ │ │ - * {Integer} Zoom only occurs if the keyMask matches the combination of │ │ │ │ - * keys down. Use bitwise operators and one or more of the │ │ │ │ - * <OpenLayers.Handler> constants to construct a keyMask. Leave null if │ │ │ │ - * not used mask. Default is null. │ │ │ │ + * 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.). │ │ │ │ */ │ │ │ │ - keyMask: null, │ │ │ │ + resolutions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: alwaysZoom │ │ │ │ - * {Boolean} Always zoom in/out when box drawn, even if the zoom level does │ │ │ │ - * not change. │ │ │ │ + * APIProperty: maxExtent │ │ │ │ + * {<OpenLayers.Bounds>|Array} If provided as an array, the array │ │ │ │ + * should consist of four values (left, bottom, right, top). │ │ │ │ + * The maximum extent for the layer. Defaults to null. │ │ │ │ + * │ │ │ │ + * The center of these bounds will not stray outside │ │ │ │ + * of the viewport extent during panning. In addition, if │ │ │ │ + * <displayOutsideMaxExtent> is set to false, data will not be │ │ │ │ + * requested that falls completely outside of these bounds. │ │ │ │ */ │ │ │ │ - alwaysZoom: false, │ │ │ │ + maxExtent: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: zoomOnClick │ │ │ │ - * {Boolean} Should we zoom when no box was dragged, i.e. the user only │ │ │ │ - * clicked? Default is true. │ │ │ │ + * APIProperty: minExtent │ │ │ │ + * {<OpenLayers.Bounds>|Array} If provided as an array, the array │ │ │ │ + * should consist of four values (left, bottom, right, top). │ │ │ │ + * The minimum extent for the layer. Defaults to null. │ │ │ │ */ │ │ │ │ - zoomOnClick: true, │ │ │ │ + minExtent: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: draw │ │ │ │ + * APIProperty: maxResolution │ │ │ │ + * {Float} Default max is 360 deg / 256 px, which corresponds to │ │ │ │ + * zoom level 0 on gmaps. Specify a different value in the layer │ │ │ │ + * options if you are not using the default <OpenLayers.Map.tileSize> │ │ │ │ + * and displaying the whole world. │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - this.handler = new OpenLayers.Handler.Box(this, { │ │ │ │ - done: this.zoomBox │ │ │ │ - }, { │ │ │ │ - keyMask: this.keyMask │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ + maxResolution: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: zoomBox │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>} │ │ │ │ + * APIProperty: minResolution │ │ │ │ + * {Float} │ │ │ │ */ │ │ │ │ - zoomBox: function(position) { │ │ │ │ - if (position instanceof OpenLayers.Bounds) { │ │ │ │ - var bounds, │ │ │ │ - targetCenterPx = position.getCenterPixel(); │ │ │ │ - if (!this.out) { │ │ │ │ - var minXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.left, │ │ │ │ - y: position.bottom │ │ │ │ - }); │ │ │ │ - var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.right, │ │ │ │ - y: position.top │ │ │ │ - }); │ │ │ │ - bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, │ │ │ │ - maxXY.lon, maxXY.lat); │ │ │ │ - } else { │ │ │ │ - var pixWidth = position.right - position.left; │ │ │ │ - var pixHeight = position.bottom - position.top; │ │ │ │ - var zoomFactor = Math.min((this.map.size.h / pixHeight), │ │ │ │ - (this.map.size.w / pixWidth)); │ │ │ │ - var extent = this.map.getExtent(); │ │ │ │ - var center = this.map.getLonLatFromPixel(targetCenterPx); │ │ │ │ - var xmin = center.lon - (extent.getWidth() / 2) * zoomFactor; │ │ │ │ - var xmax = center.lon + (extent.getWidth() / 2) * zoomFactor; │ │ │ │ - var ymin = center.lat - (extent.getHeight() / 2) * zoomFactor; │ │ │ │ - var ymax = center.lat + (extent.getHeight() / 2) * zoomFactor; │ │ │ │ - bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax); │ │ │ │ - } │ │ │ │ - // always zoom in/out │ │ │ │ - var lastZoom = this.map.getZoom(), │ │ │ │ - size = this.map.getSize(), │ │ │ │ - centerPx = { │ │ │ │ - x: size.w / 2, │ │ │ │ - y: size.h / 2 │ │ │ │ - }, │ │ │ │ - zoom = this.map.getZoomForExtent(bounds), │ │ │ │ - oldRes = this.map.getResolution(), │ │ │ │ - newRes = this.map.getResolutionForZoom(zoom); │ │ │ │ - if (oldRes == newRes) { │ │ │ │ - this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx)); │ │ │ │ - } else { │ │ │ │ - var zoomOriginPx = { │ │ │ │ - x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / │ │ │ │ - (oldRes - newRes), │ │ │ │ - y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / │ │ │ │ - (oldRes - newRes) │ │ │ │ - }; │ │ │ │ - this.map.zoomTo(zoom, zoomOriginPx); │ │ │ │ - } │ │ │ │ - if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) { │ │ │ │ - this.map.zoomTo(lastZoom + (this.out ? -1 : 1)); │ │ │ │ - } │ │ │ │ - } else if (this.zoomOnClick) { // it's a pixel │ │ │ │ - if (!this.out) { │ │ │ │ - this.map.zoomTo(this.map.getZoom() + 1, position); │ │ │ │ - } else { │ │ │ │ - this.map.zoomTo(this.map.getZoom() - 1, position); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ZoomBox" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/DragPan.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Handler/Drag.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.DragPan │ │ │ │ - * The DragPan control pans the map with a drag of the mouse. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + minResolution: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: type │ │ │ │ - * {OpenLayers.Control.TYPES} │ │ │ │ + /** │ │ │ │ + * APIProperty: numZoomLevels │ │ │ │ + * {Integer} │ │ │ │ */ │ │ │ │ - type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ + numZoomLevels: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: panned │ │ │ │ - * {Boolean} The map moved. │ │ │ │ + * APIProperty: minScale │ │ │ │ + * {Float} │ │ │ │ */ │ │ │ │ - panned: false, │ │ │ │ + minScale: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: interval │ │ │ │ - * {Integer} The number of milliseconds that should ellapse before │ │ │ │ - * panning the map again. Defaults to 0 milliseconds, which means that │ │ │ │ - * no separate cycle is used for panning. In most cases you won't want │ │ │ │ - * to change this value. For slow machines/devices larger values can be │ │ │ │ - * tried out. │ │ │ │ + * APIProperty: maxScale │ │ │ │ + * {Float} │ │ │ │ */ │ │ │ │ - interval: 0, │ │ │ │ + maxScale: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: documentDrag │ │ │ │ - * {Boolean} If set to true, mouse dragging will continue even if the │ │ │ │ - * mouse cursor leaves the map viewport. Default is false. │ │ │ │ + * APIProperty: displayOutsideMaxExtent │ │ │ │ + * {Boolean} Request map tiles that are completely outside of the max │ │ │ │ + * extent for this layer. Defaults to false. │ │ │ │ */ │ │ │ │ - documentDrag: false, │ │ │ │ + displayOutsideMaxExtent: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: kinetic │ │ │ │ - * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - kinetic: null, │ │ │ │ + wrapDateLine: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: enableKinetic │ │ │ │ - * {Boolean} Set this option to enable "kinetic dragging". Can be │ │ │ │ - * set to true or to an object. If set to an object this │ │ │ │ - * object will be passed to the {<OpenLayers.Kinetic>} │ │ │ │ - * constructor. Defaults to true. │ │ │ │ - * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is │ │ │ │ - * included in your build config. │ │ │ │ + * Property: metadata │ │ │ │ + * {Object} This object can be used to store additional information on a │ │ │ │ + * layer object. │ │ │ │ */ │ │ │ │ - enableKinetic: true, │ │ │ │ + metadata: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: kineticInterval │ │ │ │ - * {Integer} Interval in milliseconds between 2 steps in the "kinetic │ │ │ │ - * scrolling". Applies only if enableKinetic is set. Defaults │ │ │ │ - * to 10 milliseconds. │ │ │ │ + * Constructor: OpenLayers.Layer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} The layer name │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ */ │ │ │ │ - kineticInterval: 10, │ │ │ │ + initialize: function(name, options) { │ │ │ │ │ │ │ │ + this.metadata = {}; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: draw │ │ │ │ - * Creates a Drag handler, using <panMap> and │ │ │ │ - * <panMapDone> as callbacks. │ │ │ │ - */ │ │ │ │ - draw: function() { │ │ │ │ - if (this.enableKinetic && OpenLayers.Kinetic) { │ │ │ │ - var config = { │ │ │ │ - interval: this.kineticInterval │ │ │ │ - }; │ │ │ │ - if (typeof this.enableKinetic === "object") { │ │ │ │ - config = OpenLayers.Util.extend(config, this.enableKinetic); │ │ │ │ - } │ │ │ │ - this.kinetic = new OpenLayers.Kinetic(config); │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + // make sure we respect alwaysInRange if set on the prototype │ │ │ │ + if (this.alwaysInRange != null) { │ │ │ │ + options.alwaysInRange = this.alwaysInRange; │ │ │ │ } │ │ │ │ - this.handler = new OpenLayers.Handler.Drag(this, { │ │ │ │ - "move": this.panMap, │ │ │ │ - "done": this.panMapDone, │ │ │ │ - "down": this.panMapStart │ │ │ │ - }, { │ │ │ │ - interval: this.interval, │ │ │ │ - documentDrag: this.documentDrag │ │ │ │ - }); │ │ │ │ - }, │ │ │ │ + this.addOptions(options); │ │ │ │ + │ │ │ │ + this.name = name; │ │ │ │ + │ │ │ │ + if (this.id == null) { │ │ │ │ + │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ + │ │ │ │ + this.div = OpenLayers.Util.createDiv(this.id); │ │ │ │ + this.div.style.width = "100%"; │ │ │ │ + this.div.style.height = "100%"; │ │ │ │ + this.div.dir = "ltr"; │ │ │ │ + │ │ │ │ + this.events = new OpenLayers.Events(this, this.div); │ │ │ │ + if (this.eventListeners instanceof Object) { │ │ │ │ + this.events.on(this.eventListeners); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: panMapStart │ │ │ │ - */ │ │ │ │ - panMapStart: function() { │ │ │ │ - if (this.kinetic) { │ │ │ │ - this.kinetic.begin(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: panMap │ │ │ │ + * 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: │ │ │ │ - * xy - {<OpenLayers.Pixel>} Pixel of the mouse position │ │ │ │ + * setNewBaseLayer - {Boolean} Set a new base layer when this layer has │ │ │ │ + * been destroyed. Default is true. │ │ │ │ */ │ │ │ │ - panMap: function(xy) { │ │ │ │ - if (this.kinetic) { │ │ │ │ - this.kinetic.update(xy); │ │ │ │ + destroy: function(setNewBaseLayer) { │ │ │ │ + if (setNewBaseLayer == null) { │ │ │ │ + setNewBaseLayer = true; │ │ │ │ } │ │ │ │ - this.panned = true; │ │ │ │ - this.map.pan( │ │ │ │ - this.handler.last.x - xy.x, │ │ │ │ - this.handler.last.y - xy.y, { │ │ │ │ - dragging: true, │ │ │ │ - animate: false │ │ │ │ + if (this.map != null) { │ │ │ │ + this.map.removeLayer(this, setNewBaseLayer); │ │ │ │ + } │ │ │ │ + this.projection = null; │ │ │ │ + this.map = null; │ │ │ │ + this.name = null; │ │ │ │ + this.div = null; │ │ │ │ + this.options = null; │ │ │ │ + │ │ │ │ + if (this.events) { │ │ │ │ + if (this.eventListeners) { │ │ │ │ + this.events.un(this.eventListeners); │ │ │ │ } │ │ │ │ - ); │ │ │ │ + this.events.destroy(); │ │ │ │ + } │ │ │ │ + this.eventListeners = null; │ │ │ │ + this.events = null; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: panMapDone │ │ │ │ - * Finish the panning operation. Only call setCenter (through <panMap>) │ │ │ │ - * if the map has actually been moved. │ │ │ │ + * Method: clone │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * xy - {<OpenLayers.Pixel>} Pixel of the mouse position │ │ │ │ + * obj - {<OpenLayers.Layer>} The layer to be cloned │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer>} An exact clone of this <OpenLayers.Layer> │ │ │ │ */ │ │ │ │ - panMapDone: function(xy) { │ │ │ │ - if (this.panned) { │ │ │ │ - var res = null; │ │ │ │ - if (this.kinetic) { │ │ │ │ - res = this.kinetic.end(xy); │ │ │ │ - } │ │ │ │ - this.map.pan( │ │ │ │ - this.handler.last.x - xy.x, │ │ │ │ - this.handler.last.y - xy.y, { │ │ │ │ - dragging: !!res, │ │ │ │ - animate: false │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - if (res) { │ │ │ │ - var self = this; │ │ │ │ - this.kinetic.move(res, function(x, y, end) { │ │ │ │ - self.map.pan(x, y, { │ │ │ │ - dragging: !end, │ │ │ │ - animate: false │ │ │ │ - }); │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - this.panned = false; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Control.DragPan" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Handler/MouseWheel.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Handler.js │ │ │ │ - */ │ │ │ │ + clone: function(obj) { │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Handler.MouseWheel │ │ │ │ - * Handler for wheel up/down events. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Handler> │ │ │ │ - */ │ │ │ │ -OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - /** │ │ │ │ - * Property: wheelListener │ │ │ │ - * {function} │ │ │ │ - */ │ │ │ │ - wheelListener: null, │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer(this.name, this.getOptions()); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: interval │ │ │ │ - * {Integer} In order to increase server performance, an interval (in │ │ │ │ - * milliseconds) can be set to reduce the number of up/down events │ │ │ │ - * called. If set, a new up/down event will not be set until the │ │ │ │ - * interval has passed. │ │ │ │ - * Defaults to 0, meaning no interval. │ │ │ │ - */ │ │ │ │ - interval: 0, │ │ │ │ + // catch any randomly tagged-on properties │ │ │ │ + OpenLayers.Util.applyDefaults(obj, this); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: maxDelta │ │ │ │ - * {Integer} Maximum delta to collect before breaking from the current │ │ │ │ - * interval. In cumulative mode, this also limits the maximum delta │ │ │ │ - * returned from the handler. Default is Number.POSITIVE_INFINITY. │ │ │ │ - */ │ │ │ │ - maxDelta: Number.POSITIVE_INFINITY, │ │ │ │ + // a cloned layer should never have its map property set │ │ │ │ + // because it has not been added to a map yet. │ │ │ │ + obj.map = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: delta │ │ │ │ - * {Integer} When interval is set, delta collects the mousewheel z-deltas │ │ │ │ - * of the events that occur within the interval. │ │ │ │ - * See also the cumulative option │ │ │ │ - */ │ │ │ │ - delta: 0, │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: cumulative │ │ │ │ - * {Boolean} When interval is set: true to collect all the mousewheel │ │ │ │ - * z-deltas, false to only record the delta direction (positive or │ │ │ │ - * negative) │ │ │ │ + * Method: getOptions │ │ │ │ + * Extracts an object from the layer with the properties that were set as │ │ │ │ + * options, but updates them with the values currently set on the │ │ │ │ + * instance. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} the <options> of the layer, representing the current state. │ │ │ │ */ │ │ │ │ - cumulative: true, │ │ │ │ + getOptions: function() { │ │ │ │ + var options = {}; │ │ │ │ + for (var o in this.options) { │ │ │ │ + options[o] = this[o]; │ │ │ │ + } │ │ │ │ + return options; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Handler.MouseWheel │ │ │ │ + /** │ │ │ │ + * APIMethod: setName │ │ │ │ + * Sets the new layer name for this layer. Can trigger a changelayer event │ │ │ │ + * on the map. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} │ │ │ │ - * callbacks - {Object} An object containing a single function to be │ │ │ │ - * called when the drag operation is finished. │ │ │ │ - * The callback should expect to recieve a single │ │ │ │ - * argument, the point geometry. │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - this.wheelListener = OpenLayers.Function.bindAsEventListener( │ │ │ │ - this.onWheelEvent, this │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ + * newName - {String} The new name. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ - this.wheelListener = null; │ │ │ │ + setName: function(newName) { │ │ │ │ + if (newName != this.name) { │ │ │ │ + this.name = newName; │ │ │ │ + if (this.map != null) { │ │ │ │ + this.map.events.triggerEvent("changelayer", { │ │ │ │ + layer: this, │ │ │ │ + property: "name" │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/ │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: onWheelEvent │ │ │ │ - * Catch the wheel event and handle it xbrowserly │ │ │ │ + * APIMethod: addOptions │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * e - {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. │ │ │ │ */ │ │ │ │ - onWheelEvent: function(e) { │ │ │ │ + addOptions: function(newOptions, reinitialize) { │ │ │ │ │ │ │ │ - // make sure we have a map and check keyboard modifiers │ │ │ │ - if (!this.map || !this.checkModifiers(e)) { │ │ │ │ - return; │ │ │ │ + if (this.options == null) { │ │ │ │ + this.options = {}; │ │ │ │ } │ │ │ │ │ │ │ │ - // Ride up the element's DOM hierarchy to determine if it or any of │ │ │ │ - // its ancestors was: │ │ │ │ - // * specifically marked as scrollable (CSS overflow property) │ │ │ │ - // * one of our layer divs or a div marked as scrollable │ │ │ │ - // ('olScrollable' CSS class) │ │ │ │ - // * the map div │ │ │ │ - // │ │ │ │ - var overScrollableDiv = false; │ │ │ │ - var allowScroll = false; │ │ │ │ - var overMapDiv = false; │ │ │ │ - │ │ │ │ - var elem = OpenLayers.Event.element(e); │ │ │ │ - while ((elem != null) && !overMapDiv && !overScrollableDiv) { │ │ │ │ - │ │ │ │ - if (!overScrollableDiv) { │ │ │ │ - try { │ │ │ │ - var overflow; │ │ │ │ - if (elem.currentStyle) { │ │ │ │ - overflow = elem.currentStyle["overflow"]; │ │ │ │ - } else { │ │ │ │ - var style = │ │ │ │ - document.defaultView.getComputedStyle(elem, null); │ │ │ │ - overflow = style.getPropertyValue("overflow"); │ │ │ │ - } │ │ │ │ - overScrollableDiv = (overflow && │ │ │ │ - (overflow == "auto") || (overflow == "scroll")); │ │ │ │ - } catch (err) { │ │ │ │ - //sometimes when scrolling in a popup, this causes │ │ │ │ - // obscure browser error │ │ │ │ - } │ │ │ │ + if (newOptions) { │ │ │ │ + // make sure this.projection references a projection object │ │ │ │ + if (typeof newOptions.projection == "string") { │ │ │ │ + newOptions.projection = new OpenLayers.Projection(newOptions.projection); │ │ │ │ } │ │ │ │ - │ │ │ │ - if (!allowScroll) { │ │ │ │ - allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable'); │ │ │ │ - if (!allowScroll) { │ │ │ │ - for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ - // Are we in the layer div? Note that we have two cases │ │ │ │ - // here: one is to catch EventPane layers, which have a │ │ │ │ - // pane above the layer (layer.pane) │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - if (elem == layer.div || elem == layer.pane) { │ │ │ │ - allowScroll = true; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - overMapDiv = (elem == this.map.div); │ │ │ │ - │ │ │ │ - elem = elem.parentNode; │ │ │ │ } │ │ │ │ │ │ │ │ - // Logic below is the following: │ │ │ │ - // │ │ │ │ - // If we are over a scrollable div or not over the map div: │ │ │ │ - // * do nothing (let the browser handle scrolling) │ │ │ │ - // │ │ │ │ - // otherwise │ │ │ │ - // │ │ │ │ - // If we are over the layer div or a 'olScrollable' div: │ │ │ │ - // * zoom/in out │ │ │ │ - // then │ │ │ │ - // * kill event (so as not to also scroll the page after zooming) │ │ │ │ - // │ │ │ │ - // otherwise │ │ │ │ - // │ │ │ │ - // Kill the event (dont scroll the page if we wheel over the │ │ │ │ - // layerswitcher or the pan/zoom control) │ │ │ │ - // │ │ │ │ - if (!overScrollableDiv && overMapDiv) { │ │ │ │ - if (allowScroll) { │ │ │ │ - var delta = 0; │ │ │ │ + // update our copy for clone │ │ │ │ + OpenLayers.Util.extend(this.options, newOptions); │ │ │ │ │ │ │ │ - if (e.wheelDelta) { │ │ │ │ - delta = e.wheelDelta; │ │ │ │ - if (delta % 160 === 0) { │ │ │ │ - // opera have steps of 160 instead of 120 │ │ │ │ - delta = delta * 0.75; │ │ │ │ - } │ │ │ │ - delta = delta / 120; │ │ │ │ - } else if (e.detail) { │ │ │ │ - // detail in Firefox on OS X is 1/3 of Windows │ │ │ │ - // so force delta 1 / -1 │ │ │ │ - delta = -(e.detail / Math.abs(e.detail)); │ │ │ │ - } │ │ │ │ - this.delta += delta; │ │ │ │ + // add new options to this │ │ │ │ + OpenLayers.Util.extend(this, newOptions); │ │ │ │ │ │ │ │ - window.clearTimeout(this._timeoutId); │ │ │ │ - if (this.interval && Math.abs(this.delta) < this.maxDelta) { │ │ │ │ - // store e because window.event might change during delay │ │ │ │ - var evt = OpenLayers.Util.extend({}, e); │ │ │ │ - this._timeoutId = window.setTimeout( │ │ │ │ - OpenLayers.Function.bind(function() { │ │ │ │ - this.wheelZoom(evt); │ │ │ │ - }, this), │ │ │ │ - this.interval │ │ │ │ - ); │ │ │ │ - } else { │ │ │ │ - this.wheelZoom(e); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Event.stop(e); │ │ │ │ + // 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(); │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: wheelZoom │ │ │ │ - * Given the wheel event, we carry out the appropriate zooming in or out, │ │ │ │ - * based on the 'wheelDelta' or 'detail' property of the event. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * e - {Event} │ │ │ │ - */ │ │ │ │ - wheelZoom: function(e) { │ │ │ │ - var delta = this.delta; │ │ │ │ - this.delta = 0; │ │ │ │ + // 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) { │ │ │ │ │ │ │ │ - if (delta) { │ │ │ │ - e.xy = this.map.events.getMousePosition(e); │ │ │ │ - if (delta < 0) { │ │ │ │ - this.callback("down", │ │ │ │ - [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]); │ │ │ │ - } else { │ │ │ │ - this.callback("up", │ │ │ │ - [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]); │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: activate │ │ │ │ + * APIMethod: onMapResize │ │ │ │ + * This function can be implemented by subclasses │ │ │ │ */ │ │ │ │ - activate: function(evt) { │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - //register mousewheel events specifically on the window and document │ │ │ │ - var wheelListener = this.wheelListener; │ │ │ │ - OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener); │ │ │ │ - OpenLayers.Event.observe(window, "mousewheel", wheelListener); │ │ │ │ - OpenLayers.Event.observe(document, "mousewheel", wheelListener); │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ + onMapResize: function() { │ │ │ │ + //this function can be implemented by subclasses │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: deactivate │ │ │ │ + * APIMethod: redraw │ │ │ │ + * Redraws the layer. Returns true if the layer was redrawn, false if not. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The layer was redrawn. │ │ │ │ */ │ │ │ │ - deactivate: function(evt) { │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - // unregister mousewheel events specifically on the window and document │ │ │ │ - var wheelListener = this.wheelListener; │ │ │ │ - OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener); │ │ │ │ - OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener); │ │ │ │ - OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener); │ │ │ │ - return true; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.MouseWheel" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Handler/Click.js │ │ │ │ - ====================================================================== */ │ │ │ │ + redraw: function() { │ │ │ │ + var redrawn = false; │ │ │ │ + if (this.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. */ │ │ │ │ + // min/max Range may have changed │ │ │ │ + this.inRange = this.calculateInRange(); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Handler.js │ │ │ │ - */ │ │ │ │ + // map's center might not yet be set │ │ │ │ + var extent = this.getExtent(); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Handler.Click │ │ │ │ - * A handler for mouse clicks. The intention of this handler is to give │ │ │ │ - * controls more flexibility with handling clicks. Browsers trigger │ │ │ │ - * click events twice for a double-click. In addition, the mousedown, │ │ │ │ - * mousemove, mouseup sequence fires a click event. With this handler, │ │ │ │ - * controls can decide whether to ignore clicks associated with a double │ │ │ │ - * click. By setting a <pixelTolerance>, controls can also ignore clicks │ │ │ │ - * that include a drag. Create a new instance with the │ │ │ │ - * <OpenLayers.Handler.Click> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Handler> │ │ │ │ - */ │ │ │ │ -OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - /** │ │ │ │ - * APIProperty: delay │ │ │ │ - * {Number} Number of milliseconds between clicks before the event is │ │ │ │ - * considered a double-click. │ │ │ │ - */ │ │ │ │ - delay: 300, │ │ │ │ + if (extent && this.inRange && this.visibility) { │ │ │ │ + var zoomChanged = true; │ │ │ │ + this.moveTo(extent, zoomChanged, false); │ │ │ │ + this.events.triggerEvent("moveend", { │ │ │ │ + "zoomChanged": zoomChanged │ │ │ │ + }); │ │ │ │ + redrawn = true; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return redrawn; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: single │ │ │ │ - * {Boolean} Handle single clicks. Default is true. If false, clicks │ │ │ │ - * will not be reported. If true, single-clicks will be reported. │ │ │ │ + * Method: moveTo │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to │ │ │ │ + * do some init work in that case. │ │ │ │ + * dragging - {Boolean} │ │ │ │ */ │ │ │ │ - single: true, │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + var display = this.visibility; │ │ │ │ + if (!this.isBaseLayer) { │ │ │ │ + display = display && this.inRange; │ │ │ │ + } │ │ │ │ + this.display(display); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: double │ │ │ │ - * {Boolean} Handle double-clicks. Default is false. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 'double': false, │ │ │ │ + moveByPx: function(dx, dy) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: pixelTolerance │ │ │ │ - * {Number} Maximum number of pixels between mouseup and mousedown for an │ │ │ │ - * event to be considered a click. Default is 0. If set to an │ │ │ │ - * integer value, clicks with a drag greater than the value will be │ │ │ │ - * ignored. This property can only be set when the handler is │ │ │ │ - * constructed. │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the layer. This is done through an accessor │ │ │ │ + * so that subclasses can override this and take special action once │ │ │ │ + * they have their map variable set. │ │ │ │ + * │ │ │ │ + * Here we take care to bring over any of the necessary default │ │ │ │ + * properties from the map. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - pixelTolerance: 0, │ │ │ │ + setMap: function(map) { │ │ │ │ + if (this.map == null) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: dblclickTolerance │ │ │ │ - * {Number} Maximum distance in pixels between clicks for a sequence of │ │ │ │ - * events to be considered a double click. Default is 13. If the │ │ │ │ - * distance between two clicks is greater than this value, a double- │ │ │ │ - * click will not be fired. │ │ │ │ - */ │ │ │ │ - dblclickTolerance: 13, │ │ │ │ + this.map = map; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: stopSingle │ │ │ │ - * {Boolean} Stop other listeners from being notified of clicks. Default │ │ │ │ - * is false. If true, any listeners registered before this one for │ │ │ │ - * click or rightclick events will not be notified. │ │ │ │ - */ │ │ │ │ - stopSingle: false, │ │ │ │ + // 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; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: stopDouble │ │ │ │ - * {Boolean} Stop other listeners from being notified of double-clicks. │ │ │ │ - * Default is false. If true, any click listeners registered before │ │ │ │ - * this one will not be notified of *any* double-click events. │ │ │ │ - * │ │ │ │ - * The one caveat with stopDouble is that given a map with two click │ │ │ │ - * handlers, one with stopDouble true and the other with stopSingle │ │ │ │ - * true, the stopSingle handler should be activated last to get │ │ │ │ - * uniform cross-browser performance. Since IE triggers one click │ │ │ │ - * with a dblclick and FF triggers two, if a stopSingle handler is │ │ │ │ - * activated first, all it gets in IE is a single click when the │ │ │ │ - * second handler stops propagation on the dblclick. │ │ │ │ - */ │ │ │ │ - stopDouble: false, │ │ │ │ + this.projection = this.projection || this.map.projection; │ │ │ │ + if (typeof this.projection == "string") { │ │ │ │ + this.projection = new OpenLayers.Projection(this.projection); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: timerId │ │ │ │ - * {Number} The id of the timeout waiting to clear the <delayedCall>. │ │ │ │ - */ │ │ │ │ - timerId: null, │ │ │ │ + // 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; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: down │ │ │ │ - * {Object} Object that store relevant information about the last │ │ │ │ - * mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives │ │ │ │ - * the average location of the mouse/touch event. Its 'touches' │ │ │ │ - * property records clientX/clientY of each touches. │ │ │ │ - */ │ │ │ │ - down: null, │ │ │ │ + this.initResolutions(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: last │ │ │ │ - * {Object} Object that store relevant information about the last │ │ │ │ - * mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives │ │ │ │ - * the average location of the mouse/touch event. Its 'touches' │ │ │ │ - * property records clientX/clientY of each touches. │ │ │ │ - */ │ │ │ │ - last: null, │ │ │ │ + if (!this.isBaseLayer) { │ │ │ │ + this.inRange = this.calculateInRange(); │ │ │ │ + var show = ((this.visibility) && (this.inRange)); │ │ │ │ + this.div.style.display = show ? "" : "none"; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: first │ │ │ │ - * {Object} When waiting for double clicks, this object will store │ │ │ │ - * information about the first click in a two click sequence. │ │ │ │ - */ │ │ │ │ - first: null, │ │ │ │ + // deal with gutters │ │ │ │ + this.setTileSize(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: rightclickTimerId │ │ │ │ - * {Number} The id of the right mouse timeout waiting to clear the │ │ │ │ - * <delayedEvent>. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - rightclickTimerId: null, │ │ │ │ + afterAdd: function() {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Handler.Click │ │ │ │ - * Create a new click handler. │ │ │ │ + * 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: │ │ │ │ - * control - {<OpenLayers.Control>} The control that is making use of │ │ │ │ - * this handler. If a handler is being used without a control, the │ │ │ │ - * handler's setMap method must be overridden to deal properly with │ │ │ │ - * the map. │ │ │ │ - * callbacks - {Object} An object with keys corresponding to callbacks │ │ │ │ - * that will be called by the handler. The callbacks should │ │ │ │ - * expect to recieve a single argument, the click event. │ │ │ │ - * Callbacks for 'click' and 'dblclick' are supported. │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * handler. │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: touchstart │ │ │ │ - * Handle touchstart. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Continue propagating this event. │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - touchstart: function(evt) { │ │ │ │ - this.startTouch(); │ │ │ │ - this.down = this.getEventInfo(evt); │ │ │ │ - this.last = this.getEventInfo(evt); │ │ │ │ - return true; │ │ │ │ + removeMap: function(map) { │ │ │ │ + //to be overridden by subclasses │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: touchmove │ │ │ │ - * Store position of last move, because touchend event can have │ │ │ │ - * an empty "touches" property. │ │ │ │ + * APIMethod: getImageSize │ │ │ │ * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} optional tile bounds, can be used │ │ │ │ + * by subclasses that have to deal with different tile sizes at the │ │ │ │ + * layer extent edges (e.g. Zoomify) │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Continue propagating this event. │ │ │ │ + * {<OpenLayers.Size>} The size that the image should be, taking into │ │ │ │ + * account gutters. │ │ │ │ */ │ │ │ │ - touchmove: function(evt) { │ │ │ │ - this.last = this.getEventInfo(evt); │ │ │ │ - return true; │ │ │ │ + getImageSize: function(bounds) { │ │ │ │ + return (this.imageSize || this.tileSize); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: touchend │ │ │ │ - * Correctly set event xy property, and add lastTouches to have │ │ │ │ - * touches property from last touchstart or touchmove │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Continue propagating this event. │ │ │ │ + * APIMethod: setTileSize │ │ │ │ + * Set the tile size based on the map size. This also sets layer.imageSize │ │ │ │ + * or use by Tile.Image. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * size - {<OpenLayers.Size>} │ │ │ │ */ │ │ │ │ - touchend: function(evt) { │ │ │ │ - // touchstart may not have been allowed to propagate │ │ │ │ - if (this.down) { │ │ │ │ - evt.xy = this.last.xy; │ │ │ │ - evt.lastTouches = this.last.touches; │ │ │ │ - this.handleSingle(evt); │ │ │ │ - this.down = null; │ │ │ │ + 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)); │ │ │ │ } │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: mousedown │ │ │ │ - * Handle mousedown. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Continue propagating this event. │ │ │ │ - */ │ │ │ │ - mousedown: function(evt) { │ │ │ │ - this.down = this.getEventInfo(evt); │ │ │ │ - this.last = this.getEventInfo(evt); │ │ │ │ - return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: mouseup │ │ │ │ - * Handle mouseup. Installed to support collection of right mouse events. │ │ │ │ + * APIMethod: getVisibility │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Continue propagating this event. │ │ │ │ + * {Boolean} The layer should be displayed (if in range). │ │ │ │ */ │ │ │ │ - mouseup: function(evt) { │ │ │ │ - var propagate = true; │ │ │ │ - │ │ │ │ - // Collect right mouse clicks from the mouseup │ │ │ │ - // IE - ignores the second right click in mousedown so using │ │ │ │ - // mouseup instead │ │ │ │ - if (this.checkModifiers(evt) && this.control.handleRightClicks && │ │ │ │ - OpenLayers.Event.isRightClick(evt)) { │ │ │ │ - propagate = this.rightclick(evt); │ │ │ │ - } │ │ │ │ - │ │ │ │ - return propagate; │ │ │ │ + getVisibility: function() { │ │ │ │ + return this.visibility; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: rightclick │ │ │ │ - * Handle rightclick. For a dblrightclick, we get two clicks so we need │ │ │ │ - * to always register for dblrightclick to properly handle single │ │ │ │ - * clicks. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Continue propagating this event. │ │ │ │ + /** │ │ │ │ + * 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) │ │ │ │ */ │ │ │ │ - rightclick: function(evt) { │ │ │ │ - if (this.passesTolerance(evt)) { │ │ │ │ - if (this.rightclickTimerId != null) { │ │ │ │ - //Second click received before timeout this must be │ │ │ │ - // a double click │ │ │ │ - this.clearTimer(); │ │ │ │ - this.callback('dblrightclick', [evt]); │ │ │ │ - return !this.stopDouble; │ │ │ │ - } else { │ │ │ │ - //Set the rightclickTimerId, send evt only if double is │ │ │ │ - // true else trigger single │ │ │ │ - var clickEvent = this['double'] ? │ │ │ │ - OpenLayers.Util.extend({}, evt) : │ │ │ │ - this.callback('rightclick', [evt]); │ │ │ │ - │ │ │ │ - var delayedRightCall = OpenLayers.Function.bind( │ │ │ │ - this.delayedRightCall, │ │ │ │ - this, │ │ │ │ - clickEvent │ │ │ │ - ); │ │ │ │ - this.rightclickTimerId = window.setTimeout( │ │ │ │ - delayedRightCall, this.delay │ │ │ │ - ); │ │ │ │ + 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" │ │ │ │ + }); │ │ │ │ } │ │ │ │ - } │ │ │ │ - return !this.stopSingle; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: delayedRightCall │ │ │ │ - * Sets <rightclickTimerId> to null. And optionally triggers the │ │ │ │ - * rightclick callback if evt is set. │ │ │ │ - */ │ │ │ │ - delayedRightCall: function(evt) { │ │ │ │ - this.rightclickTimerId = null; │ │ │ │ - if (evt) { │ │ │ │ - this.callback('rightclick', [evt]); │ │ │ │ + this.events.triggerEvent("visibilitychanged"); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: click │ │ │ │ - * Handle click events from the browser. This is registered as a listener │ │ │ │ - * for click events and should not be called from other events in this │ │ │ │ - * handler. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Continue propagating this event. │ │ │ │ + /** │ │ │ │ + * APIMethod: display │ │ │ │ + * Hide or show the Layer. This is designed to be used internally, and │ │ │ │ + * is not generally the way to enable or disable the layer. For that, │ │ │ │ + * use the setVisibility function instead.. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * display - {Boolean} │ │ │ │ */ │ │ │ │ - click: function(evt) { │ │ │ │ - if (!this.last) { │ │ │ │ - this.last = this.getEventInfo(evt); │ │ │ │ + display: function(display) { │ │ │ │ + if (display != (this.div.style.display != "none")) { │ │ │ │ + this.div.style.display = (display && this.calculateInRange()) ? "block" : "none"; │ │ │ │ } │ │ │ │ - this.handleSingle(evt); │ │ │ │ - return !this.stopSingle; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: dblclick │ │ │ │ - * Handle dblclick. For a dblclick, we get two clicks in some browsers │ │ │ │ - * (FF) and one in others (IE). So we need to always register for │ │ │ │ - * dblclick to properly handle single clicks. This method is registered │ │ │ │ - * as a listener for the dblclick browser event. It should *not* be │ │ │ │ - * called by other methods in this handler. │ │ │ │ - * │ │ │ │ + * APIMethod: calculateInRange │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Continue propagating this event. │ │ │ │ + * {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. │ │ │ │ */ │ │ │ │ - dblclick: function(evt) { │ │ │ │ - this.handleDouble(evt); │ │ │ │ - return !this.stopDouble; │ │ │ │ - }, │ │ │ │ + calculateInRange: function() { │ │ │ │ + var inRange = false; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: handleDouble │ │ │ │ - * Handle double-click sequence. │ │ │ │ - */ │ │ │ │ - handleDouble: function(evt) { │ │ │ │ - if (this.passesDblclickTolerance(evt)) { │ │ │ │ - if (this["double"]) { │ │ │ │ - this.callback("dblclick", [evt]); │ │ │ │ + if (this.alwaysInRange) { │ │ │ │ + inRange = true; │ │ │ │ + } else { │ │ │ │ + if (this.map) { │ │ │ │ + var resolution = this.map.getResolution(); │ │ │ │ + inRange = ((resolution >= this.minResolution) && │ │ │ │ + (resolution <= this.maxResolution)); │ │ │ │ } │ │ │ │ - // to prevent a dblclick from firing the click callback in IE │ │ │ │ - this.clearTimer(); │ │ │ │ } │ │ │ │ + return inRange; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleSingle │ │ │ │ - * Handle single click sequence. │ │ │ │ + * APIMethod: setIsBaseLayer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * isBaseLayer - {Boolean} │ │ │ │ */ │ │ │ │ - handleSingle: function(evt) { │ │ │ │ - if (this.passesTolerance(evt)) { │ │ │ │ - if (this.timerId != null) { │ │ │ │ - // already received a click │ │ │ │ - if (this.last.touches && this.last.touches.length === 1) { │ │ │ │ - // touch device, no dblclick event - this may be a double │ │ │ │ - if (this["double"]) { │ │ │ │ - // on Android don't let the browser zoom on the page │ │ │ │ - OpenLayers.Event.preventDefault(evt); │ │ │ │ - } │ │ │ │ - this.handleDouble(evt); │ │ │ │ - } │ │ │ │ - // if we're not in a touch environment we clear the click timer │ │ │ │ - // if we've got a second touch, we'll get two touchend events │ │ │ │ - if (!this.last.touches || this.last.touches.length !== 2) { │ │ │ │ - this.clearTimer(); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - // remember the first click info so we can compare to the second │ │ │ │ - this.first = this.getEventInfo(evt); │ │ │ │ - // set the timer, send evt only if single is true │ │ │ │ - //use a clone of the event object because it will no longer │ │ │ │ - //be a valid event object in IE in the timer callback │ │ │ │ - var clickEvent = this.single ? │ │ │ │ - OpenLayers.Util.extend({}, evt) : null; │ │ │ │ - this.queuePotentialClick(clickEvent); │ │ │ │ + setIsBaseLayer: function(isBaseLayer) { │ │ │ │ + if (isBaseLayer != this.isBaseLayer) { │ │ │ │ + this.isBaseLayer = isBaseLayer; │ │ │ │ + if (this.map != null) { │ │ │ │ + this.map.events.triggerEvent("changebaselayer", { │ │ │ │ + layer: this │ │ │ │ + }); │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: queuePotentialClick │ │ │ │ - * This method is separated out largely to make testing easier (so we │ │ │ │ - * don't have to override window.setTimeout) │ │ │ │ - */ │ │ │ │ - queuePotentialClick: function(evt) { │ │ │ │ - this.timerId = window.setTimeout( │ │ │ │ - OpenLayers.Function.bind(this.delayedCall, this, evt), │ │ │ │ - this.delay │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: passesTolerance │ │ │ │ - * Determine whether the event is within the optional pixel tolerance. Note │ │ │ │ - * that the pixel tolerance check only works if mousedown events get to │ │ │ │ - * the listeners registered here. If they are stopped by other elements, │ │ │ │ - * the <pixelTolerance> will have no effect here (this method will always │ │ │ │ - * return true). │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The click is within the pixel tolerance (if specified). │ │ │ │ - */ │ │ │ │ - passesTolerance: function(evt) { │ │ │ │ - var passes = true; │ │ │ │ - if (this.pixelTolerance != null && this.down && this.down.xy) { │ │ │ │ - passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy); │ │ │ │ - // for touch environments, we also enforce that all touches │ │ │ │ - // start and end within the given tolerance to be considered a click │ │ │ │ - if (passes && this.touch && │ │ │ │ - this.down.touches.length === this.last.touches.length) { │ │ │ │ - // the touchend event doesn't come with touches, so we check │ │ │ │ - // down and last │ │ │ │ - for (var i = 0, ii = this.down.touches.length; i < ii; ++i) { │ │ │ │ - if (this.getTouchDistance( │ │ │ │ - this.down.touches[i], │ │ │ │ - this.last.touches[i] │ │ │ │ - ) > this.pixelTolerance) { │ │ │ │ - passes = false; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return passes; │ │ │ │ - }, │ │ │ │ + /********************************************************/ │ │ │ │ + /* */ │ │ │ │ + /* Baselayer Functions */ │ │ │ │ + /* */ │ │ │ │ + /********************************************************/ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getTouchDistance │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The pixel displacement between two touches. │ │ │ │ + * 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 │ │ │ │ */ │ │ │ │ - getTouchDistance: function(from, to) { │ │ │ │ - return Math.sqrt( │ │ │ │ - Math.pow(from.clientX - to.clientX, 2) + │ │ │ │ - Math.pow(from.clientY - to.clientY, 2) │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ + initResolutions: function() { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: passesDblclickTolerance │ │ │ │ - * Determine whether the event is within the optional double-cick pixel │ │ │ │ - * tolerance. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The click is within the double-click pixel tolerance. │ │ │ │ - */ │ │ │ │ - passesDblclickTolerance: function(evt) { │ │ │ │ - var passes = true; │ │ │ │ - if (this.down && this.first) { │ │ │ │ - passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance; │ │ │ │ - } │ │ │ │ - return passes; │ │ │ │ - }, │ │ │ │ + // 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! │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: clearTimer │ │ │ │ - * Clear the timer and set <timerId> to null. │ │ │ │ - */ │ │ │ │ - clearTimer: function() { │ │ │ │ - if (this.timerId != null) { │ │ │ │ - window.clearTimeout(this.timerId); │ │ │ │ - this.timerId = null; │ │ │ │ + var i, len, p; │ │ │ │ + var props = {}, │ │ │ │ + alwaysInRange = true; │ │ │ │ + │ │ │ │ + // get resolution data from layer config │ │ │ │ + // (we also set alwaysInRange in the layer as appropriate) │ │ │ │ + for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) { │ │ │ │ + p = this.RESOLUTION_PROPERTIES[i]; │ │ │ │ + props[p] = this.options[p]; │ │ │ │ + if (alwaysInRange && this.options[p]) { │ │ │ │ + alwaysInRange = false; │ │ │ │ + } │ │ │ │ } │ │ │ │ - if (this.rightclickTimerId != null) { │ │ │ │ - window.clearTimeout(this.rightclickTimerId); │ │ │ │ - this.rightclickTimerId = null; │ │ │ │ + if (this.options.alwaysInRange == null) { │ │ │ │ + this.alwaysInRange = alwaysInRange; │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: delayedCall │ │ │ │ - * Sets <timerId> to null. And optionally triggers the click callback if │ │ │ │ - * evt is set. │ │ │ │ - */ │ │ │ │ - delayedCall: function(evt) { │ │ │ │ - this.timerId = null; │ │ │ │ - if (evt) { │ │ │ │ - this.callback("click", [evt]); │ │ │ │ + // if we don't have resolutions then attempt to derive them from scales │ │ │ │ + if (props.resolutions == null) { │ │ │ │ + props.resolutions = this.resolutionsFromScales(props.scales); │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getEventInfo │ │ │ │ - * This method allows us to store event information without storing the │ │ │ │ - * actual event. In touch devices (at least), the same event is │ │ │ │ - * modified between touchstart, touchmove, and touchend. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object with event related info. │ │ │ │ - */ │ │ │ │ - getEventInfo: function(evt) { │ │ │ │ - var touches; │ │ │ │ - if (evt.touches) { │ │ │ │ - var len = evt.touches.length; │ │ │ │ - touches = new Array(len); │ │ │ │ - var touch; │ │ │ │ - for (var i = 0; i < len; i++) { │ │ │ │ - touch = evt.touches[i]; │ │ │ │ - touches[i] = { │ │ │ │ - clientX: touch.olClientX, │ │ │ │ - clientY: touch.olClientY │ │ │ │ - }; │ │ │ │ - } │ │ │ │ + // if we still don't have resolutions then attempt to calculate them │ │ │ │ + if (props.resolutions == null) { │ │ │ │ + props.resolutions = this.calculateResolutions(props); │ │ │ │ } │ │ │ │ - return { │ │ │ │ - xy: evt.xy, │ │ │ │ - touches: touches │ │ │ │ - }; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: deactivate │ │ │ │ - * Deactivate the handler. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The handler was successfully deactivated. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.clearTimer(); │ │ │ │ - this.down = null; │ │ │ │ - this.first = null; │ │ │ │ - this.last = null; │ │ │ │ - deactivated = true; │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ } │ │ │ │ - return deactivated; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Click" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/Navigation.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control/ZoomBox.js │ │ │ │ - * @requires OpenLayers/Control/DragPan.js │ │ │ │ - * @requires OpenLayers/Handler/MouseWheel.js │ │ │ │ - * @requires OpenLayers/Handler/Click.js │ │ │ │ - */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.Navigation │ │ │ │ - * The navigation control handles map browsing with mouse events (dragging, │ │ │ │ - * double-clicking, and scrolling the wheel). Create a new navigation │ │ │ │ - * control with the <OpenLayers.Control.Navigation> control. │ │ │ │ - * │ │ │ │ - * Note that this control is added to the map by default (if no controls │ │ │ │ - * array is sent in the options object to the <OpenLayers.Map> │ │ │ │ - * constructor). │ │ │ │ - * │ │ │ │ - * Inherits: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + // ok, we new need to set properties in the instance │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: dragPan │ │ │ │ - * {<OpenLayers.Control.DragPan>} │ │ │ │ - */ │ │ │ │ - dragPan: null, │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: dragPanOptions │ │ │ │ - * {Object} Options passed to the DragPan control. │ │ │ │ - */ │ │ │ │ - dragPanOptions: null, │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: pinchZoom │ │ │ │ - * {<OpenLayers.Control.PinchZoom>} │ │ │ │ - */ │ │ │ │ - pinchZoom: null, │ │ │ │ + if (props.resolutions) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: pinchZoomOptions │ │ │ │ - * {Object} Options passed to the PinchZoom control. │ │ │ │ - */ │ │ │ │ - pinchZoomOptions: null, │ │ │ │ + //sort resolutions array descendingly │ │ │ │ + props.resolutions.sort(function(a, b) { │ │ │ │ + return (b - a); │ │ │ │ + }); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: documentDrag │ │ │ │ - * {Boolean} Allow panning of the map by dragging outside map viewport. │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - documentDrag: false, │ │ │ │ + // if we still don't have a maxResolution get it from the │ │ │ │ + // resolutions array │ │ │ │ + if (!maxResolution) { │ │ │ │ + maxResolution = props.resolutions[0]; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: zoomBox │ │ │ │ - * {<OpenLayers.Control.ZoomBox>} │ │ │ │ - */ │ │ │ │ - zoomBox: 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]; │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomBoxEnabled │ │ │ │ - * {Boolean} Whether the user can draw a box to zoom │ │ │ │ - */ │ │ │ │ - zoomBoxEnabled: true, │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: zoomWheelEnabled │ │ │ │ - * {Boolean} Whether the mousewheel should zoom the map │ │ │ │ + * Method: resolutionsFromScales │ │ │ │ + * Derive resolutions from scales. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * scales - {Array(Number)} Scales │ │ │ │ + * │ │ │ │ + * Returns │ │ │ │ + * {Array(Number)} Resolutions │ │ │ │ */ │ │ │ │ - zoomWheelEnabled: true, │ │ │ │ + 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; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: mouseWheelOptions │ │ │ │ - * {Object} Options passed to the MouseWheel control (only useful if │ │ │ │ - * <zoomWheelEnabled> is set to true). Default is no options for maps │ │ │ │ - * with fractionalZoom set to true, otherwise │ │ │ │ - * {cumulative: false, interval: 50, maxDelta: 6} │ │ │ │ + * Method: calculateResolutions │ │ │ │ + * Calculate resolutions based on the provided properties. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * props - {Object} Properties │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array({Number})} Array of resolutions. │ │ │ │ */ │ │ │ │ - mouseWheelOptions: null, │ │ │ │ + calculateResolutions: function(props) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: handleRightClicks │ │ │ │ - * {Boolean} Whether or not to handle right clicks. Default is false. │ │ │ │ - */ │ │ │ │ - handleRightClicks: false, │ │ │ │ + var viewSize, wRes, hRes; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: zoomBoxKeyMask │ │ │ │ - * {Integer} <OpenLayers.Handler> key code of the key, which has to be │ │ │ │ - * pressed, while drawing the zoom box with the mouse on the screen. │ │ │ │ - * You should probably set handleRightClicks to true if you use this │ │ │ │ - * with MOD_CTRL, to disable the context menu for machines which use │ │ │ │ - * CTRL-Click as a right click. │ │ │ │ - * Default: <OpenLayers.Handler.MOD_SHIFT> │ │ │ │ - */ │ │ │ │ - zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT, │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: autoActivate │ │ │ │ - * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ - * true. │ │ │ │ - */ │ │ │ │ - autoActivate: true, │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.Navigation │ │ │ │ - * Create a new navigation control │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * the control │ │ │ │ - */ │ │ │ │ - initialize: function(options) { │ │ │ │ - this.handlers = {}; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + 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: destroy │ │ │ │ - * The destroy method is used to perform any clean up before the control │ │ │ │ - * is dereferenced. Typically this is where event listeners are removed │ │ │ │ - * to prevent memory leaks. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ + // 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; │ │ │ │ + } │ │ │ │ │ │ │ │ - if (this.dragPan) { │ │ │ │ - this.dragPan.destroy(); │ │ │ │ + // are we able to calculate resolutions? │ │ │ │ + if (typeof numZoomLevels !== "number" || numZoomLevels <= 0 || │ │ │ │ + (typeof maxResolution !== "number" && │ │ │ │ + typeof minResolution !== "number")) { │ │ │ │ + return; │ │ │ │ } │ │ │ │ - this.dragPan = null; │ │ │ │ │ │ │ │ - if (this.zoomBox) { │ │ │ │ - this.zoomBox.destroy(); │ │ │ │ + // now we have numZoomLevels and at least one of maxResolution │ │ │ │ + // or minResolution, we can populate the resolutions array │ │ │ │ + │ │ │ │ + var resolutions = new Array(numZoomLevels); │ │ │ │ + var base = 2; │ │ │ │ + if (typeof minResolution == "number" && │ │ │ │ + typeof maxResolution == "number") { │ │ │ │ + // if maxResolution and minResolution are set, we calculate │ │ │ │ + // the base for exponential scaling that starts at │ │ │ │ + // maxResolution and ends at minResolution in numZoomLevels │ │ │ │ + // steps. │ │ │ │ + base = Math.pow( │ │ │ │ + (maxResolution / minResolution), │ │ │ │ + (1 / (numZoomLevels - 1)) │ │ │ │ + ); │ │ │ │ } │ │ │ │ - this.zoomBox = null; │ │ │ │ │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.destroy(); │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.pinchZoom = null; │ │ │ │ │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + return resolutions; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: activate │ │ │ │ + * APIMethod: getResolution │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} The currently selected resolution of the map, taken from the │ │ │ │ + * resolutions array, indexed by current zoom level. │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - this.dragPan.activate(); │ │ │ │ - if (this.zoomWheelEnabled) { │ │ │ │ - this.handlers.wheel.activate(); │ │ │ │ - } │ │ │ │ - this.handlers.click.activate(); │ │ │ │ - if (this.zoomBoxEnabled) { │ │ │ │ - this.zoomBox.activate(); │ │ │ │ - } │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.activate(); │ │ │ │ - } │ │ │ │ - return OpenLayers.Control.prototype.activate.apply(this, arguments); │ │ │ │ + getResolution: function() { │ │ │ │ + var zoom = this.map.getZoom(); │ │ │ │ + return this.getResolutionForZoom(zoom); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: deactivate │ │ │ │ + /** │ │ │ │ + * APIMethod: getExtent │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat │ │ │ │ + * bounds of the current viewPort. │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.deactivate(); │ │ │ │ - } │ │ │ │ - this.zoomBox.deactivate(); │ │ │ │ - this.dragPan.deactivate(); │ │ │ │ - this.handlers.click.deactivate(); │ │ │ │ - this.handlers.wheel.deactivate(); │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply(this, arguments); │ │ │ │ + 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: draw │ │ │ │ + * APIMethod: getZoomForExtent │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * extent - {<OpenLayers.Bounds>} │ │ │ │ + * closest - {Boolean} Find the zoom level that most closely fits the │ │ │ │ + * specified bounds. Note that this may result in a zoom that does │ │ │ │ + * not exactly contain the entire extent. │ │ │ │ + * Default is false. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} The index of the zoomLevel (entry in the resolutions array) │ │ │ │ + * for the passed-in extent. We do this by calculating the ideal │ │ │ │ + * resolution for the given extent (based on the map size) and then │ │ │ │ + * calling getZoomForResolution(), passing along the 'closest' │ │ │ │ + * parameter. │ │ │ │ */ │ │ │ │ - draw: function() { │ │ │ │ - // disable right mouse context menu for support of right click events │ │ │ │ - if (this.handleRightClicks) { │ │ │ │ - this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False; │ │ │ │ - } │ │ │ │ + getZoomForExtent: function(extent, closest) { │ │ │ │ + var viewSize = this.map.getSize(); │ │ │ │ + var idealResolution = Math.max(extent.getWidth() / viewSize.w, │ │ │ │ + extent.getHeight() / viewSize.h); │ │ │ │ │ │ │ │ - var clickCallbacks = { │ │ │ │ - 'click': this.defaultClick, │ │ │ │ - 'dblclick': this.defaultDblClick, │ │ │ │ - 'dblrightclick': this.defaultDblRightClick │ │ │ │ - }; │ │ │ │ - var clickOptions = { │ │ │ │ - 'double': true, │ │ │ │ - 'stopDouble': true │ │ │ │ - }; │ │ │ │ - this.handlers.click = new OpenLayers.Handler.Click( │ │ │ │ - this, clickCallbacks, clickOptions │ │ │ │ - ); │ │ │ │ - this.dragPan = new OpenLayers.Control.DragPan( │ │ │ │ - OpenLayers.Util.extend({ │ │ │ │ - map: this.map, │ │ │ │ - documentDrag: this.documentDrag │ │ │ │ - }, this.dragPanOptions) │ │ │ │ - ); │ │ │ │ - this.zoomBox = new OpenLayers.Control.ZoomBox({ │ │ │ │ - map: this.map, │ │ │ │ - keyMask: this.zoomBoxKeyMask │ │ │ │ - }); │ │ │ │ - this.dragPan.draw(); │ │ │ │ - this.zoomBox.draw(); │ │ │ │ - var wheelOptions = this.map.fractionalZoom ? {} : { │ │ │ │ - cumulative: false, │ │ │ │ - interval: 50, │ │ │ │ - maxDelta: 6 │ │ │ │ - }; │ │ │ │ - this.handlers.wheel = new OpenLayers.Handler.MouseWheel( │ │ │ │ - this, { │ │ │ │ - up: this.wheelUp, │ │ │ │ - down: this.wheelDown │ │ │ │ - }, │ │ │ │ - OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions) │ │ │ │ - ); │ │ │ │ - if (OpenLayers.Control.PinchZoom) { │ │ │ │ - this.pinchZoom = new OpenLayers.Control.PinchZoom( │ │ │ │ - OpenLayers.Util.extend({ │ │ │ │ - map: this.map │ │ │ │ - }, this.pinchZoomOptions)); │ │ │ │ - } │ │ │ │ + return this.getZoomForResolution(idealResolution, closest); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: defaultClick │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + /** │ │ │ │ + * Method: getDataExtent │ │ │ │ + * Calculates the max extent which includes all of the data for the layer. │ │ │ │ + * This function is to be implemented by subclasses. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - defaultClick: function(evt) { │ │ │ │ - if (evt.lastTouches && evt.lastTouches.length == 2) { │ │ │ │ - this.map.zoomOut(); │ │ │ │ - } │ │ │ │ + getDataExtent: function() { │ │ │ │ + //to be implemented by subclasses │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: defaultDblClick │ │ │ │ + * APIMethod: getResolutionForZoom │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * zoom - {Float} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} A suitable resolution for the specified zoom. │ │ │ │ */ │ │ │ │ - defaultDblClick: function(evt) { │ │ │ │ - this.map.zoomTo(this.map.zoom + 1, evt.xy); │ │ │ │ + getResolutionForZoom: function(zoom) { │ │ │ │ + zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1)); │ │ │ │ + var resolution; │ │ │ │ + if (this.map.fractionalZoom) { │ │ │ │ + var low = Math.floor(zoom); │ │ │ │ + var high = Math.ceil(zoom); │ │ │ │ + resolution = this.resolutions[low] - │ │ │ │ + ((zoom - low) * (this.resolutions[low] - this.resolutions[high])); │ │ │ │ + } else { │ │ │ │ + resolution = this.resolutions[Math.round(zoom)]; │ │ │ │ + } │ │ │ │ + return resolution; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: defaultDblRightClick │ │ │ │ + * APIMethod: getZoomForResolution │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * resolution - {Float} │ │ │ │ + * closest - {Boolean} Find the zoom level that corresponds to the absolute │ │ │ │ + * closest resolution, which may result in a zoom whose corresponding │ │ │ │ + * resolution is actually smaller than we would have desired (if this │ │ │ │ + * is being called from a getZoomForExtent() call, then this means that │ │ │ │ + * the returned zoom index might not actually contain the entire │ │ │ │ + * extent specified... but it'll be close). │ │ │ │ + * Default is false. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} The index of the zoomLevel (entry in the resolutions array) │ │ │ │ + * that corresponds to the best fit resolution given the passed in │ │ │ │ + * value and the 'closest' specification. │ │ │ │ */ │ │ │ │ - defaultDblRightClick: function(evt) { │ │ │ │ - this.map.zoomTo(this.map.zoom - 1, evt.xy); │ │ │ │ + getZoomForResolution: function(resolution, closest) { │ │ │ │ + var zoom, i, len; │ │ │ │ + if (this.map.fractionalZoom) { │ │ │ │ + var lowZoom = 0; │ │ │ │ + var highZoom = this.resolutions.length - 1; │ │ │ │ + var highRes = this.resolutions[lowZoom]; │ │ │ │ + var lowRes = this.resolutions[highZoom]; │ │ │ │ + var res; │ │ │ │ + for (i = 0, len = this.resolutions.length; i < len; ++i) { │ │ │ │ + res = this.resolutions[i]; │ │ │ │ + if (res >= resolution) { │ │ │ │ + highRes = res; │ │ │ │ + lowZoom = i; │ │ │ │ + } │ │ │ │ + if (res <= resolution) { │ │ │ │ + lowRes = res; │ │ │ │ + highZoom = i; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var dRes = highRes - lowRes; │ │ │ │ + if (dRes > 0) { │ │ │ │ + zoom = lowZoom + ((highRes - resolution) / dRes); │ │ │ │ + } else { │ │ │ │ + zoom = lowZoom; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + var diff; │ │ │ │ + var minDiff = Number.POSITIVE_INFINITY; │ │ │ │ + for (i = 0, len = this.resolutions.length; i < len; i++) { │ │ │ │ + if (closest) { │ │ │ │ + diff = Math.abs(this.resolutions[i] - resolution); │ │ │ │ + if (diff > minDiff) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + minDiff = diff; │ │ │ │ + } else { │ │ │ │ + if (this.resolutions[i] < resolution) { │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + zoom = Math.max(0, i - 1); │ │ │ │ + } │ │ │ │ + return zoom; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: wheelChange │ │ │ │ - * │ │ │ │ + * APIMethod: getLonLatFromViewPortPx │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * deltaZ - {Integer} │ │ │ │ + * viewPortPx - {<OpenLayers.Pixel>|Object} An OpenLayers.Pixel or │ │ │ │ + * an object with a 'x' │ │ │ │ + * and 'y' properties. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in │ │ │ │ + * view port <OpenLayers.Pixel>, translated into lon/lat by the layer. │ │ │ │ */ │ │ │ │ - wheelChange: function(evt, deltaZ) { │ │ │ │ - if (!this.map.fractionalZoom) { │ │ │ │ - deltaZ = Math.round(deltaZ); │ │ │ │ - } │ │ │ │ - var currentZoom = this.map.getZoom(), │ │ │ │ - newZoom = currentZoom + deltaZ; │ │ │ │ - newZoom = Math.max(newZoom, 0); │ │ │ │ - newZoom = Math.min(newZoom, this.map.getNumZoomLevels()); │ │ │ │ - if (newZoom === currentZoom) { │ │ │ │ - return; │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.map.zoomTo(newZoom, evt.xy); │ │ │ │ + return lonlat; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: wheelUp │ │ │ │ - * User spun scroll wheel up │ │ │ │ + /** │ │ │ │ + * APIMethod: getViewPortPxFromLonLat │ │ │ │ + * Returns a pixel location given a map location. This method will return │ │ │ │ + * fractional pixel values. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * delta - {Integer} │ │ │ │ + * lonlat - {<OpenLayers.LonLat>|Object} An OpenLayers.LonLat or │ │ │ │ + * an object with a 'lon' │ │ │ │ + * and 'lat' properties. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in │ │ │ │ + * lonlat translated into view port pixels. │ │ │ │ */ │ │ │ │ - wheelUp: function(evt, delta) { │ │ │ │ - this.wheelChange(evt, delta || 1); │ │ │ │ + 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; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: wheelDown │ │ │ │ - * User spun scroll wheel down │ │ │ │ + /** │ │ │ │ + * APIMethod: setOpacity │ │ │ │ + * Sets the opacity for the entire layer (all images) │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * delta - {Integer} │ │ │ │ + * opacity - {Float} │ │ │ │ */ │ │ │ │ - wheelDown: function(evt, delta) { │ │ │ │ - this.wheelChange(evt, delta || -1); │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ + if (opacity != this.opacity) { │ │ │ │ + this.opacity = opacity; │ │ │ │ + var childNodes = this.div.childNodes; │ │ │ │ + for (var i = 0, len = childNodes.length; i < len; ++i) { │ │ │ │ + var element = childNodes[i].firstChild || childNodes[i]; │ │ │ │ + var lastChild = childNodes[i].lastChild; │ │ │ │ + //TODO de-uglify this │ │ │ │ + if (lastChild && lastChild.nodeName.toLowerCase() === "iframe") { │ │ │ │ + element = lastChild.parentNode; │ │ │ │ + } │ │ │ │ + OpenLayers.Util.modifyDOMElement(element, null, null, null, │ │ │ │ + null, null, null, opacity); │ │ │ │ + } │ │ │ │ + if (this.map != null) { │ │ │ │ + this.map.events.triggerEvent("changelayer", { │ │ │ │ + layer: this, │ │ │ │ + property: "opacity" │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: disableZoomBox │ │ │ │ + * Method: getZIndex │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} the z-index of this layer │ │ │ │ */ │ │ │ │ - disableZoomBox: function() { │ │ │ │ - this.zoomBoxEnabled = false; │ │ │ │ - this.zoomBox.deactivate(); │ │ │ │ + getZIndex: function() { │ │ │ │ + return this.div.style.zIndex; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: enableZoomBox │ │ │ │ + * Method: setZIndex │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * zIndex - {Integer} │ │ │ │ */ │ │ │ │ - enableZoomBox: function() { │ │ │ │ - this.zoomBoxEnabled = true; │ │ │ │ - if (this.active) { │ │ │ │ - this.zoomBox.activate(); │ │ │ │ - } │ │ │ │ + setZIndex: function(zIndex) { │ │ │ │ + this.div.style.zIndex = zIndex; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: disableZoomWheel │ │ │ │ + * Method: adjustBounds │ │ │ │ + * This function will take a bounds, and if wrapDateLine option is set │ │ │ │ + * on the layer, it will return a bounds which is wrapped around the │ │ │ │ + * world. We do not wrap for bounds which *cross* the │ │ │ │ + * maxExtent.left/right, only bounds which are entirely to the left │ │ │ │ + * or entirely to the right. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ + adjustBounds: function(bounds) { │ │ │ │ │ │ │ │ - disableZoomWheel: function() { │ │ │ │ - this.zoomWheelEnabled = false; │ │ │ │ - this.handlers.wheel.deactivate(); │ │ │ │ - }, │ │ │ │ + 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: enableZoomWheel │ │ │ │ - */ │ │ │ │ + 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); │ │ │ │ │ │ │ │ - enableZoomWheel: function() { │ │ │ │ - this.zoomWheelEnabled = true; │ │ │ │ - if (this.active) { │ │ │ │ - this.handlers.wheel.activate(); │ │ │ │ } │ │ │ │ + return bounds; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Navigation" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Handler/Feature.js │ │ │ │ + OpenLayers/Layer/Vector.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Handler.js │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ + * @requires OpenLayers/Renderer.js │ │ │ │ + * @requires OpenLayers/StyleMap.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Console.js │ │ │ │ + * @requires OpenLayers/Lang.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Handler.Feature │ │ │ │ - * Handler to respond to mouse events related to a drawn feature. Callbacks │ │ │ │ - * with the following keys will be notified of the following events │ │ │ │ - * associated with features: click, clickout, over, out, and dblclick. │ │ │ │ - * │ │ │ │ - * This handler stops event propagation for mousedown and mouseup if those │ │ │ │ - * browser events target features that can be selected. │ │ │ │ + * Class: OpenLayers.Layer.Vector │ │ │ │ + * Instances of OpenLayers.Layer.Vector are used to render vector data from │ │ │ │ + * a variety of sources. Create a new vector layer with the │ │ │ │ + * <OpenLayers.Layer.Vector> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Handler> │ │ │ │ + * - <OpenLayers.Layer> │ │ │ │ */ │ │ │ │ -OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ +OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: EVENTMAP │ │ │ │ - * {Object} A object mapping the browser events to objects with callback │ │ │ │ - * keys for in and out. │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * layer.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Listeners will be called with a reference to an event object. The │ │ │ │ + * properties of this event depends on exactly what happened. │ │ │ │ + * │ │ │ │ + * All event objects have at least the following properties: │ │ │ │ + * object - {Object} A reference to layer.events.object. │ │ │ │ + * element - {DOMElement} A reference to layer.events.element. │ │ │ │ + * │ │ │ │ + * Supported map event types (in addition to those from <OpenLayers.Layer.events>): │ │ │ │ + * beforefeatureadded - Triggered before a feature is added. Listeners │ │ │ │ + * will receive an object with a *feature* property referencing the │ │ │ │ + * feature to be added. To stop the feature from being added, a │ │ │ │ + * listener should return false. │ │ │ │ + * beforefeaturesadded - Triggered before an array of features is added. │ │ │ │ + * Listeners will receive an object with a *features* property │ │ │ │ + * referencing the feature to be added. To stop the features from │ │ │ │ + * being added, a listener should return false. │ │ │ │ + * featureadded - Triggered after a feature is added. The event │ │ │ │ + * object passed to listeners will have a *feature* property with a │ │ │ │ + * reference to the added feature. │ │ │ │ + * featuresadded - Triggered after features are added. The event │ │ │ │ + * object passed to listeners will have a *features* property with a │ │ │ │ + * reference to an array of added features. │ │ │ │ + * beforefeatureremoved - Triggered before a feature is removed. Listeners │ │ │ │ + * will receive an object with a *feature* property referencing the │ │ │ │ + * feature to be removed. │ │ │ │ + * beforefeaturesremoved - Triggered before multiple features are removed. │ │ │ │ + * Listeners will receive an object with a *features* property │ │ │ │ + * referencing the features to be removed. │ │ │ │ + * featureremoved - Triggerd after a feature is removed. The event │ │ │ │ + * object passed to listeners will have a *feature* property with a │ │ │ │ + * reference to the removed feature. │ │ │ │ + * featuresremoved - Triggered after features are removed. The event │ │ │ │ + * object passed to listeners will have a *features* property with a │ │ │ │ + * reference to an array of removed features. │ │ │ │ + * beforefeatureselected - Triggered before a feature is selected. Listeners │ │ │ │ + * will receive an object with a *feature* property referencing the │ │ │ │ + * feature to be selected. To stop the feature from being selectd, a │ │ │ │ + * listener should return false. │ │ │ │ + * featureselected - Triggered after a feature is selected. Listeners │ │ │ │ + * will receive an object with a *feature* property referencing the │ │ │ │ + * selected feature. │ │ │ │ + * featureunselected - Triggered after a feature is unselected. │ │ │ │ + * Listeners will receive an object with a *feature* property │ │ │ │ + * referencing the unselected feature. │ │ │ │ + * beforefeaturemodified - Triggered when a feature is selected to │ │ │ │ + * be modified. Listeners will receive an object with a *feature* │ │ │ │ + * property referencing the selected feature. │ │ │ │ + * featuremodified - Triggered when a feature has been modified. │ │ │ │ + * Listeners will receive an object with a *feature* property referencing │ │ │ │ + * the modified feature. │ │ │ │ + * afterfeaturemodified - Triggered when a feature is finished being modified. │ │ │ │ + * Listeners will receive an object with a *feature* property referencing │ │ │ │ + * the modified feature. │ │ │ │ + * vertexmodified - Triggered when a vertex within any feature geometry │ │ │ │ + * has been modified. Listeners will receive an object with a │ │ │ │ + * *feature* property referencing the modified feature, a *vertex* │ │ │ │ + * property referencing the vertex modified (always a point geometry), │ │ │ │ + * and a *pixel* property referencing the pixel location of the │ │ │ │ + * modification. │ │ │ │ + * vertexremoved - Triggered when a vertex within any feature geometry │ │ │ │ + * has been deleted. Listeners will receive an object with a │ │ │ │ + * *feature* property referencing the modified feature, a *vertex* │ │ │ │ + * property referencing the vertex modified (always a point geometry), │ │ │ │ + * and a *pixel* property referencing the pixel location of the │ │ │ │ + * removal. │ │ │ │ + * sketchstarted - Triggered when a feature sketch bound for this layer │ │ │ │ + * is started. Listeners will receive an object with a *feature* │ │ │ │ + * property referencing the new sketch feature and a *vertex* property │ │ │ │ + * referencing the creation point. │ │ │ │ + * sketchmodified - Triggered when a feature sketch bound for this layer │ │ │ │ + * is modified. Listeners will receive an object with a *vertex* │ │ │ │ + * property referencing the modified vertex and a *feature* property │ │ │ │ + * referencing the sketch feature. │ │ │ │ + * sketchcomplete - Triggered when a feature sketch bound for this layer │ │ │ │ + * is complete. Listeners will receive an object with a *feature* │ │ │ │ + * property referencing the sketch feature. By returning false, a │ │ │ │ + * listener can stop the sketch feature from being added to the layer. │ │ │ │ + * refresh - Triggered when something wants a strategy to ask the protocol │ │ │ │ + * for a new set of features. │ │ │ │ */ │ │ │ │ - EVENTMAP: { │ │ │ │ - 'click': { │ │ │ │ - 'in': 'click', │ │ │ │ - 'out': 'clickout' │ │ │ │ - }, │ │ │ │ - 'mousemove': { │ │ │ │ - 'in': 'over', │ │ │ │ - 'out': 'out' │ │ │ │ - }, │ │ │ │ - 'dblclick': { │ │ │ │ - 'in': 'dblclick', │ │ │ │ - 'out': null │ │ │ │ - }, │ │ │ │ - 'mousedown': { │ │ │ │ - 'in': null, │ │ │ │ - 'out': null │ │ │ │ - }, │ │ │ │ - 'mouseup': { │ │ │ │ - 'in': null, │ │ │ │ - 'out': null │ │ │ │ - }, │ │ │ │ - 'touchstart': { │ │ │ │ - 'in': 'click', │ │ │ │ - 'out': 'clickout' │ │ │ │ - } │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: feature │ │ │ │ - * {<OpenLayers.Feature.Vector>} The last feature that was hovered. │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} The layer is a base layer. Default is false. Set this property │ │ │ │ + * in the layer options. │ │ │ │ */ │ │ │ │ - feature: null, │ │ │ │ + isBaseLayer: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: lastFeature │ │ │ │ - * {<OpenLayers.Feature.Vector>} The last feature that was handled. │ │ │ │ + /** │ │ │ │ + * APIProperty: isFixed │ │ │ │ + * {Boolean} Whether the layer remains in one place while dragging the │ │ │ │ + * map. Note that setting this to true will move the layer to the bottom │ │ │ │ + * of the layer stack. │ │ │ │ */ │ │ │ │ - lastFeature: null, │ │ │ │ + isFixed: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: down │ │ │ │ - * {<OpenLayers.Pixel>} The location of the last mousedown. │ │ │ │ + /** │ │ │ │ + * APIProperty: features │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ */ │ │ │ │ - down: null, │ │ │ │ + features: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: up │ │ │ │ - * {<OpenLayers.Pixel>} The location of the last mouseup. │ │ │ │ + /** │ │ │ │ + * Property: filter │ │ │ │ + * {<OpenLayers.Filter>} The filter set in this layer, │ │ │ │ + * a strategy launching read requests can combined │ │ │ │ + * this filter with its own filter. │ │ │ │ */ │ │ │ │ - up: null, │ │ │ │ + filter: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: clickTolerance │ │ │ │ - * {Number} The number of pixels the mouse can move between mousedown │ │ │ │ - * and mouseup for the event to still be considered a click. │ │ │ │ - * Dragging the map should not trigger the click and clickout callbacks │ │ │ │ - * unless the map is moved by less than this tolerance. Defaults to 4. │ │ │ │ + /** │ │ │ │ + * Property: selectedFeatures │ │ │ │ + * {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ */ │ │ │ │ - clickTolerance: 4, │ │ │ │ + selectedFeatures: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: geometryTypes │ │ │ │ - * To restrict dragging to a limited set of geometry types, send a list │ │ │ │ - * of strings corresponding to the geometry class names. │ │ │ │ - * │ │ │ │ - * @type Array(String) │ │ │ │ + * Property: unrenderedFeatures │ │ │ │ + * {Object} hash of features, keyed by feature.id, that the renderer │ │ │ │ + * failed to draw │ │ │ │ */ │ │ │ │ - geometryTypes: null, │ │ │ │ + unrenderedFeatures: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: stopClick │ │ │ │ - * {Boolean} If stopClick is set to true, handled clicks do not │ │ │ │ - * propagate to other click listeners. Otherwise, handled clicks │ │ │ │ - * do propagate. Unhandled clicks always propagate, whatever the │ │ │ │ - * value of stopClick. Defaults to true. │ │ │ │ + * APIProperty: reportError │ │ │ │ + * {Boolean} report friendly error message when loading of renderer │ │ │ │ + * fails. │ │ │ │ */ │ │ │ │ - stopClick: true, │ │ │ │ + reportError: true, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: stopDown │ │ │ │ - * {Boolean} If stopDown is set to true, handled mousedowns do not │ │ │ │ - * propagate to other mousedown listeners. Otherwise, handled │ │ │ │ - * mousedowns do propagate. Unhandled mousedowns always propagate, │ │ │ │ - * whatever the value of stopDown. Defaults to true. │ │ │ │ + /** │ │ │ │ + * APIProperty: style │ │ │ │ + * {Object} Default style for the layer │ │ │ │ */ │ │ │ │ - stopDown: true, │ │ │ │ + style: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: stopUp │ │ │ │ - * {Boolean} If stopUp is set to true, handled mouseups do not │ │ │ │ - * propagate to other mouseup listeners. Otherwise, handled mouseups │ │ │ │ - * do propagate. Unhandled mouseups always propagate, whatever the │ │ │ │ - * value of stopUp. Defaults to false. │ │ │ │ + * Property: styleMap │ │ │ │ + * {<OpenLayers.StyleMap>} │ │ │ │ */ │ │ │ │ - stopUp: false, │ │ │ │ + styleMap: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Handler.Feature │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * control - {<OpenLayers.Control>} │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} │ │ │ │ - * callbacks - {Object} An object with a 'over' property whos value is │ │ │ │ - * a function to be called when the mouse is over a feature. The │ │ │ │ - * callback should expect to recieve a single argument, the feature. │ │ │ │ - * options - {Object} │ │ │ │ + * Property: strategies │ │ │ │ + * {Array(<OpenLayers.Strategy>})} Optional list of strategies for the layer. │ │ │ │ */ │ │ │ │ - initialize: function(control, layer, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]); │ │ │ │ - this.layer = layer; │ │ │ │ - }, │ │ │ │ + strategies: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: touchstart │ │ │ │ - * Handle touchstart events │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Let the event propagate. │ │ │ │ + * Property: protocol │ │ │ │ + * {<OpenLayers.Protocol>} Optional protocol for the layer. │ │ │ │ */ │ │ │ │ - touchstart: function(evt) { │ │ │ │ - this.startTouch(); │ │ │ │ - return OpenLayers.Event.isMultiTouch(evt) ? │ │ │ │ - true : this.mousedown(evt); │ │ │ │ - }, │ │ │ │ + protocol: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: touchmove │ │ │ │ - * Handle touchmove events. We just prevent the browser default behavior, │ │ │ │ - * for Android Webkit not to select text when moving the finger after │ │ │ │ - * selecting a feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * Property: renderers │ │ │ │ + * {Array(String)} List of supported Renderer classes. Add to this list to │ │ │ │ + * add support for additional renderers. This list is ordered: │ │ │ │ + * the first renderer which returns true for the 'supported()' │ │ │ │ + * method will be used, if not defined in the 'renderer' option. │ │ │ │ */ │ │ │ │ - touchmove: function(evt) { │ │ │ │ - OpenLayers.Event.preventDefault(evt); │ │ │ │ - }, │ │ │ │ + renderers: ['SVG', 'VML', 'Canvas'], │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: mousedown │ │ │ │ - * Handle mouse down. Stop propagation if a feature is targeted by this │ │ │ │ - * event (stops map dragging during feature selection). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + /** │ │ │ │ + * Property: renderer │ │ │ │ + * {<OpenLayers.Renderer>} │ │ │ │ */ │ │ │ │ - mousedown: function(evt) { │ │ │ │ - // Feature selection is only done with a left click. Other handlers may stop the │ │ │ │ - // propagation of left-click mousedown events but not right-click mousedown events. │ │ │ │ - // This mismatch causes problems when comparing the location of the down and up │ │ │ │ - // events in the click function so it is important ignore right-clicks. │ │ │ │ - if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) { │ │ │ │ - this.down = evt.xy; │ │ │ │ - } │ │ │ │ - return this.handle(evt) ? !this.stopDown : true; │ │ │ │ - }, │ │ │ │ + renderer: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: mouseup │ │ │ │ - * Handle mouse up. Stop propagation if a feature is targeted by this │ │ │ │ - * event. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ + * APIProperty: rendererOptions │ │ │ │ + * {Object} Options for the renderer. See {<OpenLayers.Renderer>} for │ │ │ │ + * supported options. │ │ │ │ */ │ │ │ │ - mouseup: function(evt) { │ │ │ │ - this.up = evt.xy; │ │ │ │ - return this.handle(evt) ? !this.stopUp : true; │ │ │ │ - }, │ │ │ │ + rendererOptions: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: click │ │ │ │ - * Handle click. Call the "click" callback if click on a feature, │ │ │ │ - * or the "clickout" callback if click outside any feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} │ │ │ │ + /** │ │ │ │ + * APIProperty: geometryType │ │ │ │ + * {String} geometryType allows you to limit the types of geometries this │ │ │ │ + * layer supports. This should be set to something like │ │ │ │ + * "OpenLayers.Geometry.Point" to limit types. │ │ │ │ */ │ │ │ │ - click: function(evt) { │ │ │ │ - return this.handle(evt) ? !this.stopClick : true; │ │ │ │ - }, │ │ │ │ + geometryType: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: mousemove │ │ │ │ - * Handle mouse moves. Call the "over" callback if moving in to a feature, │ │ │ │ - * or the "out" callback if moving out of a feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} │ │ │ │ + /** │ │ │ │ + * Property: drawn │ │ │ │ + * {Boolean} Whether the Vector Layer features have been drawn yet. │ │ │ │ */ │ │ │ │ - mousemove: function(evt) { │ │ │ │ - if (!this.callbacks['over'] && !this.callbacks['out']) { │ │ │ │ - return true; │ │ │ │ - } │ │ │ │ - this.handle(evt); │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ + drawn: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: dblclick │ │ │ │ - * Handle dblclick. Call the "dblclick" callback if dblclick on a feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} │ │ │ │ + /** │ │ │ │ + * APIProperty: ratio │ │ │ │ + * {Float} This specifies the ratio of the size of the visiblity of the Vector Layer features to the size of the map. │ │ │ │ */ │ │ │ │ - dblclick: function(evt) { │ │ │ │ - return !this.handle(evt); │ │ │ │ - }, │ │ │ │ + ratio: 1, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: geometryTypeMatches │ │ │ │ - * Return true if the geometry type of the passed feature matches │ │ │ │ - * one of the geometry types in the geometryTypes array. │ │ │ │ + * Constructor: OpenLayers.Layer.Vector │ │ │ │ + * Create a new vector layer │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Vector.Feature>} │ │ │ │ + * name - {String} A name for the layer │ │ │ │ + * options - {Object} Optional object with non-default properties to set on │ │ │ │ + * the layer. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} │ │ │ │ + * {<OpenLayers.Layer.Vector>} A new vector layer │ │ │ │ */ │ │ │ │ - geometryTypeMatches: function(feature) { │ │ │ │ - return this.geometryTypes == null || │ │ │ │ - OpenLayers.Util.indexOf(this.geometryTypes, │ │ │ │ - feature.geometry.CLASS_NAME) > -1; │ │ │ │ - }, │ │ │ │ + initialize: function(name, options) { │ │ │ │ + OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: handle │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The event occurred over a relevant feature. │ │ │ │ - */ │ │ │ │ - handle: function(evt) { │ │ │ │ - if (this.feature && !this.feature.layer) { │ │ │ │ - // feature has been destroyed │ │ │ │ - this.feature = null; │ │ │ │ + // allow user-set renderer, otherwise assign one │ │ │ │ + if (!this.renderer || !this.renderer.supported()) { │ │ │ │ + this.assignRenderer(); │ │ │ │ } │ │ │ │ - var type = evt.type; │ │ │ │ - var handled = false; │ │ │ │ - var previouslyIn = !!(this.feature); // previously in a feature │ │ │ │ - var click = (type == "click" || type == "dblclick" || type == "touchstart"); │ │ │ │ - this.feature = this.layer.getFeatureFromEvent(evt); │ │ │ │ - if (this.feature && !this.feature.layer) { │ │ │ │ - // feature has been destroyed │ │ │ │ - this.feature = null; │ │ │ │ + │ │ │ │ + // if no valid renderer found, display error │ │ │ │ + if (!this.renderer || !this.renderer.supported()) { │ │ │ │ + this.renderer = null; │ │ │ │ + this.displayError(); │ │ │ │ } │ │ │ │ - if (this.lastFeature && !this.lastFeature.layer) { │ │ │ │ - // last feature has been destroyed │ │ │ │ - this.lastFeature = null; │ │ │ │ + │ │ │ │ + if (!this.styleMap) { │ │ │ │ + this.styleMap = new OpenLayers.StyleMap(); │ │ │ │ } │ │ │ │ - if (this.feature) { │ │ │ │ - if (type === "touchstart") { │ │ │ │ - // stop the event to prevent Android Webkit from │ │ │ │ - // "flashing" the map div │ │ │ │ - OpenLayers.Event.preventDefault(evt); │ │ │ │ - } │ │ │ │ - var inNew = (this.feature != this.lastFeature); │ │ │ │ - if (this.geometryTypeMatches(this.feature)) { │ │ │ │ - // in to a feature │ │ │ │ - if (previouslyIn && inNew) { │ │ │ │ - // out of last feature and in to another │ │ │ │ - if (this.lastFeature) { │ │ │ │ - this.triggerCallback(type, 'out', [this.lastFeature]); │ │ │ │ - } │ │ │ │ - this.triggerCallback(type, 'in', [this.feature]); │ │ │ │ - } else if (!previouslyIn || click) { │ │ │ │ - // in feature for the first time │ │ │ │ - this.triggerCallback(type, 'in', [this.feature]); │ │ │ │ - } │ │ │ │ - this.lastFeature = this.feature; │ │ │ │ - handled = true; │ │ │ │ - } else { │ │ │ │ - // not in to a feature │ │ │ │ - if (this.lastFeature && (previouslyIn && inNew || click)) { │ │ │ │ - // out of last feature for the first time │ │ │ │ - this.triggerCallback(type, 'out', [this.lastFeature]); │ │ │ │ - } │ │ │ │ - // next time the mouse goes in a feature whose geometry type │ │ │ │ - // doesn't match we don't want to call the 'out' callback │ │ │ │ - // again, so let's set this.feature to null so that │ │ │ │ - // previouslyIn will evaluate to false the next time │ │ │ │ - // we enter handle. Yes, a bit hackish... │ │ │ │ - this.feature = null; │ │ │ │ + │ │ │ │ + this.features = []; │ │ │ │ + this.selectedFeatures = []; │ │ │ │ + this.unrenderedFeatures = {}; │ │ │ │ + │ │ │ │ + // Allow for custom layer behavior │ │ │ │ + if (this.strategies) { │ │ │ │ + for (var i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ + this.strategies[i].setLayer(this); │ │ │ │ } │ │ │ │ - } else if (this.lastFeature && (previouslyIn || click)) { │ │ │ │ - this.triggerCallback(type, 'out', [this.lastFeature]); │ │ │ │ } │ │ │ │ - return handled; │ │ │ │ + │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: triggerCallback │ │ │ │ - * Call the callback keyed in the event map with the supplied arguments. │ │ │ │ - * For click and clickout, the <clickTolerance> is checked first. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * type - {String} │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Destroy this layer │ │ │ │ */ │ │ │ │ - triggerCallback: function(type, mode, args) { │ │ │ │ - var key = this.EVENTMAP[type][mode]; │ │ │ │ - if (key) { │ │ │ │ - if (type == 'click' && this.up && this.down) { │ │ │ │ - // for click/clickout, only trigger callback if tolerance is met │ │ │ │ - var dpx = Math.sqrt( │ │ │ │ - Math.pow(this.up.x - this.down.x, 2) + │ │ │ │ - Math.pow(this.up.y - this.down.y, 2) │ │ │ │ - ); │ │ │ │ - if (dpx <= this.clickTolerance) { │ │ │ │ - this.callback(key, args); │ │ │ │ + destroy: function() { │ │ │ │ + if (this.strategies) { │ │ │ │ + var strategy, i, len; │ │ │ │ + for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ + strategy = this.strategies[i]; │ │ │ │ + if (strategy.autoDestroy) { │ │ │ │ + strategy.destroy(); │ │ │ │ } │ │ │ │ - // we're done with this set of events now: clear the cached │ │ │ │ - // positions so we can't trip over them later (this can occur │ │ │ │ - // if one of the up/down events gets eaten before it gets to us │ │ │ │ - // but we still get the click) │ │ │ │ - this.up = this.down = null; │ │ │ │ - } else { │ │ │ │ - this.callback(key, args); │ │ │ │ } │ │ │ │ + this.strategies = null; │ │ │ │ + } │ │ │ │ + if (this.protocol) { │ │ │ │ + if (this.protocol.autoDestroy) { │ │ │ │ + this.protocol.destroy(); │ │ │ │ + } │ │ │ │ + this.protocol = null; │ │ │ │ } │ │ │ │ + this.destroyFeatures(); │ │ │ │ + this.features = null; │ │ │ │ + this.selectedFeatures = null; │ │ │ │ + this.unrenderedFeatures = null; │ │ │ │ + if (this.renderer) { │ │ │ │ + this.renderer.destroy(); │ │ │ │ + } │ │ │ │ + this.renderer = null; │ │ │ │ + this.geometryType = null; │ │ │ │ + this.drawn = null; │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: activate │ │ │ │ - * Turn on the handler. Returns false if the handler was already active. │ │ │ │ + * Method: clone │ │ │ │ + * Create a clone of this layer. │ │ │ │ + * │ │ │ │ + * Note: Features of the layer are also cloned. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} │ │ │ │ + * {<OpenLayers.Layer.Vector>} An exact clone of this layer │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.moveLayerToTop(); │ │ │ │ - this.map.events.on({ │ │ │ │ - "removelayer": this.handleMapEvents, │ │ │ │ - "changelayer": this.handleMapEvents, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - activated = true; │ │ │ │ + clone: function(obj) { │ │ │ │ + │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.Vector(this.name, this.getOptions()); │ │ │ │ } │ │ │ │ - return activated; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Turn off the handler. Returns false if the handler was already active. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.moveLayerBack(); │ │ │ │ - this.feature = null; │ │ │ │ - this.lastFeature = null; │ │ │ │ - this.down = null; │ │ │ │ - this.up = null; │ │ │ │ - this.map.events.un({ │ │ │ │ - "removelayer": this.handleMapEvents, │ │ │ │ - "changelayer": this.handleMapEvents, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - deactivated = true; │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + var features = this.features; │ │ │ │ + var len = features.length; │ │ │ │ + var clonedFeatures = new Array(len); │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ + clonedFeatures[i] = features[i].clone(); │ │ │ │ } │ │ │ │ - return deactivated; │ │ │ │ + obj.features = clonedFeatures; │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleMapEvents │ │ │ │ - * │ │ │ │ + * Method: refresh │ │ │ │ + * Ask the layer to request features again and redraw them. Triggers │ │ │ │ + * the refresh event if the layer is in range and visible. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} │ │ │ │ + * obj - {Object} Optional object with properties for any listener of │ │ │ │ + * the refresh event. │ │ │ │ */ │ │ │ │ - handleMapEvents: function(evt) { │ │ │ │ - if (evt.type == "removelayer" || evt.property == "order") { │ │ │ │ - this.moveLayerToTop(); │ │ │ │ + refresh: function(obj) { │ │ │ │ + if (this.calculateInRange() && this.visibility) { │ │ │ │ + this.events.triggerEvent("refresh", obj); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: moveLayerToTop │ │ │ │ - * Moves the layer for this handler to the top, so mouse events can reach │ │ │ │ - * it. │ │ │ │ + /** │ │ │ │ + * Method: assignRenderer │ │ │ │ + * Iterates through the available renderer implementations and selects │ │ │ │ + * and assigns the first one whose "supported()" function returns true. │ │ │ │ */ │ │ │ │ - moveLayerToTop: function() { │ │ │ │ - var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1, │ │ │ │ - this.layer.getZIndex()) + 1; │ │ │ │ - this.layer.setZIndex(index); │ │ │ │ - │ │ │ │ + assignRenderer: function() { │ │ │ │ + for (var i = 0, len = this.renderers.length; i < len; i++) { │ │ │ │ + var rendererClass = this.renderers[i]; │ │ │ │ + var renderer = (typeof rendererClass == "function") ? │ │ │ │ + rendererClass : │ │ │ │ + OpenLayers.Renderer[rendererClass]; │ │ │ │ + if (renderer && renderer.prototype.supported()) { │ │ │ │ + this.renderer = new renderer(this.div, this.rendererOptions); │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: moveLayerBack │ │ │ │ - * Moves the layer back to the position determined by the map's layers │ │ │ │ - * array. │ │ │ │ + /** │ │ │ │ + * Method: displayError │ │ │ │ + * Let the user know their browser isn't supported. │ │ │ │ */ │ │ │ │ - moveLayerBack: function() { │ │ │ │ - var index = this.layer.getZIndex() - 1; │ │ │ │ - if (index >= this.map.Z_INDEX_BASE['Feature']) { │ │ │ │ - this.layer.setZIndex(index); │ │ │ │ - } else { │ │ │ │ - this.map.setLayerZIndex(this.layer, │ │ │ │ - this.map.getLayerIndex(this.layer)); │ │ │ │ + displayError: function() { │ │ │ │ + if (this.reportError) { │ │ │ │ + OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", { │ │ │ │ + renderers: this.renderers.join('\n') │ │ │ │ + })); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Feature" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Layer/Vector/RootContainer.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Layer/Vector.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Layer.Vector.RootContainer │ │ │ │ - * A special layer type to combine multiple vector layers inside a single │ │ │ │ - * renderer root container. This class is not supposed to be instantiated │ │ │ │ - * from user space, it is a helper class for controls that require event │ │ │ │ - * processing for multiple vector layers. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Layer.Vector> │ │ │ │ - */ │ │ │ │ -OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: displayInLayerSwitcher │ │ │ │ - * Set to false for this layer type │ │ │ │ - */ │ │ │ │ - displayInLayerSwitcher: false, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: layers │ │ │ │ - * Layers that are attached to this container. Required config option. │ │ │ │ - */ │ │ │ │ - layers: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Layer.Vector.RootContainer │ │ │ │ - * Create a new root container for multiple vector layer. This constructor │ │ │ │ - * is not supposed to be used from user space, it is only to be used by │ │ │ │ - * controls that need feature selection across multiple vector layers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * name - {String} A name for the layer │ │ │ │ - * options - {Object} Optional object with non-default properties to set on │ │ │ │ - * the layer. │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * The layer has been added to the map. │ │ │ │ * │ │ │ │ - * Required options properties: │ │ │ │ - * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this │ │ │ │ - * container │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root │ │ │ │ - * container │ │ │ │ - */ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: display │ │ │ │ - */ │ │ │ │ - display: function() {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: getFeatureFromEvent │ │ │ │ - * walk through the layers to find the feature returned by the event │ │ │ │ + * If there is no renderer set, the layer can't be used. Remove it. │ │ │ │ + * Otherwise, give the renderer a reference to the map and set its size. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} event object with a feature property │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - getFeatureFromEvent: function(evt) { │ │ │ │ - var layers = this.layers; │ │ │ │ - var feature; │ │ │ │ - for (var i = 0; i < layers.length; i++) { │ │ │ │ - feature = layers[i].getFeatureFromEvent(evt); │ │ │ │ - if (feature) { │ │ │ │ - return feature; │ │ │ │ - } │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ + │ │ │ │ + if (!this.renderer) { │ │ │ │ + this.map.removeLayer(this); │ │ │ │ + } else { │ │ │ │ + this.renderer.map = this.map; │ │ │ │ + │ │ │ │ + var newSize = this.map.getSize(); │ │ │ │ + newSize.w = newSize.w * this.ratio; │ │ │ │ + newSize.h = newSize.h * this.ratio; │ │ │ │ + this.renderer.setSize(newSize); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setMap │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ + * Method: afterAdd │ │ │ │ + * Called at the end of the map.addLayer sequence. At this point, the map │ │ │ │ + * will have a base layer. Any autoActivate strategies will be │ │ │ │ + * activated here. │ │ │ │ */ │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); │ │ │ │ - this.collectRoots(); │ │ │ │ - map.events.register("changelayer", this, this.handleChangeLayer); │ │ │ │ + afterAdd: function() { │ │ │ │ + if (this.strategies) { │ │ │ │ + var strategy, i, len; │ │ │ │ + for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ + strategy = this.strategies[i]; │ │ │ │ + if (strategy.autoActivate) { │ │ │ │ + strategy.activate(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ * Method: removeMap │ │ │ │ - * │ │ │ │ + * The layer has been removed from the map. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ removeMap: function(map) { │ │ │ │ - map.events.unregister("changelayer", this, this.handleChangeLayer); │ │ │ │ - this.resetRoots(); │ │ │ │ - OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: collectRoots │ │ │ │ - * Collects the root nodes of all layers this control is configured with │ │ │ │ - * and moveswien the nodes to this control's layer │ │ │ │ - */ │ │ │ │ - collectRoots: function() { │ │ │ │ - var layer; │ │ │ │ - // walk through all map layers, because we want to keep the order │ │ │ │ - for (var i = 0; i < this.map.layers.length; ++i) { │ │ │ │ - layer = this.map.layers[i]; │ │ │ │ - if (OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ - layer.renderer.moveRoot(this.renderer); │ │ │ │ + this.drawn = false; │ │ │ │ + if (this.strategies) { │ │ │ │ + var strategy, i, len; │ │ │ │ + for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ + strategy = this.strategies[i]; │ │ │ │ + if (strategy.autoActivate) { │ │ │ │ + strategy.deactivate(); │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: resetRoots │ │ │ │ - * Resets the root nodes back into the layers they belong to. │ │ │ │ + * Method: onMapResize │ │ │ │ + * Notify the renderer of the change in size. │ │ │ │ + * │ │ │ │ */ │ │ │ │ - resetRoots: function() { │ │ │ │ - var layer; │ │ │ │ - for (var i = 0; i < this.layers.length; ++i) { │ │ │ │ - layer = this.layers[i]; │ │ │ │ - if (this.renderer && layer.renderer.getRenderLayerId() == this.id) { │ │ │ │ - this.renderer.moveRoot(layer.renderer); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + onMapResize: function() { │ │ │ │ + OpenLayers.Layer.prototype.onMapResize.apply(this, arguments); │ │ │ │ + │ │ │ │ + var newSize = this.map.getSize(); │ │ │ │ + newSize.w = newSize.w * this.ratio; │ │ │ │ + newSize.h = newSize.h * this.ratio; │ │ │ │ + this.renderer.setSize(newSize); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleChangeLayer │ │ │ │ - * Event handler for the map's changelayer event. We need to rebuild │ │ │ │ - * this container's layer dom if order of one of its layers changes. │ │ │ │ - * This handler is added with the setMap method, and removed with the │ │ │ │ - * removeMap method. │ │ │ │ + * Method: moveTo │ │ │ │ + * Reset the vector layer's div so that it once again is lined up with │ │ │ │ + * the map. Notify the renderer of the change of extent, and in the │ │ │ │ + * case of a change of zoom level (resolution), have the │ │ │ │ + * renderer redraw features. │ │ │ │ + * │ │ │ │ + * If the layer has not yet been drawn, cycle through the layer's │ │ │ │ + * features and draw each one. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * zoomChanged - {Boolean} │ │ │ │ + * dragging - {Boolean} │ │ │ │ */ │ │ │ │ - handleChangeLayer: function(evt) { │ │ │ │ - var layer = evt.layer; │ │ │ │ - if (evt.property == "order" && │ │ │ │ - OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ - this.resetRoots(); │ │ │ │ - this.collectRoots(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Control/SelectFeature.js │ │ │ │ - ====================================================================== */ │ │ │ │ + var coordSysUnchanged = true; │ │ │ │ + if (!dragging) { │ │ │ │ + this.renderer.root.style.visibility = 'hidden'; │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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 viewSize = this.map.getSize(), │ │ │ │ + viewWidth = viewSize.w, │ │ │ │ + viewHeight = viewSize.h, │ │ │ │ + offsetLeft = (viewWidth / 2 * this.ratio) - viewWidth / 2, │ │ │ │ + offsetTop = (viewHeight / 2 * this.ratio) - viewHeight / 2; │ │ │ │ + offsetLeft += this.map.layerContainerOriginPx.x; │ │ │ │ + offsetLeft = -Math.round(offsetLeft); │ │ │ │ + offsetTop += this.map.layerContainerOriginPx.y; │ │ │ │ + offsetTop = -Math.round(offsetTop); │ │ │ │ │ │ │ │ + this.div.style.left = offsetLeft + 'px'; │ │ │ │ + this.div.style.top = offsetTop + 'px'; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Control.js │ │ │ │ - * @requires OpenLayers/Feature/Vector.js │ │ │ │ - * @requires OpenLayers/Handler/Feature.js │ │ │ │ - * @requires OpenLayers/Layer/Vector/RootContainer.js │ │ │ │ - */ │ │ │ │ + var extent = this.map.getExtent().scale(this.ratio); │ │ │ │ + coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Control.SelectFeature │ │ │ │ - * The SelectFeature control selects vector features from a given layer on │ │ │ │ - * click or hover. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Control> │ │ │ │ - */ │ │ │ │ -OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + this.renderer.root.style.visibility = 'visible'; │ │ │ │ + │ │ │ │ + // Force a reflow on gecko based browsers to prevent jump/flicker. │ │ │ │ + // This seems to happen on only certain configurations; it was originally │ │ │ │ + // noticed in FF 2.0 and Linux. │ │ │ │ + if (OpenLayers.IS_GECKO === true) { │ │ │ │ + this.div.scrollLeft = this.div.scrollLeft; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (!zoomChanged && coordSysUnchanged) { │ │ │ │ + for (var i in this.unrenderedFeatures) { │ │ │ │ + var feature = this.unrenderedFeatures[i]; │ │ │ │ + this.drawFeature(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (!this.drawn || zoomChanged || !coordSysUnchanged) { │ │ │ │ + this.drawn = true; │ │ │ │ + var feature; │ │ │ │ + for (var i = 0, len = this.features.length; i < len; i++) { │ │ │ │ + this.renderer.locked = (i !== (len - 1)); │ │ │ │ + feature = this.features[i]; │ │ │ │ + this.drawFeature(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ - * control specific events. │ │ │ │ - * │ │ │ │ - * Register a listener for a particular event with the following syntax: │ │ │ │ - * (code) │ │ │ │ - * control.events.register(type, obj, listener); │ │ │ │ - * (end) │ │ │ │ - * │ │ │ │ - * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ - * beforefeaturehighlighted - Triggered before a feature is highlighted │ │ │ │ - * featurehighlighted - Triggered when a feature is highlighted │ │ │ │ - * featureunhighlighted - Triggered when a feature is unhighlighted │ │ │ │ - * boxselectionstart - Triggered before box selection starts │ │ │ │ - * boxselectionend - Triggered after box selection ends │ │ │ │ + * APIMethod: display │ │ │ │ + * Hide or show the Layer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * display - {Boolean} │ │ │ │ */ │ │ │ │ + display: function(display) { │ │ │ │ + OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ + // we need to set the display style of the root in case it is attached │ │ │ │ + // to a foreign layer │ │ │ │ + var currentDisplay = this.div.style.display; │ │ │ │ + if (currentDisplay != this.renderer.root.style.display) { │ │ │ │ + this.renderer.root.style.display = currentDisplay; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: multipleKey │ │ │ │ - * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets │ │ │ │ - * the <multiple> property to true. Default is null. │ │ │ │ + * APIMethod: addFeatures │ │ │ │ + * Add Features to the layer. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - multipleKey: null, │ │ │ │ + addFeatures: function(features, options) { │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ + features = [features]; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: toggleKey │ │ │ │ - * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets │ │ │ │ - * the <toggle> property to true. Default is null. │ │ │ │ - */ │ │ │ │ - toggleKey: null, │ │ │ │ + var notify = !options || !options.silent; │ │ │ │ + if (notify) { │ │ │ │ + var event = { │ │ │ │ + features: features │ │ │ │ + }; │ │ │ │ + var ret = this.events.triggerEvent("beforefeaturesadded", event); │ │ │ │ + if (ret === false) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + features = event.features; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: multiple │ │ │ │ - * {Boolean} Allow selection of multiple geometries. Default is false. │ │ │ │ - */ │ │ │ │ - multiple: false, │ │ │ │ + // Track successfully added features for featuresadded event, since │ │ │ │ + // beforefeatureadded can veto single features. │ │ │ │ + var featuresAdded = []; │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ + if (i != (features.length - 1)) { │ │ │ │ + this.renderer.locked = true; │ │ │ │ + } else { │ │ │ │ + this.renderer.locked = false; │ │ │ │ + } │ │ │ │ + var feature = features[i]; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: clickout │ │ │ │ - * {Boolean} Unselect features when clicking outside any feature. │ │ │ │ - * Default is true. │ │ │ │ - */ │ │ │ │ - clickout: true, │ │ │ │ + if (this.geometryType && │ │ │ │ + !(feature.geometry instanceof this.geometryType)) { │ │ │ │ + throw new TypeError('addFeatures: component should be an ' + │ │ │ │ + this.geometryType.prototype.CLASS_NAME); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: toggle │ │ │ │ - * {Boolean} Unselect a selected feature on click. Default is false. Only │ │ │ │ - * has meaning if hover is false. │ │ │ │ - */ │ │ │ │ - toggle: false, │ │ │ │ + //give feature reference to its layer │ │ │ │ + feature.layer = this; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: hover │ │ │ │ - * {Boolean} Select on mouse over and deselect on mouse out. If true, this │ │ │ │ - * ignores clicks and only listens to mouse moves. │ │ │ │ - */ │ │ │ │ - hover: false, │ │ │ │ + if (!feature.style && this.style) { │ │ │ │ + feature.style = OpenLayers.Util.extend({}, this.style); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: highlightOnly │ │ │ │ - * {Boolean} If true do not actually select features (that is place them in │ │ │ │ - * the layer's selected features array), just highlight them. This property │ │ │ │ - * has no effect if hover is false. Defaults to false. │ │ │ │ - */ │ │ │ │ - highlightOnly: false, │ │ │ │ + if (notify) { │ │ │ │ + if (this.events.triggerEvent("beforefeatureadded", { │ │ │ │ + feature: feature │ │ │ │ + }) === false) { │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ + this.preFeatureInsert(feature); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: box │ │ │ │ - * {Boolean} Allow feature selection by drawing a box. │ │ │ │ - */ │ │ │ │ - box: false, │ │ │ │ + featuresAdded.push(feature); │ │ │ │ + this.features.push(feature); │ │ │ │ + this.drawFeature(feature); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: onBeforeSelect │ │ │ │ - * {Function} Optional function to be called before a feature is selected. │ │ │ │ - * The function should expect to be called with a feature. │ │ │ │ - */ │ │ │ │ - onBeforeSelect: function() {}, │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featureadded", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + this.onFeatureInsert(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: onSelect │ │ │ │ - * {Function} Optional function to be called when a feature is selected. │ │ │ │ - * The function should expect to be called with a feature. │ │ │ │ - */ │ │ │ │ - onSelect: function() {}, │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featuresadded", { │ │ │ │ + features: featuresAdded │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: onUnselect │ │ │ │ - * {Function} Optional function to be called when a feature is unselected. │ │ │ │ - * The function should expect to be called with a feature. │ │ │ │ - */ │ │ │ │ - onUnselect: function() {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: scope │ │ │ │ - * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect │ │ │ │ - * callbacks. If null the scope will be this control. │ │ │ │ + * APIMethod: removeFeatures │ │ │ │ + * Remove features from the layer. This erases any drawn features and │ │ │ │ + * removes them from the layer's control. The beforefeatureremoved │ │ │ │ + * and featureremoved events will be triggered for each feature. The │ │ │ │ + * featuresremoved event will be triggered after all features have │ │ │ │ + * been removed. To supress event triggering, use the silent option. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} List of features to be │ │ │ │ + * removed. │ │ │ │ + * options - {Object} Optional properties for changing behavior of the │ │ │ │ + * removal. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * silent - {Boolean} Supress event triggering. Default is false. │ │ │ │ */ │ │ │ │ - scope: null, │ │ │ │ + removeFeatures: function(features, options) { │ │ │ │ + if (!features || features.length === 0) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (features === this.features) { │ │ │ │ + return this.removeAllFeatures(options); │ │ │ │ + } │ │ │ │ + if (!(OpenLayers.Util.isArray(features))) { │ │ │ │ + features = [features]; │ │ │ │ + } │ │ │ │ + if (features === this.selectedFeatures) { │ │ │ │ + features = features.slice(); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: geometryTypes │ │ │ │ - * {Array(String)} To restrict selecting to a limited set of geometry types, │ │ │ │ - * send a list of strings corresponding to the geometry class names. │ │ │ │ - */ │ │ │ │ - geometryTypes: null, │ │ │ │ + var notify = !options || !options.silent; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: layer │ │ │ │ - * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer │ │ │ │ - * root for all layers this control is configured with (if an array of │ │ │ │ - * layers was passed to the constructor), or the vector layer the control │ │ │ │ - * was configured with (if a single layer was passed to the constructor). │ │ │ │ - */ │ │ │ │ - layer: null, │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent( │ │ │ │ + "beforefeaturesremoved", { │ │ │ │ + features: features │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: layers │ │ │ │ - * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on, │ │ │ │ - * or null if the control was configured with a single layer │ │ │ │ - */ │ │ │ │ - layers: null, │ │ │ │ + for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ + // We remain locked so long as we're not at 0 │ │ │ │ + // and the 'next' feature has a geometry. We do the geometry check │ │ │ │ + // because if all the features after the current one are 'null', we │ │ │ │ + // won't call eraseGeometry, so we break the 'renderer functions │ │ │ │ + // will always be called with locked=false *last*' rule. The end result │ │ │ │ + // is a possible gratiutious unlocking to save a loop through the rest │ │ │ │ + // of the list checking the remaining features every time. So long as │ │ │ │ + // null geoms are rare, this is probably okay. │ │ │ │ + if (i != 0 && features[i - 1].geometry) { │ │ │ │ + this.renderer.locked = true; │ │ │ │ + } else { │ │ │ │ + this.renderer.locked = false; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: callbacks │ │ │ │ - * {Object} The functions that are sent to the handlers.feature for callback │ │ │ │ - */ │ │ │ │ - callbacks: null, │ │ │ │ + var feature = features[i]; │ │ │ │ + delete this.unrenderedFeatures[feature.id]; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: selectStyle │ │ │ │ - * {Object} Hash of styles │ │ │ │ - */ │ │ │ │ - selectStyle: null, │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: renderIntent │ │ │ │ - * {String} key used to retrieve the select style from the layer's │ │ │ │ - * style map. │ │ │ │ - */ │ │ │ │ - renderIntent: "select", │ │ │ │ + this.features = OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ + // feature has no layer at this point │ │ │ │ + feature.layer = null; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: handlers │ │ │ │ - * {Object} Object with references to multiple <OpenLayers.Handler> │ │ │ │ - * instances. │ │ │ │ - */ │ │ │ │ - handlers: null, │ │ │ │ + if (feature.geometry) { │ │ │ │ + this.renderer.eraseFeatures(feature); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Control.SelectFeature │ │ │ │ - * Create a new control for selecting features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The │ │ │ │ - * layer(s) this control will select features from. │ │ │ │ - * options - {Object} │ │ │ │ - */ │ │ │ │ - initialize: function(layers, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + //in the case that this feature is one of the selected features, │ │ │ │ + // remove it from that array as well. │ │ │ │ + if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) { │ │ │ │ + OpenLayers.Util.removeItem(this.selectedFeatures, feature); │ │ │ │ + } │ │ │ │ │ │ │ │ - if (this.scope === null) { │ │ │ │ - this.scope = this; │ │ │ │ - } │ │ │ │ - this.initLayer(layers); │ │ │ │ - var callbacks = { │ │ │ │ - click: this.clickFeature, │ │ │ │ - clickout: this.clickoutFeature │ │ │ │ - }; │ │ │ │ - if (this.hover) { │ │ │ │ - callbacks.over = this.overFeature; │ │ │ │ - callbacks.out = this.outFeature; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featureremoved", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ } │ │ │ │ │ │ │ │ - this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); │ │ │ │ - this.handlers = { │ │ │ │ - feature: new OpenLayers.Handler.Feature( │ │ │ │ - this, this.layer, this.callbacks, { │ │ │ │ - geometryTypes: this.geometryTypes │ │ │ │ - } │ │ │ │ - ) │ │ │ │ - }; │ │ │ │ - │ │ │ │ - if (this.box) { │ │ │ │ - this.handlers.box = new OpenLayers.Handler.Box( │ │ │ │ - this, { │ │ │ │ - done: this.selectBox │ │ │ │ - }, { │ │ │ │ - boxDivClassName: "olHandlerBoxSelectFeature" │ │ │ │ - } │ │ │ │ - ); │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featuresremoved", { │ │ │ │ + features: features │ │ │ │ + }); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: initLayer │ │ │ │ - * Assign the layer property. If layers is an array, we need to use │ │ │ │ - * a RootContainer. │ │ │ │ + /** │ │ │ │ + * APIMethod: removeAllFeatures │ │ │ │ + * Remove all features from the layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. │ │ │ │ + * options - {Object} Optional properties for changing behavior of the │ │ │ │ + * removal. │ │ │ │ + * │ │ │ │ + * Valid options: │ │ │ │ + * silent - {Boolean} Supress event triggering. Default is false. │ │ │ │ */ │ │ │ │ - initLayer: function(layers) { │ │ │ │ - if (OpenLayers.Util.isArray(layers)) { │ │ │ │ - this.layers = layers; │ │ │ │ - this.layer = new OpenLayers.Layer.Vector.RootContainer( │ │ │ │ - this.id + "_container", { │ │ │ │ - layers: layers │ │ │ │ + removeAllFeatures: function(options) { │ │ │ │ + var notify = !options || !options.silent; │ │ │ │ + var features = this.features; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent( │ │ │ │ + "beforefeaturesremoved", { │ │ │ │ + features: features │ │ │ │ } │ │ │ │ ); │ │ │ │ - } else { │ │ │ │ - this.layer = layers; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - if (this.active && this.layers) { │ │ │ │ - this.map.removeLayer(this.layer); │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - if (this.layers) { │ │ │ │ - this.layer.destroy(); │ │ │ │ } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: activate │ │ │ │ - * Activates the control. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The control was effectively activated. │ │ │ │ - */ │ │ │ │ - activate: function() { │ │ │ │ - if (!this.active) { │ │ │ │ - if (this.layers) { │ │ │ │ - this.map.addLayer(this.layer); │ │ │ │ + var feature; │ │ │ │ + for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ + feature = features[i]; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ } │ │ │ │ - this.handlers.feature.activate(); │ │ │ │ - if (this.box && this.handlers.box) { │ │ │ │ - this.handlers.box.activate(); │ │ │ │ + feature.layer = null; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featureremoved", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ } │ │ │ │ } │ │ │ │ - return OpenLayers.Control.prototype.activate.apply( │ │ │ │ - this, arguments │ │ │ │ - ); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Deactivates the control. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The control was effectively deactivated. │ │ │ │ - */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.handlers.feature.deactivate(); │ │ │ │ - if (this.handlers.box) { │ │ │ │ - this.handlers.box.deactivate(); │ │ │ │ - } │ │ │ │ - if (this.layers) { │ │ │ │ - this.map.removeLayer(this.layer); │ │ │ │ - } │ │ │ │ + this.renderer.clear(); │ │ │ │ + this.features = []; │ │ │ │ + this.unrenderedFeatures = {}; │ │ │ │ + this.selectedFeatures = []; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featuresremoved", { │ │ │ │ + features: features │ │ │ │ + }); │ │ │ │ } │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply( │ │ │ │ - this, arguments │ │ │ │ - ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: unselectAll │ │ │ │ - * Unselect all selected features. To unselect all except for a single │ │ │ │ - * feature, set the options.except property to the feature. │ │ │ │ + * APIMethod: destroyFeatures │ │ │ │ + * Erase and destroy features on the layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional configuration object. │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} An optional array of │ │ │ │ + * features to destroy. If not supplied, all features on the layer │ │ │ │ + * will be destroyed. │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - unselectAll: function(options) { │ │ │ │ - // we'll want an option to supress notification here │ │ │ │ - var layers = this.layers || [this.layer], │ │ │ │ - layer, feature, l, numExcept; │ │ │ │ - for (l = 0; l < layers.length; ++l) { │ │ │ │ - layer = layers[l]; │ │ │ │ - numExcept = 0; │ │ │ │ - //layer.selectedFeatures is null when layer is destroyed and │ │ │ │ - //one of it's preremovelayer listener calls setLayer │ │ │ │ - //with another layer on this control │ │ │ │ - if (layer.selectedFeatures != null) { │ │ │ │ - while (layer.selectedFeatures.length > numExcept) { │ │ │ │ - feature = layer.selectedFeatures[numExcept]; │ │ │ │ - if (!options || options.except != feature) { │ │ │ │ - this.unselect(feature); │ │ │ │ - } else { │ │ │ │ - ++numExcept; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + destroyFeatures: function(features, options) { │ │ │ │ + var all = (features == undefined); // evaluates to true if │ │ │ │ + // features is null │ │ │ │ + if (all) { │ │ │ │ + features = this.features; │ │ │ │ + } │ │ │ │ + if (features) { │ │ │ │ + this.removeFeatures(features, options); │ │ │ │ + for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ + features[i].destroy(); │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clickFeature │ │ │ │ - * Called on click in a feature │ │ │ │ - * Only responds if this.hover is false. │ │ │ │ + * APIMethod: drawFeature │ │ │ │ + * Draw (or redraw) a feature on the layer. If the optional style argument │ │ │ │ + * is included, this style will be used. If no style is included, the │ │ │ │ + * feature's style will be used. If the feature doesn't have a style, │ │ │ │ + * the layer's style will be used. │ │ │ │ + * │ │ │ │ + * This function is not designed to be used when adding features to │ │ │ │ + * the layer (use addFeatures instead). It is meant to be used when │ │ │ │ + * the style of a feature has changed, or in some other way needs to │ │ │ │ + * visually updated *after* it has already been added to a layer. You │ │ │ │ + * must add the feature to the layer for most layer-related events to │ │ │ │ + * happen. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ + * Parameters: │ │ │ │ * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * style - {String | Object} Named render intent or full symbolizer object. │ │ │ │ */ │ │ │ │ - clickFeature: function(feature) { │ │ │ │ - if (!this.hover) { │ │ │ │ - var selected = (OpenLayers.Util.indexOf( │ │ │ │ - feature.layer.selectedFeatures, feature) > -1); │ │ │ │ - if (selected) { │ │ │ │ - if (this.toggleSelect()) { │ │ │ │ - this.unselect(feature); │ │ │ │ - } else if (!this.multipleSelect()) { │ │ │ │ - this.unselectAll({ │ │ │ │ - except: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - if (!this.multipleSelect()) { │ │ │ │ - this.unselectAll({ │ │ │ │ - except: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - this.select(feature); │ │ │ │ + drawFeature: function(feature, style) { │ │ │ │ + // don't try to draw the feature with the renderer if the layer is not │ │ │ │ + // drawn itself │ │ │ │ + if (!this.drawn) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (typeof style != "object") { │ │ │ │ + if (!style && feature.state === OpenLayers.State.DELETE) { │ │ │ │ + style = "delete"; │ │ │ │ + } │ │ │ │ + var renderIntent = style || feature.renderIntent; │ │ │ │ + style = feature.style || this.style; │ │ │ │ + if (!style) { │ │ │ │ + style = this.styleMap.createSymbolizer(feature, renderIntent); │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: multipleSelect │ │ │ │ - * Allow for multiple selected features based on <multiple> property and │ │ │ │ - * <multipleKey> event modifier. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Allow for multiple selected features. │ │ │ │ - */ │ │ │ │ - multipleSelect: function() { │ │ │ │ - return this.multiple || (this.handlers.feature.evt && │ │ │ │ - this.handlers.feature.evt[this.multipleKey]); │ │ │ │ + var drawn = this.renderer.drawFeature(feature, style); │ │ │ │ + //TODO remove the check for null when we get rid of Renderer.SVG │ │ │ │ + if (drawn === false || drawn === null) { │ │ │ │ + this.unrenderedFeatures[feature.id] = feature; │ │ │ │ + } else { │ │ │ │ + delete this.unrenderedFeatures[feature.id]; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: toggleSelect │ │ │ │ - * Event should toggle the selected state of a feature based on <toggle> │ │ │ │ - * property and <toggleKey> event modifier. │ │ │ │ + * Method: eraseFeatures │ │ │ │ + * Erase features from the layer. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Toggle the selected state of a feature. │ │ │ │ + * Parameters: │ │ │ │ + * features - {Array(<OpenLayers.Feature.Vector>)} │ │ │ │ */ │ │ │ │ - toggleSelect: function() { │ │ │ │ - return this.toggle || (this.handlers.feature.evt && │ │ │ │ - this.handlers.feature.evt[this.toggleKey]); │ │ │ │ + eraseFeatures: function(features) { │ │ │ │ + this.renderer.eraseFeatures(features); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clickoutFeature │ │ │ │ - * Called on click outside a previously clicked (selected) feature. │ │ │ │ - * Only responds if this.hover is false. │ │ │ │ + * Method: getFeatureFromEvent │ │ │ │ + * Given an event, return a feature if the event occurred over one. │ │ │ │ + * Otherwise, return null. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Vector.Feature>} │ │ │ │ + * evt - {Event} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Feature.Vector>} A feature if one was under the event. │ │ │ │ */ │ │ │ │ - clickoutFeature: function(feature) { │ │ │ │ - if (!this.hover && this.clickout) { │ │ │ │ - this.unselectAll(); │ │ │ │ + getFeatureFromEvent: function(evt) { │ │ │ │ + if (!this.renderer) { │ │ │ │ + throw new Error('getFeatureFromEvent called on layer with no ' + │ │ │ │ + 'renderer. This usually means you destroyed a ' + │ │ │ │ + 'layer, but not some handler which is associated ' + │ │ │ │ + 'with it.'); │ │ │ │ + } │ │ │ │ + var feature = null; │ │ │ │ + var featureId = this.renderer.getFeatureIdFromEvent(evt); │ │ │ │ + if (featureId) { │ │ │ │ + if (typeof featureId === "string") { │ │ │ │ + feature = this.getFeatureById(featureId); │ │ │ │ + } else { │ │ │ │ + feature = featureId; │ │ │ │ + } │ │ │ │ } │ │ │ │ + return feature; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: overFeature │ │ │ │ - * Called on over a feature. │ │ │ │ - * Only responds if this.hover is true. │ │ │ │ + * APIMethod: getFeatureBy │ │ │ │ + * Given a property value, return the feature if it exists in the features array │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * property - {String} │ │ │ │ + * value - {String} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Feature.Vector>} A feature corresponding to the given │ │ │ │ + * property value or null if there is no such feature. │ │ │ │ */ │ │ │ │ - overFeature: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - if (this.hover) { │ │ │ │ - if (this.highlightOnly) { │ │ │ │ - this.highlight(feature); │ │ │ │ - } else if (OpenLayers.Util.indexOf( │ │ │ │ - layer.selectedFeatures, feature) == -1) { │ │ │ │ - this.select(feature); │ │ │ │ + getFeatureBy: function(property, value) { │ │ │ │ + //TBD - would it be more efficient to use a hash for this.features? │ │ │ │ + var feature = null; │ │ │ │ + for (var i = 0, len = this.features.length; i < len; ++i) { │ │ │ │ + if (this.features[i][property] == value) { │ │ │ │ + feature = this.features[i]; │ │ │ │ + break; │ │ │ │ } │ │ │ │ } │ │ │ │ + return feature; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: outFeature │ │ │ │ - * Called on out of a selected feature. │ │ │ │ - * Only responds if this.hover is true. │ │ │ │ + * APIMethod: getFeatureById │ │ │ │ + * Given a feature id, return the feature if it exists in the features array │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * featureId - {String} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Feature.Vector>} A feature corresponding to the given │ │ │ │ + * featureId or null if there is no such feature. │ │ │ │ */ │ │ │ │ - outFeature: function(feature) { │ │ │ │ - if (this.hover) { │ │ │ │ - if (this.highlightOnly) { │ │ │ │ - // we do nothing if we're not the last highlighter of the │ │ │ │ - // feature │ │ │ │ - if (feature._lastHighlighter == this.id) { │ │ │ │ - // if another select control had highlighted the feature before │ │ │ │ - // we did it ourself then we use that control to highlight the │ │ │ │ - // feature as it was before we highlighted it, else we just │ │ │ │ - // unhighlight it │ │ │ │ - if (feature._prevHighlighter && │ │ │ │ - feature._prevHighlighter != this.id) { │ │ │ │ - delete feature._lastHighlighter; │ │ │ │ - var control = this.map.getControl( │ │ │ │ - feature._prevHighlighter); │ │ │ │ - if (control) { │ │ │ │ - control.highlight(feature); │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.unhighlight(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.unselect(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + getFeatureById: function(featureId) { │ │ │ │ + return this.getFeatureBy('id', featureId); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: highlight │ │ │ │ - * Redraw feature with the select style. │ │ │ │ + * APIMethod: getFeatureByFid │ │ │ │ + * Given a feature fid, return the feature if it exists in the features array │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * featureFid - {String} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Feature.Vector>} A feature corresponding to the given │ │ │ │ + * featureFid or null if there is no such feature. │ │ │ │ */ │ │ │ │ - highlight: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - var cont = this.events.triggerEvent("beforefeaturehighlighted", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (cont !== false) { │ │ │ │ - feature._prevHighlighter = feature._lastHighlighter; │ │ │ │ - feature._lastHighlighter = this.id; │ │ │ │ - var style = this.selectStyle || this.renderIntent; │ │ │ │ - layer.drawFeature(feature, style); │ │ │ │ - this.events.triggerEvent("featurehighlighted", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - } │ │ │ │ + getFeatureByFid: function(featureFid) { │ │ │ │ + return this.getFeatureBy('fid', featureFid); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: unhighlight │ │ │ │ - * Redraw feature with the "default" style │ │ │ │ + * APIMethod: getFeaturesByAttribute │ │ │ │ + * Returns an array of features that have the given attribute key set to the │ │ │ │ + * given value. Comparison of attribute values takes care of datatypes, e.g. │ │ │ │ + * the string '1234' is not equal to the number 1234. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * attrName - {String} │ │ │ │ + * attrValue - {Mixed} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * Array({<OpenLayers.Feature.Vector>}) An array of features that have the │ │ │ │ + * passed named attribute set to the given value. │ │ │ │ */ │ │ │ │ - unhighlight: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - // three cases: │ │ │ │ - // 1. there's no other highlighter, in that case _prev is undefined, │ │ │ │ - // and we just need to undef _last │ │ │ │ - // 2. another control highlighted the feature after we did it, in │ │ │ │ - // that case _last references this other control, and we just │ │ │ │ - // need to undef _prev │ │ │ │ - // 3. another control highlighted the feature before we did it, in │ │ │ │ - // that case _prev references this other control, and we need to │ │ │ │ - // set _last to _prev and undef _prev │ │ │ │ - if (feature._prevHighlighter == undefined) { │ │ │ │ - delete feature._lastHighlighter; │ │ │ │ - } else if (feature._prevHighlighter == this.id) { │ │ │ │ - delete feature._prevHighlighter; │ │ │ │ - } else { │ │ │ │ - feature._lastHighlighter = feature._prevHighlighter; │ │ │ │ - delete feature._prevHighlighter; │ │ │ │ + getFeaturesByAttribute: function(attrName, attrValue) { │ │ │ │ + var i, │ │ │ │ + feature, │ │ │ │ + len = this.features.length, │ │ │ │ + foundFeatures = []; │ │ │ │ + for (i = 0; i < len; i++) { │ │ │ │ + feature = this.features[i]; │ │ │ │ + if (feature && feature.attributes) { │ │ │ │ + if (feature.attributes[attrName] === attrValue) { │ │ │ │ + foundFeatures.push(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - layer.drawFeature(feature, feature.style || feature.layer.style || │ │ │ │ - "default"); │ │ │ │ - this.events.triggerEvent("featureunhighlighted", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ + return foundFeatures; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: select │ │ │ │ - * Add feature to the layer's selectedFeature array, render the feature as │ │ │ │ - * selected, and call the onSelect function. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - */ │ │ │ │ - select: function(feature) { │ │ │ │ - var cont = this.onBeforeSelect.call(this.scope, feature); │ │ │ │ - var layer = feature.layer; │ │ │ │ - if (cont !== false) { │ │ │ │ - cont = layer.events.triggerEvent("beforefeatureselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (cont !== false) { │ │ │ │ - layer.selectedFeatures.push(feature); │ │ │ │ - this.highlight(feature); │ │ │ │ - // if the feature handler isn't involved in the feature │ │ │ │ - // selection (because the box handler is used or the │ │ │ │ - // feature is selected programatically) we fake the │ │ │ │ - // feature handler to allow unselecting on click │ │ │ │ - if (!this.handlers.feature.lastFeature) { │ │ │ │ - this.handlers.feature.lastFeature = layer.selectedFeatures[0]; │ │ │ │ - } │ │ │ │ - layer.events.triggerEvent("featureselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.onSelect.call(this.scope, feature); │ │ │ │ - } │ │ │ │ + * Unselect the selected features │ │ │ │ + * i.e. clears the featureSelection array │ │ │ │ + * change the style back │ │ │ │ + clearSelection: function() { │ │ │ │ + │ │ │ │ + var vectorLayer = this.map.vectorLayer; │ │ │ │ + for (var i = 0; i < this.map.featureSelection.length; i++) { │ │ │ │ + var featureSelection = this.map.featureSelection[i]; │ │ │ │ + vectorLayer.drawFeature(featureSelection, vectorLayer.style); │ │ │ │ } │ │ │ │ + this.map.featureSelection = []; │ │ │ │ }, │ │ │ │ + */ │ │ │ │ + │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: unselect │ │ │ │ - * Remove feature from the layer's selectedFeature array, render the feature as │ │ │ │ - * normal, and call the onUnselect function. │ │ │ │ + * APIMethod: onFeatureInsert │ │ │ │ + * method called after a feature is inserted. │ │ │ │ + * Does nothing by default. Override this if you │ │ │ │ + * need to do something on feature updates. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - unselect: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - // Store feature style for restoration later │ │ │ │ - this.unhighlight(feature); │ │ │ │ - OpenLayers.Util.removeItem(layer.selectedFeatures, feature); │ │ │ │ - layer.events.triggerEvent("featureunselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.onUnselect.call(this.scope, feature); │ │ │ │ - }, │ │ │ │ + onFeatureInsert: function(feature) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: selectBox │ │ │ │ - * Callback from the handlers.box set up when <box> selection is true │ │ │ │ - * on. │ │ │ │ + * APIMethod: preFeatureInsert │ │ │ │ + * method called before a feature is inserted. │ │ │ │ + * Does nothing by default. Override this if you │ │ │ │ + * need to do something when features are first added to the │ │ │ │ + * layer, but before they are drawn, such as adjust the style. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> } │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - selectBox: function(position) { │ │ │ │ - if (position instanceof OpenLayers.Bounds) { │ │ │ │ - var minXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.left, │ │ │ │ - y: position.bottom │ │ │ │ - }); │ │ │ │ - var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.right, │ │ │ │ - y: position.top │ │ │ │ - }); │ │ │ │ - var bounds = new OpenLayers.Bounds( │ │ │ │ - minXY.lon, minXY.lat, maxXY.lon, maxXY.lat │ │ │ │ - ); │ │ │ │ - │ │ │ │ - // if multiple is false, first deselect currently selected features │ │ │ │ - if (!this.multipleSelect()) { │ │ │ │ - this.unselectAll(); │ │ │ │ - } │ │ │ │ - │ │ │ │ - // because we're using a box, we consider we want multiple selection │ │ │ │ - var prevMultiple = this.multiple; │ │ │ │ - this.multiple = true; │ │ │ │ - var layers = this.layers || [this.layer]; │ │ │ │ - this.events.triggerEvent("boxselectionstart", { │ │ │ │ - layers: layers │ │ │ │ - }); │ │ │ │ - var layer; │ │ │ │ - for (var l = 0; l < layers.length; ++l) { │ │ │ │ - layer = layers[l]; │ │ │ │ - for (var i = 0, len = layer.features.length; i < len; ++i) { │ │ │ │ - var feature = layer.features[i]; │ │ │ │ - // check if the feature is displayed │ │ │ │ - if (!feature.getVisibility()) { │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.geometryTypes == null || OpenLayers.Util.indexOf( │ │ │ │ - this.geometryTypes, feature.geometry.CLASS_NAME) > -1) { │ │ │ │ - if (bounds.toGeometry().intersects(feature.geometry)) { │ │ │ │ - if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { │ │ │ │ - this.select(feature); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.multiple = prevMultiple; │ │ │ │ - this.events.triggerEvent("boxselectionend", { │ │ │ │ - layers: layers │ │ │ │ - }); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + preFeatureInsert: function(feature) {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setMap │ │ │ │ - * Set the map property for the control. │ │ │ │ + * APIMethod: getDataExtent │ │ │ │ + * Calculates the max extent which includes all of the features. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * map - {<OpenLayers.Map>} │ │ │ │ - */ │ │ │ │ - setMap: function(map) { │ │ │ │ - this.handlers.feature.setMap(map); │ │ │ │ - if (this.box) { │ │ │ │ - this.handlers.box.setMap(map); │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setLayer │ │ │ │ - * Attach a new layer to the control, overriding any existing layers. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * layers - Array of {<OpenLayers.Layer.Vector>} or a single │ │ │ │ - * {<OpenLayers.Layer.Vector>} │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} or null if the layer has no features with │ │ │ │ + * geometries. │ │ │ │ */ │ │ │ │ - setLayer: function(layers) { │ │ │ │ - var isActive = this.active; │ │ │ │ - this.unselectAll(); │ │ │ │ - this.deactivate(); │ │ │ │ - if (this.layers) { │ │ │ │ - this.layer.destroy(); │ │ │ │ - this.layers = null; │ │ │ │ - } │ │ │ │ - this.initLayer(layers); │ │ │ │ - this.handlers.feature.layer = this.layer; │ │ │ │ - if (isActive) { │ │ │ │ - this.activate(); │ │ │ │ + getDataExtent: function() { │ │ │ │ + var maxExtent = null; │ │ │ │ + var features = this.features; │ │ │ │ + if (features && (features.length > 0)) { │ │ │ │ + var geometry = null; │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ + geometry = features[i].geometry; │ │ │ │ + if (geometry) { │ │ │ │ + if (maxExtent === null) { │ │ │ │ + maxExtent = new OpenLayers.Bounds(); │ │ │ │ + } │ │ │ │ + maxExtent.extend(geometry.getBounds()); │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + return maxExtent; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Control.SelectFeature" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Vector" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Strategy.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/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Strategy │ │ │ │ - * Abstract vector layer strategy class. Not to be instantiated directly. Use │ │ │ │ - * one of the strategy subclasses instead. │ │ │ │ + * Class: OpenLayers.Layer.HTTPRequest │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer> │ │ │ │ */ │ │ │ │ -OpenLayers.Strategy = OpenLayers.Class({ │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: layer │ │ │ │ - * {<OpenLayers.Layer.Vector>} The layer this strategy belongs to. │ │ │ │ - */ │ │ │ │ - layer: null, │ │ │ │ +OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: options │ │ │ │ - * {Object} Any options sent to the constructor. │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - options: null, │ │ │ │ + URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: active │ │ │ │ - * {Boolean} The control is active. │ │ │ │ + * Property: url │ │ │ │ + * {Array(String) or String} This is either an array of url strings or │ │ │ │ + * a single url string. │ │ │ │ */ │ │ │ │ - active: null, │ │ │ │ + url: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: autoActivate │ │ │ │ - * {Boolean} The creator of the strategy can set autoActivate to false │ │ │ │ - * to fully control when the protocol is activated and deactivated. │ │ │ │ - * Defaults to true. │ │ │ │ + /** │ │ │ │ + * Property: params │ │ │ │ + * {Object} Hashtable of key/value parameters │ │ │ │ */ │ │ │ │ - autoActivate: true, │ │ │ │ + params: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: autoDestroy │ │ │ │ - * {Boolean} The creator of the strategy can set autoDestroy to false │ │ │ │ - * to fully control when the strategy is destroyed. Defaults to │ │ │ │ - * true. │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ + * │ │ │ │ */ │ │ │ │ - autoDestroy: true, │ │ │ │ + reproject: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Strategy │ │ │ │ - * Abstract class for vector strategies. Create instances of a subclass. │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Layer.HTTPRequest │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * name - {String} │ │ │ │ + * url - {Array(String) or String} │ │ │ │ + * params - {Object} │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + initialize: function(name, url, params, options) { │ │ │ │ + OpenLayers.Layer.prototype.initialize.apply(this, [name, options]); │ │ │ │ + this.url = url; │ │ │ │ + if (!this.params) { │ │ │ │ + this.params = OpenLayers.Util.extend({}, params); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ * APIMethod: destroy │ │ │ │ - * Clean up the strategy. │ │ │ │ */ │ │ │ │ destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - this.layer = null; │ │ │ │ - this.options = null; │ │ │ │ + this.url = null; │ │ │ │ + this.params = null; │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setLayer │ │ │ │ - * Called to set the <layer> property. │ │ │ │ - * │ │ │ │ + * APIMethod: clone │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * layer - {<OpenLayers.Layer.Vector>} │ │ │ │ + * obj - {Object} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.HTTPRequest>} An exact clone of this │ │ │ │ + * <OpenLayers.Layer.HTTPRequest> │ │ │ │ */ │ │ │ │ - setLayer: function(layer) { │ │ │ │ - this.layer = layer; │ │ │ │ + clone: function(obj) { │ │ │ │ + │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.HTTPRequest(this.name, │ │ │ │ + this.url, │ │ │ │ + this.params, │ │ │ │ + this.getOptions()); │ │ │ │ + } │ │ │ │ + │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: setUrl │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * newUrl - {String} │ │ │ │ + */ │ │ │ │ + setUrl: function(newUrl) { │ │ │ │ + this.url = newUrl; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: activate │ │ │ │ - * Activate the strategy. Register any listeners, do appropriate setup. │ │ │ │ + * APIMethod: mergeNewParams │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * newParams - {Object} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} True if the strategy was successfully activated or false if │ │ │ │ - * the strategy was already active. │ │ │ │ + * redrawn: {Boolean} whether the layer was actually redrawn. │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - if (!this.active) { │ │ │ │ - this.active = true; │ │ │ │ - return true; │ │ │ │ + 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 false; │ │ │ │ + return ret; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Deactivate the strategy. Unregister any listeners, do appropriate │ │ │ │ - * tear-down. │ │ │ │ + * APIMethod: redraw │ │ │ │ + * Redraws the layer. Returns true if the layer was redrawn, false if not. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * force - {Boolean} Force redraw by adding random parameter. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} True if the strategy was successfully deactivated or false if │ │ │ │ - * the strategy was already inactive. │ │ │ │ + * {Boolean} The layer was redrawn. │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.active = false; │ │ │ │ - return true; │ │ │ │ + redraw: function(force) { │ │ │ │ + if (force) { │ │ │ │ + return this.mergeNewParams({ │ │ │ │ + "_olSalt": Math.random() │ │ │ │ + }); │ │ │ │ + } else { │ │ │ │ + return OpenLayers.Layer.prototype.redraw.apply(this, []); │ │ │ │ } │ │ │ │ - return false; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - 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. │ │ │ │ + /** │ │ │ │ + * 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: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * paramString - {String} │ │ │ │ + * urls - {Array(String)} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Filter>} │ │ │ │ + * {String} An entry from the urls array, deterministically selected based │ │ │ │ + * on the paramString. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ + 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)]; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Remove reference to anything added. │ │ │ │ - */ │ │ │ │ - destroy: function() {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: evaluate │ │ │ │ - * Evaluates this filter in a specific context. Instances or subclasses │ │ │ │ - * are supposed to override this method. │ │ │ │ + * 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: │ │ │ │ - * 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. │ │ │ │ + * newParams - {Object} │ │ │ │ + * altUrl - {String} Use this as the url instead of the layer's url │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - evaluate: function(context) { │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ + getFullRequestString: function(newParams, altUrl) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Clones this filter. Should be implemented by subclasses. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Filter>} Clone of this filter. │ │ │ │ - */ │ │ │ │ - clone: function() { │ │ │ │ - return null; │ │ │ │ - }, │ │ │ │ + // if not altUrl passed in, use layer's url │ │ │ │ + var url = altUrl || this.url; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: toString │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} Include <OpenLayers.Format.CQL> in your build to get a CQL │ │ │ │ - * representation of the filter returned. Otherwise "[Object object]" │ │ │ │ - * will be returned. │ │ │ │ - */ │ │ │ │ - toString: function() { │ │ │ │ - var string; │ │ │ │ - if (OpenLayers.Format && OpenLayers.Format.CQL) { │ │ │ │ - string = OpenLayers.Format.CQL.prototype.write(this); │ │ │ │ - } else { │ │ │ │ - string = Object.prototype.toString.call(this); │ │ │ │ + // 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); │ │ │ │ } │ │ │ │ - return string; │ │ │ │ + │ │ │ │ + // ignore parameters that are already in the url search string │ │ │ │ + var urlParams = │ │ │ │ + OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url)); │ │ │ │ + for (var key in allParams) { │ │ │ │ + if (key.toUpperCase() in urlParams) { │ │ │ │ + delete allParams[key]; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ + │ │ │ │ + return OpenLayers.Util.urlAppend(url, paramsString); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Filter" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.HTTPRequest" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Filter/Spatial.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/Filter.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Filter.Spatial │ │ │ │ - * This class represents a spatial filter. │ │ │ │ - * Currently implemented: BBOX, DWithin and Intersects │ │ │ │ + * Class: OpenLayers.Tile │ │ │ │ + * This is a class designed to designate a single tile, however │ │ │ │ + * it is explicitly designed to do relatively little. Tiles store │ │ │ │ + * information about themselves -- such as the URL that they are related │ │ │ │ + * to, and their size - but do not add themselves to the layer div │ │ │ │ + * automatically, for example. Create a new tile with the │ │ │ │ + * <OpenLayers.Tile> constructor, or a subclass. │ │ │ │ + * │ │ │ │ + * TBD 3.0 - remove reference to url in above paragraph │ │ │ │ * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Filter> │ │ │ │ */ │ │ │ │ -OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ +OpenLayers.Tile = OpenLayers.Class({ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: type │ │ │ │ - * {String} Type of spatial filter. │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} An events object that handles all │ │ │ │ + * events on the tile. │ │ │ │ * │ │ │ │ - * 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 │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * tile.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types: │ │ │ │ + * beforedraw - Triggered before the tile is drawn. Used to defer │ │ │ │ + * drawing to an animation queue. To defer drawing, listeners need │ │ │ │ + * to return false, which will abort drawing. The queue handler needs │ │ │ │ + * to call <draw>(true) to actually draw the tile. │ │ │ │ + * loadstart - Triggered when tile loading starts. │ │ │ │ + * loadend - Triggered when tile loading ends. │ │ │ │ + * loaderror - Triggered before the loadend event (i.e. when the tile is │ │ │ │ + * still hidden) if the tile could not be loaded. │ │ │ │ + * reload - Triggered when an already loading tile is reloaded. │ │ │ │ + * unload - Triggered before a tile is unloaded. │ │ │ │ */ │ │ │ │ - type: null, │ │ │ │ + events: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: property │ │ │ │ - * {String} Name of the context property to compare. │ │ │ │ + * APIProperty: eventListeners │ │ │ │ + * {Object} If set as an option at construction, the eventListeners │ │ │ │ + * object will be registered with <OpenLayers.Events.on>. Object │ │ │ │ + * structure must be a listeners object as shown in the example for │ │ │ │ + * the events.on method. │ │ │ │ + * │ │ │ │ + * This options can be set in the ``tileOptions`` option from │ │ │ │ + * <OpenLayers.Layer.Grid>. For example, to be notified of the │ │ │ │ + * ``loadend`` event of each tiles: │ │ │ │ + * (code) │ │ │ │ + * new OpenLayers.Layer.OSM('osm', 'http://tile.openstreetmap.org/${z}/${x}/${y}.png', { │ │ │ │ + * tileOptions: { │ │ │ │ + * eventListeners: { │ │ │ │ + * 'loadend': function(evt) { │ │ │ │ + * // do something on loadend │ │ │ │ + * } │ │ │ │ + * } │ │ │ │ + * } │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ */ │ │ │ │ - property: null, │ │ │ │ + eventListeners: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: value │ │ │ │ - * {<OpenLayers.Bounds> || <OpenLayers.Geometry>} The bounds or geometry │ │ │ │ - * to be used by the filter. Use bounds for BBOX filters and geometry │ │ │ │ - * for INTERSECTS or DWITHIN filters. │ │ │ │ + * Property: id │ │ │ │ + * {String} null │ │ │ │ */ │ │ │ │ - value: null, │ │ │ │ + id: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: layer │ │ │ │ + * {<OpenLayers.Layer>} layer the tile is attached to │ │ │ │ + */ │ │ │ │ + layer: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: distance │ │ │ │ - * {Number} The distance to use in a DWithin spatial filter. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - distance: null, │ │ │ │ + url: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: bounds │ │ │ │ + * {<OpenLayers.Bounds>} null │ │ │ │ + */ │ │ │ │ + bounds: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: size │ │ │ │ + * {<OpenLayers.Size>} null │ │ │ │ + */ │ │ │ │ + size: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: position │ │ │ │ + * {<OpenLayers.Pixel>} Top Left pixel of the tile │ │ │ │ + */ │ │ │ │ + position: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: distanceUnits │ │ │ │ - * {String} The units to use for the distance, e.g. 'm'. │ │ │ │ + * Property: isLoading │ │ │ │ + * {Boolean} Is the tile loading? │ │ │ │ + */ │ │ │ │ + isLoading: false, │ │ │ │ + │ │ │ │ + /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor. │ │ │ │ + * there is no need for the base tile class to have a url. │ │ │ │ */ │ │ │ │ - distanceUnits: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Filter.Spatial │ │ │ │ - * Creates a spatial filter. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object with properties to set on the │ │ │ │ - * filter. │ │ │ │ + * Constructor: OpenLayers.Tile │ │ │ │ + * Constructor for a new <OpenLayers.Tile> instance. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Filter.Spatial>} │ │ │ │ + * Parameters: │ │ │ │ + * layer - {<OpenLayers.Layer>} layer that the tile will go in. │ │ │ │ + * position - {<OpenLayers.Pixel>} │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * url - {<String>} │ │ │ │ + * size - {<OpenLayers.Size>} │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ + initialize: function(layer, position, bounds, url, size, options) { │ │ │ │ + this.layer = layer; │ │ │ │ + this.position = position.clone(); │ │ │ │ + this.setBounds(bounds); │ │ │ │ + this.url = url; │ │ │ │ + if (size) { │ │ │ │ + this.size = size.clone(); │ │ │ │ + } │ │ │ │ + │ │ │ │ + //give the tile a unique id based on its BBOX. │ │ │ │ + this.id = OpenLayers.Util.createUniqueID("Tile_"); │ │ │ │ + │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + │ │ │ │ + this.events = new OpenLayers.Events(this); │ │ │ │ + if (this.eventListeners instanceof Object) { │ │ │ │ + this.events.on(this.eventListeners); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: evaluate │ │ │ │ - * Evaluates this filter for a specific feature. │ │ │ │ - * │ │ │ │ + * Method: unload │ │ │ │ + * Call immediately before destroying if you are listening to tile │ │ │ │ + * events, so that counters are properly handled if tile is still │ │ │ │ + * loading at destroy-time. Will only fire an event if the tile is │ │ │ │ + * still loading. │ │ │ │ + */ │ │ │ │ + unload: function() { │ │ │ │ + if (this.isLoading) { │ │ │ │ + this.isLoading = false; │ │ │ │ + this.events.triggerEvent("unload"); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Nullify references to prevent circular references and memory leaks. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.layer = null; │ │ │ │ + this.bounds = null; │ │ │ │ + this.size = null; │ │ │ │ + this.position = null; │ │ │ │ + │ │ │ │ + if (this.eventListeners) { │ │ │ │ + this.events.un(this.eventListeners); │ │ │ │ + } │ │ │ │ + this.events.destroy(); │ │ │ │ + this.eventListeners = null; │ │ │ │ + this.events = null; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * Clear whatever is currently in the tile, then return whether or not │ │ │ │ + * it should actually be re-drawn. This is an example implementation │ │ │ │ + * that can be overridden by subclasses. The minimum thing to do here │ │ │ │ + * is to call <clear> and return the result from <shouldDraw>. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} feature to apply the filter to. │ │ │ │ + * 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} The feature meets filter criteria. │ │ │ │ + * {Boolean} Whether or not the tile should actually be drawn. Returns null │ │ │ │ + * if a beforedraw listener returned false. │ │ │ │ */ │ │ │ │ - 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.'); │ │ │ │ + draw: function(force) { │ │ │ │ + if (!force) { │ │ │ │ + //clear tile's contents and mark as not drawn │ │ │ │ + this.clear(); │ │ │ │ } │ │ │ │ - return intersect; │ │ │ │ + var draw = this.shouldDraw(); │ │ │ │ + if (draw && !force && this.events.triggerEvent("beforedraw") === false) { │ │ │ │ + draw = null; │ │ │ │ + } │ │ │ │ + return draw; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Clones this filter. │ │ │ │ + * 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: │ │ │ │ - * {<OpenLayers.Filter.Spatial>} Clone of this filter. │ │ │ │ + * {Boolean} Whether or not the tile should actually be drawn. │ │ │ │ */ │ │ │ │ - clone: function() { │ │ │ │ - var options = OpenLayers.Util.applyDefaults({ │ │ │ │ - value: this.value && this.value.clone && this.value.clone() │ │ │ │ - }, this); │ │ │ │ - return new OpenLayers.Filter.Spatial(options); │ │ │ │ + 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; │ │ │ │ }, │ │ │ │ - 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"; │ │ │ │ + /** │ │ │ │ + * Method: setBounds │ │ │ │ + * Sets the bounds on this instance │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds {<OpenLayers.Bounds>} │ │ │ │ + */ │ │ │ │ + setBounds: function(bounds) { │ │ │ │ + bounds = bounds.clone(); │ │ │ │ + if (this.layer.map.baseLayer.wrapDateLine) { │ │ │ │ + var worldExtent = this.layer.map.getMaxExtent(), │ │ │ │ + tolerance = this.layer.map.getResolution(); │ │ │ │ + bounds = bounds.wrapDateLine(worldExtent, { │ │ │ │ + leftTolerance: tolerance, │ │ │ │ + rightTolerance: tolerance │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + this.bounds = bounds; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveTo │ │ │ │ + * Reposition the tile. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * position - {<OpenLayers.Pixel>} │ │ │ │ + * redraw - {Boolean} Call draw method on tile after moving. │ │ │ │ + * Default is true │ │ │ │ + */ │ │ │ │ + moveTo: function(bounds, position, redraw) { │ │ │ │ + if (redraw == null) { │ │ │ │ + redraw = true; │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.setBounds(bounds); │ │ │ │ + this.position = position.clone(); │ │ │ │ + if (redraw) { │ │ │ │ + this.draw(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: clear │ │ │ │ + * Clear the tile of any bounds/position-related data so that it can │ │ │ │ + * be reused in a new location. │ │ │ │ + */ │ │ │ │ + clear: function(draw) { │ │ │ │ + // to be extended by subclasses │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Tile" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Strategy/BBOX.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/Strategy.js │ │ │ │ - * @requires OpenLayers/Filter/Spatial.js │ │ │ │ + * @requires OpenLayers/Tile.js │ │ │ │ + * @requires OpenLayers/Animation.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Strategy.BBOX │ │ │ │ - * A simple strategy that reads new features when the viewport invalidates │ │ │ │ - * some bounds. │ │ │ │ + * Class: OpenLayers.Tile.Image │ │ │ │ + * Instances of OpenLayers.Tile.Image are used to manage the image tiles │ │ │ │ + * used by various layers. Create a new image tile with the │ │ │ │ + * <OpenLayers.Tile.Image> constructor. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Strategy> │ │ │ │ + * - <OpenLayers.Tile> │ │ │ │ */ │ │ │ │ -OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ +OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: bounds │ │ │ │ - * {<OpenLayers.Bounds>} The current data bounds (in the same projection │ │ │ │ - * as the layer - not always the same projection as the map). │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} An events object that handles all │ │ │ │ + * events on the tile. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * tile.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to the <OpenLayers.Tile> events): │ │ │ │ + * beforeload - Triggered before an image is prepared for loading, when the │ │ │ │ + * url for the image is known already. Listeners may call <setImage> on │ │ │ │ + * the tile instance. If they do so, that image will be used and no new │ │ │ │ + * one will be created. │ │ │ │ */ │ │ │ │ - bounds: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: resolution │ │ │ │ - * {Float} The current data resolution. │ │ │ │ + * APIProperty: url │ │ │ │ + * {String} The URL of the image being requested. No default. Filled in by │ │ │ │ + * layer.getURL() function. May be modified by loadstart listeners. │ │ │ │ */ │ │ │ │ - resolution: null, │ │ │ │ + url: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: imgDiv │ │ │ │ + * {HTMLImageElement} The image for this tile. │ │ │ │ + */ │ │ │ │ + imgDiv: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: ratio │ │ │ │ - * {Float} The ratio of the data bounds to the viewport bounds (in each │ │ │ │ - * dimension). Default is 2. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - ratio: 2, │ │ │ │ + frame: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: resFactor │ │ │ │ - * {Float} Optional factor used to determine when previously requested │ │ │ │ - * features are invalid. If set, the resFactor will be compared to the │ │ │ │ - * resolution of the previous request to the current map resolution. │ │ │ │ - * If resFactor > (old / new) and 1/resFactor < (old / new). If you │ │ │ │ - * set a resFactor of 1, data will be requested every time the │ │ │ │ - * resolution changes. If you set a resFactor of 3, data will be │ │ │ │ - * requested if the old resolution is 3 times the new, or if the new is │ │ │ │ - * 3 times the old. If the old bounds do not contain the new bounds │ │ │ │ - * new data will always be requested (with or without considering │ │ │ │ - * resFactor). │ │ │ │ + * Property: imageReloadAttempts │ │ │ │ + * {Integer} Attempts to load the image. │ │ │ │ */ │ │ │ │ - resFactor: null, │ │ │ │ + imageReloadAttempts: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: response │ │ │ │ - * {<OpenLayers.Protocol.Response>} The protocol response object returned │ │ │ │ - * by the layer protocol. │ │ │ │ + * Property: layerAlphaHack │ │ │ │ + * {Boolean} True if the png alpha hack needs to be applied on the layer's div. │ │ │ │ */ │ │ │ │ - response: null, │ │ │ │ + layerAlphaHack: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Strategy.BBOX │ │ │ │ - * Create a new BBOX strategy. │ │ │ │ + * Property: asyncRequestId │ │ │ │ + * {Integer} ID of an request to see if request is still valid. This is a │ │ │ │ + * number which increments by 1 for each asynchronous request. │ │ │ │ + */ │ │ │ │ + asyncRequestId: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: maxGetUrlLength │ │ │ │ + * {Number} If set, requests that would result in GET urls with more │ │ │ │ + * characters than the number provided will be made using form-encoded │ │ │ │ + * HTTP POST. It is good practice to avoid urls that are longer than 2048 │ │ │ │ + * characters. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * 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, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: activate │ │ │ │ - * Set up strategy with regard to reading new batches of remote data. │ │ │ │ + * Property: canvasContext │ │ │ │ + * {CanvasRenderingContext2D} A canvas context associated with │ │ │ │ + * the tile image. │ │ │ │ + */ │ │ │ │ + canvasContext: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: crossOriginKeyword │ │ │ │ + * The value of the crossorigin keyword to use when loading images. This is │ │ │ │ + * only relevant when using <getCanvasContext> for tiles from remote │ │ │ │ + * origins and should be set to either 'anonymous' or 'use-credentials' │ │ │ │ + * for servers that send Access-Control-Allow-Origin headers with their │ │ │ │ + * tiles. │ │ │ │ + */ │ │ │ │ + crossOriginKeyword: null, │ │ │ │ + │ │ │ │ + /** TBD 3.0 - reorder the parameters to the init function to remove │ │ │ │ + * URL. the getUrl() function on the layer gets called on │ │ │ │ + * each draw(), so no need to specify it here. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Tile.Image │ │ │ │ + * Constructor for a new <OpenLayers.Tile.Image> instance. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The strategy was successfully activated. │ │ │ │ + * Parameters: │ │ │ │ + * layer - {<OpenLayers.Layer>} layer that the tile will go in. │ │ │ │ + * position - {<OpenLayers.Pixel>} │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * url - {<String>} Deprecated. Remove me in 3.0. │ │ │ │ + * size - {<OpenLayers.Size>} │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - this.layer.events.on({ │ │ │ │ - "moveend": this.update, │ │ │ │ - "refresh": this.update, │ │ │ │ - "visibilitychanged": this.update, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.update(); │ │ │ │ + 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"; │ │ │ │ } │ │ │ │ - return activated; │ │ │ │ + if (this.maxGetUrlLength != null) { │ │ │ │ + OpenLayers.Util.extend(this, OpenLayers.Tile.Image.IFrame); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * nullify references to prevent circular references and memory leaks │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + if (this.imgDiv) { │ │ │ │ + this.clear(); │ │ │ │ + this.imgDiv = null; │ │ │ │ + this.frame = null; │ │ │ │ + } │ │ │ │ + // don't handle async requests any more │ │ │ │ + this.asyncRequestId = null; │ │ │ │ + OpenLayers.Tile.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Tear down strategy with regard to reading new batches of remote data. │ │ │ │ + * Method: draw │ │ │ │ + * Check that a tile should be drawn, and draw it. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The strategy was successfully deactivated. │ │ │ │ + * {Boolean} Was a tile drawn? Or null if a beforedraw listener returned │ │ │ │ + * false. │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - this.layer.events.un({ │ │ │ │ - "moveend": this.update, │ │ │ │ - "refresh": this.update, │ │ │ │ - "visibilitychanged": this.update, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + draw: function() { │ │ │ │ + var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments); │ │ │ │ + if (shouldDraw) { │ │ │ │ + // The layer's reproject option is deprecated. │ │ │ │ + if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { │ │ │ │ + // getBoundsFromBaseLayer is defined in deprecated.js. │ │ │ │ + this.bounds = this.getBoundsFromBaseLayer(this.position); │ │ │ │ + } │ │ │ │ + if (this.isLoading) { │ │ │ │ + //if we're already loading, send 'reload' instead of 'loadstart'. │ │ │ │ + this._loadEvent = "reload"; │ │ │ │ + } else { │ │ │ │ + this.isLoading = true; │ │ │ │ + this._loadEvent = "loadstart"; │ │ │ │ + } │ │ │ │ + this.renderTile(); │ │ │ │ + this.positionTile(); │ │ │ │ + } else if (shouldDraw === false) { │ │ │ │ + this.unload(); │ │ │ │ } │ │ │ │ - return deactivated; │ │ │ │ + return shouldDraw; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: update │ │ │ │ - * Callback function called on "moveend" or "refresh" layer events. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will determine │ │ │ │ - * the behaviour of this Strategy │ │ │ │ - * │ │ │ │ - * Valid options include: │ │ │ │ - * force - {Boolean} if true, new data must be unconditionally read. │ │ │ │ - * noAbort - {Boolean} if true, do not abort previous requests. │ │ │ │ + * Method: renderTile │ │ │ │ + * Internal function to actually initialize the image tile, │ │ │ │ + * position it correctly, and set its url. │ │ │ │ */ │ │ │ │ - update: function(options) { │ │ │ │ - var mapBounds = this.getMapBounds(); │ │ │ │ - if (mapBounds !== null && ((options && options.force) || │ │ │ │ - (this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds)))) { │ │ │ │ - this.calculateBounds(mapBounds); │ │ │ │ - this.resolution = this.layer.map.getResolution(); │ │ │ │ - this.triggerRead(options); │ │ │ │ + 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: getMapBounds │ │ │ │ - * Get the map bounds expressed in the same projection as this layer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} Map bounds in the projection of the layer. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - getMapBounds: function() { │ │ │ │ - if (this.layer.map === null) { │ │ │ │ - return null; │ │ │ │ - } │ │ │ │ - var bounds = this.layer.map.getExtent(); │ │ │ │ - if (bounds && !this.layer.projection.equals( │ │ │ │ - this.layer.map.getProjectionObject())) { │ │ │ │ - bounds = bounds.clone().transform( │ │ │ │ - this.layer.map.getProjectionObject(), this.layer.projection │ │ │ │ - ); │ │ │ │ + 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(); │ │ │ │ } │ │ │ │ - return bounds; │ │ │ │ + style.left = this.position.x + "px"; │ │ │ │ + style.top = this.position.y + "px"; │ │ │ │ + style.width = Math.round(ratio * size.w) + "px"; │ │ │ │ + style.height = Math.round(ratio * size.h) + "px"; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: invalidBounds │ │ │ │ - * Determine whether the previously requested set of features is invalid. │ │ │ │ - * This occurs when the new map bounds do not contain the previously │ │ │ │ - * requested bounds. In addition, if <resFactor> is set, it will be │ │ │ │ - * considered. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be │ │ │ │ - * retrieved from the map object if not provided │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - invalidBounds: function(mapBounds) { │ │ │ │ - if (!mapBounds) { │ │ │ │ - mapBounds = this.getMapBounds(); │ │ │ │ - } │ │ │ │ - var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds); │ │ │ │ - if (!invalid && this.resFactor) { │ │ │ │ - var ratio = this.resolution / this.layer.map.getResolution(); │ │ │ │ - invalid = (ratio >= this.resFactor || ratio <= (1 / this.resFactor)); │ │ │ │ + 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"); │ │ │ │ } │ │ │ │ - return invalid; │ │ │ │ + this.canvasContext = null; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: calculateBounds │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * mapBounds - {<OpenLayers.Bounds>} the current map extent, will be │ │ │ │ - * retrieved from the map object if not provided │ │ │ │ + * Method: getImage │ │ │ │ + * Returns or creates and returns the tile image. │ │ │ │ */ │ │ │ │ - calculateBounds: function(mapBounds) { │ │ │ │ - if (!mapBounds) { │ │ │ │ - mapBounds = this.getMapBounds(); │ │ │ │ + getImage: function() { │ │ │ │ + if (!this.imgDiv) { │ │ │ │ + this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false); │ │ │ │ + │ │ │ │ + var style = this.imgDiv.style; │ │ │ │ + if (this.frame) { │ │ │ │ + var left = 0, │ │ │ │ + top = 0; │ │ │ │ + if (this.layer.gutter) { │ │ │ │ + left = this.layer.gutter / this.layer.tileSize.w * 100; │ │ │ │ + top = this.layer.gutter / this.layer.tileSize.h * 100; │ │ │ │ + } │ │ │ │ + style.left = -left + "%"; │ │ │ │ + style.top = -top + "%"; │ │ │ │ + style.width = (2 * left + 100) + "%"; │ │ │ │ + style.height = (2 * top + 100) + "%"; │ │ │ │ + } │ │ │ │ + style.visibility = "hidden"; │ │ │ │ + style.opacity = 0; │ │ │ │ + if (this.layer.opacity < 1) { │ │ │ │ + style.filter = 'alpha(opacity=' + │ │ │ │ + (this.layer.opacity * 100) + │ │ │ │ + ')'; │ │ │ │ + } │ │ │ │ + style.position = "absolute"; │ │ │ │ + if (this.layerAlphaHack) { │ │ │ │ + // move the image out of sight │ │ │ │ + style.paddingTop = style.height; │ │ │ │ + style.height = "0"; │ │ │ │ + style.width = "100%"; │ │ │ │ + } │ │ │ │ + if (this.frame) { │ │ │ │ + this.frame.appendChild(this.imgDiv); │ │ │ │ + } │ │ │ │ } │ │ │ │ - var center = mapBounds.getCenterLonLat(); │ │ │ │ - var dataWidth = mapBounds.getWidth() * this.ratio; │ │ │ │ - var dataHeight = mapBounds.getHeight() * this.ratio; │ │ │ │ - this.bounds = new OpenLayers.Bounds( │ │ │ │ - center.lon - (dataWidth / 2), │ │ │ │ - center.lat - (dataHeight / 2), │ │ │ │ - center.lon + (dataWidth / 2), │ │ │ │ - center.lat + (dataHeight / 2) │ │ │ │ - ); │ │ │ │ + │ │ │ │ + return this.imgDiv; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: triggerRead │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Additional options for the protocol's read method │ │ │ │ - * (optional) │ │ │ │ + * APIMethod: setImage │ │ │ │ + * Sets the image element for this tile. This method should only be called │ │ │ │ + * from beforeload listeners. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} The protocol response object │ │ │ │ - * returned by the layer protocol. │ │ │ │ + * Parameters │ │ │ │ + * img - {HTMLImageElement} The image to use for this tile. │ │ │ │ */ │ │ │ │ - triggerRead: function(options) { │ │ │ │ - if (this.response && !(options && options.noAbort === true)) { │ │ │ │ - this.layer.protocol.abort(this.response); │ │ │ │ - this.layer.events.triggerEvent("loadend"); │ │ │ │ - } │ │ │ │ - var evt = { │ │ │ │ - filter: this.createFilter() │ │ │ │ - }; │ │ │ │ - this.layer.events.triggerEvent("loadstart", evt); │ │ │ │ - this.response = this.layer.protocol.read( │ │ │ │ - OpenLayers.Util.applyDefaults({ │ │ │ │ - filter: evt.filter, │ │ │ │ - callback: this.merge, │ │ │ │ - scope: this │ │ │ │ - }, options)); │ │ │ │ + setImage: function(img) { │ │ │ │ + this.imgDiv = img; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createFilter │ │ │ │ - * Creates a spatial BBOX filter. If the layer that this strategy belongs │ │ │ │ - * to has a filter property, this filter will be combined with the BBOX │ │ │ │ - * filter. │ │ │ │ - * │ │ │ │ - * Returns │ │ │ │ - * {<OpenLayers.Filter>} The filter object. │ │ │ │ + * Method: initImage │ │ │ │ + * Creates the content for the frame on the tile. │ │ │ │ */ │ │ │ │ - createFilter: function() { │ │ │ │ - var filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ - value: this.bounds, │ │ │ │ - projection: this.layer.projection │ │ │ │ - }); │ │ │ │ - if (this.layer.filter) { │ │ │ │ - filter = new OpenLayers.Filter.Logical({ │ │ │ │ - type: OpenLayers.Filter.Logical.AND, │ │ │ │ - filters: [this.layer.filter, filter] │ │ │ │ - }); │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - return filter; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: merge │ │ │ │ - * Given a list of features, determine which ones to add to the layer. │ │ │ │ - * If the layer projection differs from the map projection, features │ │ │ │ - * will be transformed from the layer projection to the map projection. │ │ │ │ + * Method: setImgSrc │ │ │ │ + * Sets the source for the tile image │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object passed │ │ │ │ - * by the protocol. │ │ │ │ + * url - {String} or undefined to hide the image │ │ │ │ */ │ │ │ │ - merge: function(resp) { │ │ │ │ - this.layer.destroyFeatures(); │ │ │ │ - if (resp.success()) { │ │ │ │ - var features = resp.features; │ │ │ │ - if (features && features.length > 0) { │ │ │ │ - var remote = this.layer.projection; │ │ │ │ - var local = this.layer.map.getProjectionObject(); │ │ │ │ - if (!local.equals(remote)) { │ │ │ │ - var geom; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - geom = features[i].geometry; │ │ │ │ - if (geom) { │ │ │ │ - geom.transform(remote, local); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + 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"); │ │ │ │ } │ │ │ │ - this.layer.addFeatures(features); │ │ │ │ } │ │ │ │ + img.src = url; │ │ │ │ } else { │ │ │ │ - this.bounds = null; │ │ │ │ + // 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); │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.response = null; │ │ │ │ - this.layer.events.triggerEvent("loadend", { │ │ │ │ - response: resp │ │ │ │ - }); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy.BBOX" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Strategy/Fixed.js │ │ │ │ - ====================================================================== */ │ │ │ │ - │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Strategy.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Strategy.Fixed │ │ │ │ - * A simple strategy that requests features once and never requests new data. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Strategy> │ │ │ │ - */ │ │ │ │ -OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIProperty: preload │ │ │ │ - * {Boolean} Load data before layer made visible. Enabling this may result │ │ │ │ - * in considerable overhead if your application loads many data layers │ │ │ │ - * that are not visible by default. Default is false. │ │ │ │ + * Method: getTile │ │ │ │ + * Get the tile's markup. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The tile's markup │ │ │ │ */ │ │ │ │ - preload: false, │ │ │ │ + getTile: function() { │ │ │ │ + return this.frame ? this.frame : this.getImage(); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Strategy.Fixed │ │ │ │ - * Create a new Fixed strategy. │ │ │ │ + * 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. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * 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: activate │ │ │ │ - * Activate the strategy: load data or add listener to load when visible │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} True if the strategy was successfully activated or false if │ │ │ │ - * the strategy was already active. │ │ │ │ + * Method: onImageLoad │ │ │ │ + * Handler for the image onload event │ │ │ │ */ │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); │ │ │ │ - if (activated) { │ │ │ │ - this.layer.events.on({ │ │ │ │ - "refresh": this.load, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - if (this.layer.visibility == true || this.preload) { │ │ │ │ - this.load(); │ │ │ │ - } else { │ │ │ │ - this.layer.events.on({ │ │ │ │ - "visibilitychanged": this.load, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - } │ │ │ │ + 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')"; │ │ │ │ } │ │ │ │ - return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: deactivate │ │ │ │ - * Deactivate the strategy. Undo what is done in <activate>. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The strategy was successfully deactivated. │ │ │ │ + * Method: onImageError │ │ │ │ + * Handler for the image onerror event │ │ │ │ */ │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - this.layer.events.un({ │ │ │ │ - "refresh": this.load, │ │ │ │ - "visibilitychanged": this.load, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + 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(); │ │ │ │ + } │ │ │ │ } │ │ │ │ - return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: load │ │ │ │ - * Tells protocol to load data and unhooks the visibilitychanged event │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} options to pass to protocol read. │ │ │ │ + * Method: stopLoading │ │ │ │ + * Stops a loading sequence so <onImageLoad> won't be executed. │ │ │ │ */ │ │ │ │ - load: function(options) { │ │ │ │ - var layer = this.layer; │ │ │ │ - layer.events.triggerEvent("loadstart", { │ │ │ │ - filter: layer.filter │ │ │ │ - }); │ │ │ │ - layer.protocol.read(OpenLayers.Util.applyDefaults({ │ │ │ │ - callback: this.merge, │ │ │ │ - filter: layer.filter, │ │ │ │ - scope: this │ │ │ │ - }, options)); │ │ │ │ - layer.events.un({ │ │ │ │ - "visibilitychanged": this.load, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ + stopLoading: function() { │ │ │ │ + OpenLayers.Event.stopObservingElement(this.imgDiv); │ │ │ │ + window.clearTimeout(this._loadTimeout); │ │ │ │ + delete this._loadTimeout; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: merge │ │ │ │ - * Add all features to the layer. │ │ │ │ - * If the layer projection differs from the map projection, features │ │ │ │ - * will be transformed from the layer projection to the map projection. │ │ │ │ + * 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. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object passed │ │ │ │ - * by the protocol. │ │ │ │ + * 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} │ │ │ │ */ │ │ │ │ - merge: function(resp) { │ │ │ │ - var layer = this.layer; │ │ │ │ - layer.destroyFeatures(); │ │ │ │ - var features = resp.features; │ │ │ │ - if (features && features.length > 0) { │ │ │ │ - var remote = layer.projection; │ │ │ │ - var local = layer.map.getProjectionObject(); │ │ │ │ - if (!local.equals(remote)) { │ │ │ │ - var geom; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - geom = features[i].geometry; │ │ │ │ - if (geom) { │ │ │ │ - geom.transform(remote, local); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - layer.addFeatures(features); │ │ │ │ + return this.canvasContext; │ │ │ │ } │ │ │ │ - layer.events.triggerEvent("loadend", { │ │ │ │ - response: resp │ │ │ │ - }); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy.Fixed" │ │ │ │ + CLASS_NAME: "OpenLayers.Tile.Image" │ │ │ │ + │ │ │ │ }); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Tile.Image.IMAGE │ │ │ │ + * {HTMLImageElement} The image for a tile. │ │ │ │ + */ │ │ │ │ +OpenLayers.Tile.Image.IMAGE = (function() { │ │ │ │ + var img = new Image(); │ │ │ │ + img.className = "olTileImage"; │ │ │ │ + // avoid image gallery menu in IE6 │ │ │ │ + img.galleryImg = "no"; │ │ │ │ + return img; │ │ │ │ +}()); │ │ │ │ + │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Renderer/Elements.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/Renderer.js │ │ │ │ + * @requires OpenLayers/Layer/HTTPRequest.js │ │ │ │ + * @requires OpenLayers/Tile/Image.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. │ │ │ │ + * Class: OpenLayers.Layer.Grid │ │ │ │ + * Base class for layers that use a lattice of tiles. Create a new grid │ │ │ │ + * layer with the <OpenLayers.Layer.Grid> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.HTTPRequest> │ │ │ │ */ │ │ │ │ -OpenLayers.ElementsIndexer = OpenLayers.Class({ │ │ │ │ +OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: maxZIndex │ │ │ │ - * {Integer} This is the largest-most z-index value for a node │ │ │ │ - * contained within the indexer. │ │ │ │ + * APIProperty: tileSize │ │ │ │ + * {<OpenLayers.Size>} │ │ │ │ */ │ │ │ │ - maxZIndex: null, │ │ │ │ + tileSize: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: order │ │ │ │ - * {Array<String>} This is an array of node id's stored in the │ │ │ │ - * order that they should show up on screen. Id's higher up in the │ │ │ │ - * array (higher array index) represent nodes with higher z-indeces. │ │ │ │ + * Property: tileOriginCorner │ │ │ │ + * {String} If the <tileOrigin> property is not provided, the tile origin │ │ │ │ + * will be derived from the layer's <maxExtent>. The corner of the │ │ │ │ + * <maxExtent> used is determined by this property. Acceptable values │ │ │ │ + * are "tl" (top left), "tr" (top right), "bl" (bottom left), and "br" │ │ │ │ + * (bottom right). Default is "bl". │ │ │ │ */ │ │ │ │ - order: null, │ │ │ │ + tileOriginCorner: "bl", │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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). │ │ │ │ + * APIProperty: tileOrigin │ │ │ │ + * {<OpenLayers.LonLat>} Optional origin for aligning the grid of tiles. │ │ │ │ + * If provided, requests for tiles at all resolutions will be aligned │ │ │ │ + * with this location (no tiles shall overlap this location). If │ │ │ │ + * not provided, the grid of tiles will be aligned with the layer's │ │ │ │ + * <maxExtent>. Default is ``null``. │ │ │ │ */ │ │ │ │ - indices: null, │ │ │ │ + tileOrigin: null, │ │ │ │ + │ │ │ │ + /** APIProperty: tileOptions │ │ │ │ + * {Object} optional configuration options for <OpenLayers.Tile> instances │ │ │ │ + * created by this Layer, if supported by the tile class. │ │ │ │ + */ │ │ │ │ + tileOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIProperty: tileClass │ │ │ │ + * {<OpenLayers.Tile>} The tile class to use for this layer. │ │ │ │ + * Defaults is OpenLayers.Tile.Image. │ │ │ │ */ │ │ │ │ - compare: null, │ │ │ │ + tileClass: OpenLayers.Tile.Image, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: initialize │ │ │ │ - * Create a new indexer with │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * yOrdering - {Boolean} Whether to use y-ordering. │ │ │ │ + * Property: grid │ │ │ │ + * {Array(Array(<OpenLayers.Tile>))} This is an array of rows, each row is │ │ │ │ + * an array of tiles. │ │ │ │ */ │ │ │ │ - initialize: function(yOrdering) { │ │ │ │ + grid: null, │ │ │ │ │ │ │ │ - this.compare = yOrdering ? │ │ │ │ - OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : │ │ │ │ - OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER; │ │ │ │ + /** │ │ │ │ + * 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, │ │ │ │ │ │ │ │ - this.clear(); │ │ │ │ - }, │ │ │ │ + /** 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, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: insert │ │ │ │ - * Insert a new node into the indexer. In order to find the correct │ │ │ │ - * positioning for the node to be inserted, this method uses a binary │ │ │ │ - * search. This makes inserting O(log(n)). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * newNode - {DOMElement} The new node to be inserted. │ │ │ │ - * │ │ │ │ - * Returns │ │ │ │ - * {DOMElement} the node before which we should insert our newNode, or │ │ │ │ - * null if newNode can just be appended. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + buffer: 0, │ │ │ │ │ │ │ │ - var nodeId = newNode.id; │ │ │ │ + /** │ │ │ │ + * 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", │ │ │ │ │ │ │ │ - this.determineZIndex(newNode); │ │ │ │ + /** │ │ │ │ + * APIProperty: numLoadingTiles │ │ │ │ + * {Integer} How many tiles are still loading? │ │ │ │ + */ │ │ │ │ + numLoadingTiles: 0, │ │ │ │ │ │ │ │ - var leftIndex = -1; │ │ │ │ - var rightIndex = this.order.length; │ │ │ │ - var middle; │ │ │ │ + /** │ │ │ │ + * Property: serverResolutions │ │ │ │ + * {Array(Number}} This property is documented in subclasses as │ │ │ │ + * an API property. │ │ │ │ + */ │ │ │ │ + serverResolutions: null, │ │ │ │ │ │ │ │ - while (rightIndex - leftIndex > 1) { │ │ │ │ - middle = parseInt((leftIndex + rightIndex) / 2); │ │ │ │ + /** │ │ │ │ + * Property: loading │ │ │ │ + * {Boolean} Indicates if tiles are being loaded. │ │ │ │ + */ │ │ │ │ + loading: false, │ │ │ │ │ │ │ │ - var placement = this.compare(this, newNode, │ │ │ │ - OpenLayers.Util.getElement(this.order[middle])); │ │ │ │ + /** │ │ │ │ + * Property: backBuffer │ │ │ │ + * {DOMElement} The back buffer. │ │ │ │ + */ │ │ │ │ + backBuffer: null, │ │ │ │ │ │ │ │ - if (placement > 0) { │ │ │ │ - leftIndex = middle; │ │ │ │ - } else { │ │ │ │ - rightIndex = middle; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: gridResolution │ │ │ │ + * {Number} The resolution of the current grid. Used for backbuffer and │ │ │ │ + * client zoom. This property is updated every time the grid is │ │ │ │ + * initialized. │ │ │ │ + */ │ │ │ │ + gridResolution: null, │ │ │ │ │ │ │ │ - this.order.splice(rightIndex, 0, nodeId); │ │ │ │ - this.indices[nodeId] = this.getZIndex(newNode); │ │ │ │ + /** │ │ │ │ + * Property: backBufferResolution │ │ │ │ + * {Number} The resolution of the current back buffer. This property is │ │ │ │ + * updated each time a back buffer is created. │ │ │ │ + */ │ │ │ │ + backBufferResolution: null, │ │ │ │ │ │ │ │ - // 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); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: backBufferLonLat │ │ │ │ + * {Object} The top-left corner of the current back buffer. Includes lon │ │ │ │ + * and lat properties. This object is updated each time a back buffer │ │ │ │ + * is created. │ │ │ │ + */ │ │ │ │ + backBufferLonLat: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: remove │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} The node to be removed. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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]; │ │ │ │ + backBufferTimerId: null, │ │ │ │ │ │ │ │ - // 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; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: removeBackBufferDelay │ │ │ │ + * {Number} Delay for removing the backbuffer when all tiles have finished │ │ │ │ + * loading. Can be set to 0 when no css opacity transitions for the │ │ │ │ + * olTileImage class are used. Default is 0 for <singleTile> layers, │ │ │ │ + * 2500 for tiled layers. See <className> for more information on │ │ │ │ + * tile animation. │ │ │ │ + */ │ │ │ │ + removeBackBufferDelay: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clear │ │ │ │ + * APIProperty: className │ │ │ │ + * {String} Name of the class added to the layer div. If not set in the │ │ │ │ + * options passed to the constructor then className defaults to │ │ │ │ + * "olLayerGridSingleTile" for single tile layers (see <singleTile>), │ │ │ │ + * and "olLayerGrid" for non single tile layers. │ │ │ │ + * │ │ │ │ + * Note: │ │ │ │ + * │ │ │ │ + * The displaying of tiles is not animated by default for single tile │ │ │ │ + * layers - OpenLayers' default theme (style.css) includes this: │ │ │ │ + * (code) │ │ │ │ + * .olLayerGrid .olTileImage { │ │ │ │ + * -webkit-transition: opacity 0.2s linear; │ │ │ │ + * -moz-transition: opacity 0.2s linear; │ │ │ │ + * -o-transition: opacity 0.2s linear; │ │ │ │ + * transition: opacity 0.2s linear; │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ + * To animate tile displaying for any grid layer the following │ │ │ │ + * CSS rule can be used: │ │ │ │ + * (code) │ │ │ │ + * .olTileImage { │ │ │ │ + * -webkit-transition: opacity 0.2s linear; │ │ │ │ + * -moz-transition: opacity 0.2s linear; │ │ │ │ + * -o-transition: opacity 0.2s linear; │ │ │ │ + * transition: opacity 0.2s linear; │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ + * In that case, to avoid flash effects, <removeBackBufferDelay> │ │ │ │ + * should not be zero. │ │ │ │ */ │ │ │ │ - clear: function() { │ │ │ │ - this.order = []; │ │ │ │ - this.indices = {}; │ │ │ │ - this.maxZIndex = 0; │ │ │ │ - }, │ │ │ │ + className: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: exists │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * layer.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} The node to test for existence. │ │ │ │ + * Listeners will be called with a reference to an event object. The │ │ │ │ + * properties of this event depends on exactly what happened. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Whether or not the node exists in the indexer? │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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. │ │ │ │ - * │ │ │ │ + * Property: gridLayout │ │ │ │ + * {Object} Object containing properties tilelon, tilelat, startcol, │ │ │ │ + * startrow │ │ │ │ + */ │ │ │ │ + gridLayout: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: rowSign │ │ │ │ + * {Number} 1 for grids starting at the top, -1 for grids starting at the │ │ │ │ + * bottom. This is used for several grid index and offset calculations. │ │ │ │ + */ │ │ │ │ + rowSign: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: transitionendEvents │ │ │ │ + * {Array} Event names for transitionend │ │ │ │ + */ │ │ │ │ + transitionendEvents: [ │ │ │ │ + 'transitionend', 'webkitTransitionEnd', 'otransitionend', │ │ │ │ + 'oTransitionEnd' │ │ │ │ + ], │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Grid │ │ │ │ + * Create a new grid layer │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} The node whose z-index to get. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} The z-index value for the specified node (from the node │ │ │ │ - * data itself). │ │ │ │ + * name - {String} │ │ │ │ + * url - {String} │ │ │ │ + * params - {Object} │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ */ │ │ │ │ - getZIndex: function(node) { │ │ │ │ - return node._style.graphicZIndex; │ │ │ │ + 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: 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} │ │ │ │ + * Method: initProperties │ │ │ │ + * Set any properties that depend on the value of singleTile. │ │ │ │ + * Currently sets removeBackBufferDelay and className │ │ │ │ */ │ │ │ │ - determineZIndex: function(node) { │ │ │ │ - var zIndex = node._style.graphicZIndex; │ │ │ │ + initProperties: function() { │ │ │ │ + if (this.options.removeBackBufferDelay === undefined) { │ │ │ │ + this.removeBackBufferDelay = this.singleTile ? 0 : 2500; │ │ │ │ + } │ │ │ │ │ │ │ │ - // 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; │ │ │ │ + if (this.options.className === undefined) { │ │ │ │ + this.className = this.singleTile ? 'olLayerGridSingleTile' : │ │ │ │ + 'olLayerGrid'; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getNextElement │ │ │ │ - * Get the next element in the order stack. │ │ │ │ - * │ │ │ │ + * Method: setMap │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * index - {Integer} The index of the current node in this.order. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} the node following the index passed in, or │ │ │ │ - * null. │ │ │ │ + * map - {<OpenLayers.Map>} The map. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map); │ │ │ │ + OpenLayers.Element.addClass(this.div, this.className); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.ElementsIndexer" │ │ │ │ -}); │ │ │ │ + /** │ │ │ │ + * Method: removeMap │ │ │ │ + * Called when the layer is removed from the map. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} The map. │ │ │ │ + */ │ │ │ │ + removeMap: function(map) { │ │ │ │ + this.removeBackBuffer(); │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * 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 = { │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Deconstruct the layer and clear the grid. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.removeBackBuffer(); │ │ │ │ + this.clearGrid(); │ │ │ │ + │ │ │ │ + this.grid = null; │ │ │ │ + this.tileSize = null; │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * 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: │ │ │ │ - * indexer - {<OpenLayers.ElementsIndexer>} │ │ │ │ - * newNode - {DOMElement} │ │ │ │ - * nextNode - {DOMElement} │ │ │ │ - * │ │ │ │ + * newParams - {Object} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Integer} │ │ │ │ + * redrawn: {Boolean} whether the layer was actually redrawn. │ │ │ │ */ │ │ │ │ - Z_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ - var newZIndex = indexer.getZIndex(newNode); │ │ │ │ │ │ │ │ - var returnVal = 0; │ │ │ │ - if (nextNode) { │ │ │ │ - var nextZIndex = indexer.getZIndex(nextNode); │ │ │ │ - returnVal = newZIndex - nextZIndex; │ │ │ │ + /** │ │ │ │ + * 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; │ │ │ │ } │ │ │ │ - │ │ │ │ - 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. │ │ │ │ + * APIMethod: addOptions │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * indexer - {<OpenLayers.ElementsIndexer>} │ │ │ │ - * newNode - {DOMElement} │ │ │ │ - * nextNode - {DOMElement} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Integer} │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - │ │ │ │ - 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. │ │ │ │ - * │ │ │ │ + * APIMethod: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * indexer - {<OpenLayers.ElementsIndexer>} │ │ │ │ - * newNode - {DOMElement} │ │ │ │ - * nextNode - {DOMElement} │ │ │ │ + * obj - {Object} Is this ever used? │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Integer} │ │ │ │ + * {<OpenLayers.Layer.Grid>} An exact clone of this OpenLayers.Layer.Grid │ │ │ │ */ │ │ │ │ - Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ - var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER( │ │ │ │ - indexer, │ │ │ │ - newNode, │ │ │ │ - nextNode │ │ │ │ - ); │ │ │ │ + clone: function(obj) { │ │ │ │ │ │ │ │ - if (nextNode && returnVal === 0) { │ │ │ │ - var result = nextNode._boundsBottom - newNode._boundsBottom; │ │ │ │ - returnVal = (result === 0) ? 1 : result; │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.Grid(this.name, │ │ │ │ + this.url, │ │ │ │ + this.params, │ │ │ │ + this.getOptions()); │ │ │ │ } │ │ │ │ │ │ │ │ - return returnVal; │ │ │ │ - } │ │ │ │ -}; │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Renderer.Elements │ │ │ │ - * This is another virtual class in that it should never be instantiated by │ │ │ │ - * itself as a Renderer. It exists because there is *tons* of shared │ │ │ │ - * functionality between different vector libraries which use nodes/elements │ │ │ │ - * as a base for rendering vectors. │ │ │ │ - * │ │ │ │ - * The highlevel bits of code that are implemented here are the adding and │ │ │ │ - * removing of geometries, which is essentially the same for any │ │ │ │ - * element-based renderer. The details of creating each node and drawing the │ │ │ │ - * paths are of course different, but the machinery is the same. │ │ │ │ - * │ │ │ │ - * Inherits: │ │ │ │ - * - <OpenLayers.Renderer> │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, { │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + if (this.tileSize != null) { │ │ │ │ + obj.tileSize = this.tileSize.clone(); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: rendererRoot │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - rendererRoot: null, │ │ │ │ + // 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; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: root │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - root: null, │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: vectorRoot │ │ │ │ - * {DOMElement} │ │ │ │ + * Method: moveTo │ │ │ │ + * This function is called whenever the map is moved. All the moving │ │ │ │ + * of actual 'tiles' is done by the map, but moveTo's role is to accept │ │ │ │ + * a bounds and make sure the data that that bounds requires is pre-loaded. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * zoomChanged - {Boolean} │ │ │ │ + * dragging - {Boolean} │ │ │ │ */ │ │ │ │ - vectorRoot: null, │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: textRoot │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - textRoot: null, │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: xmlns │ │ │ │ - * {String} │ │ │ │ - */ │ │ │ │ - xmlns: null, │ │ │ │ + bounds = bounds || this.map.getExtent(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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, │ │ │ │ + if (bounds != null) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: rightOfDateLine │ │ │ │ - * {Boolean} Keeps track of the location of the map extent relative to the │ │ │ │ - * date line. The <setExtent> method compares this value (which is the one │ │ │ │ - * from the previous <setExtent> call) with the current position of the map │ │ │ │ - * extent relative to the date line and updates the xOffset when the extent │ │ │ │ - * has moved from one side of the date line to the other. │ │ │ │ - */ │ │ │ │ + // if grid is empty or zoom has changed, we *must* re-tile │ │ │ │ + var forceReTile = !this.grid.length || zoomChanged; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: Indexer │ │ │ │ - * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer │ │ │ │ - * created upon initialization if the zIndexing or yOrdering options │ │ │ │ - * passed to this renderer's constructor are set to true. │ │ │ │ - */ │ │ │ │ - indexer: null, │ │ │ │ + // total bounds of the tiles │ │ │ │ + var tilesBounds = this.getTilesBounds(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: BACKGROUND_ID_SUFFIX │ │ │ │ - * {String} │ │ │ │ - */ │ │ │ │ - BACKGROUND_ID_SUFFIX: "_background", │ │ │ │ + // the new map resolution │ │ │ │ + var resolution = this.map.getResolution(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: LABEL_ID_SUFFIX │ │ │ │ - * {String} │ │ │ │ - */ │ │ │ │ - LABEL_ID_SUFFIX: "_label", │ │ │ │ + // the server-supported resolution for the new map resolution │ │ │ │ + var serverResolution = this.getServerResolution(resolution); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constant: LABEL_OUTLINE_SUFFIX │ │ │ │ - * {String} │ │ │ │ - */ │ │ │ │ - LABEL_OUTLINE_SUFFIX: "_outline", │ │ │ │ + if (this.singleTile) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Renderer.Elements │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * containerID - {String} │ │ │ │ - * options - {Object} options for this renderer. │ │ │ │ - * │ │ │ │ - * Supported options are: │ │ │ │ - * yOrdering - {Boolean} Whether to use y-ordering │ │ │ │ - * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored │ │ │ │ - * if yOrdering is set to true. │ │ │ │ - */ │ │ │ │ - initialize: function(containerID, options) { │ │ │ │ - OpenLayers.Renderer.prototype.initialize.apply(this, arguments); │ │ │ │ + // 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) │ │ │ │ │ │ │ │ - this.rendererRoot = this.createRenderRoot(); │ │ │ │ - this.root = this.createRoot("_root"); │ │ │ │ - this.vectorRoot = this.createRoot("_vroot"); │ │ │ │ - this.textRoot = this.createRoot("_troot"); │ │ │ │ + if (forceReTile || │ │ │ │ + (!dragging && !tilesBounds.containsBounds(bounds))) { │ │ │ │ │ │ │ │ - this.root.appendChild(this.vectorRoot); │ │ │ │ - this.root.appendChild(this.textRoot); │ │ │ │ + // 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. │ │ │ │ │ │ │ │ - this.rendererRoot.appendChild(this.root); │ │ │ │ - this.container.appendChild(this.rendererRoot); │ │ │ │ + if (zoomChanged && this.transitionEffect !== 'resize') { │ │ │ │ + this.removeBackBuffer(); │ │ │ │ + } │ │ │ │ │ │ │ │ - if (options && (options.zIndexing || options.yOrdering)) { │ │ │ │ - this.indexer = new OpenLayers.ElementsIndexer(options.yOrdering); │ │ │ │ + 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: destroy │ │ │ │ + * Method: getTileData │ │ │ │ + * Given a map location, retrieve a tile and the pixel offset within that │ │ │ │ + * tile corresponding to the location. If there is not an existing │ │ │ │ + * tile in the grid that covers the given location, null will be │ │ │ │ + * returned. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * loc - {<OpenLayers.LonLat>} map location │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} Object with the following properties: tile ({<OpenLayers.Tile>}), │ │ │ │ + * i ({Number} x-pixel offset from top left), and j ({Integer} y-pixel │ │ │ │ + * offset from top left). │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - │ │ │ │ - this.clear(); │ │ │ │ + getTileData: function(loc) { │ │ │ │ + var data = null, │ │ │ │ + x = loc.lon, │ │ │ │ + y = loc.lat, │ │ │ │ + numRows = this.grid.length; │ │ │ │ │ │ │ │ - this.rendererRoot = null; │ │ │ │ - this.root = null; │ │ │ │ - this.xmlns = null; │ │ │ │ + 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; │ │ │ │ │ │ │ │ - OpenLayers.Renderer.prototype.destroy.apply(this, arguments); │ │ │ │ + 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: clear │ │ │ │ - * Remove all the elements from the root │ │ │ │ + * Method: destroyTile │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * tile - {<OpenLayers.Tile>} │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ - } │ │ │ │ + destroyTile: function(tile) { │ │ │ │ + this.removeTileMonitoringHooks(tile); │ │ │ │ + tile.destroy(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setExtent │ │ │ │ - * Set the visible part of the layer. │ │ │ │ + * Method: getServerResolution │ │ │ │ + * Return the closest server-supported resolution. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * extent - {<OpenLayers.Bounds>} │ │ │ │ - * resolutionChanged - {Boolean} │ │ │ │ + * resolution - {Number} The base resolution. If undefined the │ │ │ │ + * map resolution is used. │ │ │ │ * │ │ │ │ * 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. │ │ │ │ + * {Number} The closest server resolution value. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - this.rightOfDateLine = rightOfDateLine; │ │ │ │ + resolution = serverResolution; │ │ │ │ } │ │ │ │ - return coordSysUnchanged; │ │ │ │ + return resolution; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getNodeType │ │ │ │ - * This function is in charge of asking the specific renderer which type │ │ │ │ - * of node to create for the given geometry and style. All geometries │ │ │ │ - * in an Elements-based renderer consist of one node and some │ │ │ │ - * attributes. We have the nodeFactory() function which creates a node │ │ │ │ - * for us, but it takes a 'type' as input, and that is precisely what │ │ │ │ - * this function tells us. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: getServerZoom │ │ │ │ + * Return the zoom value corresponding to the best matching server │ │ │ │ + * resolution, taking into account <serverResolutions> and <zoomOffset>. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} The corresponding node type for the specified geometry │ │ │ │ + * {Number} The closest server supported zoom. This is not the map zoom │ │ │ │ + * level, but an index of the server's resolutions array. │ │ │ │ */ │ │ │ │ - getNodeType: function(geometry, style) {}, │ │ │ │ + getServerZoom: function() { │ │ │ │ + var resolution = this.getServerResolution(); │ │ │ │ + return this.serverResolutions ? │ │ │ │ + OpenLayers.Util.indexOf(this.serverResolutions, resolution) : │ │ │ │ + this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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. │ │ │ │ + /** │ │ │ │ + * Method: applyBackBuffer │ │ │ │ + * Create, insert, scale and position a back buffer for the layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {String} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} true if the geometry has been drawn completely; null if │ │ │ │ - * incomplete; false otherwise │ │ │ │ + * resolution - {Number} The resolution to transition to. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + applyBackBuffer: function(resolution) { │ │ │ │ + if (this.backBufferTimerId !== null) { │ │ │ │ + this.removeBackBuffer(); │ │ │ │ } │ │ │ │ - │ │ │ │ - rendered = false; │ │ │ │ - var removeBackground = false; │ │ │ │ - if (style.display != "none") { │ │ │ │ - if (style.backgroundGraphic) { │ │ │ │ - this.redrawBackgroundNode(geometry.id, geometry, style, │ │ │ │ - featureId); │ │ │ │ - } else { │ │ │ │ - removeBackground = true; │ │ │ │ + var backBuffer = this.backBuffer; │ │ │ │ + if (!backBuffer) { │ │ │ │ + backBuffer = this.createBackBuffer(); │ │ │ │ + if (!backBuffer) { │ │ │ │ + return; │ │ │ │ } │ │ │ │ - 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 (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; │ │ │ │ } │ │ │ │ - if (removeBackground) { │ │ │ │ - var node = document.getElementById( │ │ │ │ - geometry.id + this.BACKGROUND_ID_SUFFIX); │ │ │ │ - if (node) { │ │ │ │ - node.parentNode.removeChild(node); │ │ │ │ - } │ │ │ │ + │ │ │ │ + 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'; │ │ │ │ } │ │ │ │ - return rendered; │ │ │ │ + │ │ │ │ + // 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: redrawNode │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * id - {String} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {String} │ │ │ │ - * │ │ │ │ + * Method: createBackBuffer │ │ │ │ + * Create a back buffer. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} true if the complete geometry could be drawn, null if parts of │ │ │ │ - * the geometry could not be drawn, false otherwise │ │ │ │ + * {DOMElement} The DOM element for the back buffer, undefined if the │ │ │ │ + * grid isn't initialized yet. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + 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; │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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); │ │ │ │ + /** │ │ │ │ + * 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); │ │ │ │ } │ │ │ │ - } 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); │ │ │ │ + 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; │ │ │ │ } │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.postDraw(node); │ │ │ │ - │ │ │ │ - return drawResult.complete; │ │ │ │ + /** │ │ │ │ + * Method: moveByPx │ │ │ │ + * Move the layer based on pixel vector. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * dx - {Number} │ │ │ │ + * dy - {Number} │ │ │ │ + */ │ │ │ │ + moveByPx: function(dx, dy) { │ │ │ │ + if (!this.singleTile) { │ │ │ │ + this.moveGriddedTiles(); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * 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: │ │ │ │ - * id - {String} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * featureId - {String} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} true if the complete geometry could be drawn, null if parts of │ │ │ │ - * the geometry could not be drawn, false otherwise │ │ │ │ + * size - {<OpenLayers.Size>} │ │ │ │ */ │ │ │ │ - redrawBackgroundNode: function(id, geometry, style, featureId) { │ │ │ │ - var backgroundStyle = OpenLayers.Util.extend({}, style); │ │ │ │ + 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]); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // 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; │ │ │ │ + /** │ │ │ │ + * APIMethod: getTilesBounds │ │ │ │ + * Return the bounds of the tile grid. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} A Bounds object representing the bounds of all the │ │ │ │ + * currently loaded tiles (including those partially or not at all seen │ │ │ │ + * onscreen). │ │ │ │ + */ │ │ │ │ + getTilesBounds: function() { │ │ │ │ + var bounds = null; │ │ │ │ │ │ │ │ - // Erase background styles. │ │ │ │ - backgroundStyle.backgroundGraphic = null; │ │ │ │ - backgroundStyle.backgroundXOffset = null; │ │ │ │ - backgroundStyle.backgroundYOffset = null; │ │ │ │ - backgroundStyle.backgroundGraphicZIndex = 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(); │ │ │ │ │ │ │ │ - return this.redrawNode( │ │ │ │ - id + this.BACKGROUND_ID_SUFFIX, │ │ │ │ - geometry, │ │ │ │ - backgroundStyle, │ │ │ │ - null │ │ │ │ - ); │ │ │ │ + bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, │ │ │ │ + bottomLeftTileBounds.bottom, │ │ │ │ + bottomLeftTileBounds.left + width, │ │ │ │ + bottomLeftTileBounds.bottom + height); │ │ │ │ + } │ │ │ │ + return bounds; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawGeometryNode │ │ │ │ - * Given a node, draw a geometry on the specified layer. │ │ │ │ - * node and geometry are required arguments, style is optional. │ │ │ │ - * This method is only called by the render itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ + * Method: initSingleTile │ │ │ │ * │ │ │ │ - * 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) │ │ │ │ + * Parameters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - drawGeometryNode: function(node, geometry, style) { │ │ │ │ - style = style || node._style; │ │ │ │ + initSingleTile: function(bounds) { │ │ │ │ + this.events.triggerEvent("retile"); │ │ │ │ │ │ │ │ - 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; │ │ │ │ + //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] = []; │ │ │ │ } │ │ │ │ │ │ │ │ - node._options = options; │ │ │ │ + var tile = this.grid[0][0]; │ │ │ │ + if (!tile) { │ │ │ │ + tile = this.addTile(tileBounds, px); │ │ │ │ │ │ │ │ - //set style │ │ │ │ - //TBD simplify this │ │ │ │ - if (drawn != false) { │ │ │ │ - return { │ │ │ │ - node: this.setStyle(node, style, options, geometry), │ │ │ │ - complete: drawn │ │ │ │ - }; │ │ │ │ + this.addTileMonitoringHooks(tile); │ │ │ │ + tile.draw(); │ │ │ │ + this.grid[0][0] = tile; │ │ │ │ } else { │ │ │ │ - return false; │ │ │ │ + tile.moveTo(tileBounds, px); │ │ │ │ } │ │ │ │ + │ │ │ │ + //remove all but our single tile │ │ │ │ + this.removeExcessTiles(1, 1); │ │ │ │ + │ │ │ │ + // store the resolution of the grid │ │ │ │ + this.gridResolution = this.getServerResolution(); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: postDraw │ │ │ │ - * Things that have do be done after the geometry node is appended │ │ │ │ - * to its parent node. To be overridden by subclasses. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: calculateGridLayout │ │ │ │ + * Generate parameters for the grid layout. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - */ │ │ │ │ - postDraw: function(node) {}, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: drawPoint │ │ │ │ - * Virtual function for drawing Point Geometry. │ │ │ │ - * Should be implemented by subclasses. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ + * bounds - {<OpenLayers.Bound>|Object} OpenLayers.Bounds or an │ │ │ │ + * object with a 'left' and 'top' properties. │ │ │ │ + * origin - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an │ │ │ │ + * object with a 'lon' and 'lat' properties. │ │ │ │ + * resolution - {Number} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} or false if the renderer could not draw the point │ │ │ │ + * {Object} Object containing properties tilelon, tilelat, startcol, │ │ │ │ + * startrow │ │ │ │ */ │ │ │ │ - drawPoint: function(node, geometry) {}, │ │ │ │ + calculateGridLayout: function(bounds, origin, resolution) { │ │ │ │ + var tilelon = resolution * this.tileSize.w; │ │ │ │ + var tilelat = resolution * this.tileSize.h; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawLineString │ │ │ │ - * Virtual function for drawing LineString Geometry. │ │ │ │ - * Should be implemented by subclasses. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or null if the renderer could not draw all components of │ │ │ │ - * the linestring, or false if nothing could be drawn │ │ │ │ - */ │ │ │ │ - drawLineString: function(node, geometry) {}, │ │ │ │ + var offsetlon = bounds.left - origin.lon; │ │ │ │ + var tilecol = Math.floor(offsetlon / tilelon) - this.buffer; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawLinearRing │ │ │ │ - * Virtual function for drawing LinearRing Geometry. │ │ │ │ - * Should be implemented by subclasses. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or null if the renderer could not draw all components │ │ │ │ - * of the linear ring, or false if nothing could be drawn │ │ │ │ - */ │ │ │ │ - drawLinearRing: function(node, geometry) {}, │ │ │ │ + var rowSign = this.rowSign; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawPolygon │ │ │ │ - * Virtual function for drawing Polygon Geometry. │ │ │ │ - * Should be implemented by subclasses. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or null if the renderer could not draw all components │ │ │ │ - * of the polygon, or false if nothing could be drawn │ │ │ │ - */ │ │ │ │ - drawPolygon: function(node, geometry) {}, │ │ │ │ + var offsetlat = rowSign * (origin.lat - bounds.top + tilelat); │ │ │ │ + var tilerow = Math[~rowSign ? 'floor' : 'ceil'](offsetlat / tilelat) - this.buffer * rowSign; │ │ │ │ + │ │ │ │ + return { │ │ │ │ + tilelon: tilelon, │ │ │ │ + tilelat: tilelat, │ │ │ │ + startcol: tilecol, │ │ │ │ + startrow: tilerow │ │ │ │ + }; │ │ │ │ + │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawRectangle │ │ │ │ - * Virtual function for drawing Rectangle Geometry. │ │ │ │ - * Should be implemented by subclasses. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ + * Method: getTileOrigin │ │ │ │ + * Determine the origin for aligning the grid of tiles. If a <tileOrigin> │ │ │ │ + * property is supplied, that will be returned. Otherwise, the origin │ │ │ │ + * will be derived from the layer's <maxExtent> property. In this case, │ │ │ │ + * the tile origin will be the corner of the <maxExtent> given by the │ │ │ │ + * <tileOriginCorner> property. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} or false if the renderer could not draw the rectangle │ │ │ │ + * {<OpenLayers.LonLat>} The tile origin. │ │ │ │ */ │ │ │ │ - drawRectangle: function(node, geometry) {}, │ │ │ │ + getTileOrigin: function() { │ │ │ │ + var origin = this.tileOrigin; │ │ │ │ + if (!origin) { │ │ │ │ + var extent = this.getMaxExtent(); │ │ │ │ + var edges = ({ │ │ │ │ + "tl": ["left", "top"], │ │ │ │ + "tr": ["right", "top"], │ │ │ │ + "bl": ["left", "bottom"], │ │ │ │ + "br": ["right", "bottom"] │ │ │ │ + })[this.tileOriginCorner]; │ │ │ │ + origin = new OpenLayers.LonLat(extent[edges[0]], extent[edges[1]]); │ │ │ │ + } │ │ │ │ + return origin; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawCircle │ │ │ │ - * Virtual function for drawing Circle Geometry. │ │ │ │ - * Should be implemented by subclasses. │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ + * Method: getTileBoundsForGridIndex │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * row - {Number} The row of the grid │ │ │ │ + * col - {Number} The column of the grid │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} or false if the renderer could not draw the circle │ │ │ │ + * {<OpenLayers.Bounds>} The bounds for the tile at (row, col) │ │ │ │ */ │ │ │ │ - drawCircle: function(node, geometry) {}, │ │ │ │ + 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: removeText │ │ │ │ - * Removes a label │ │ │ │ + * Method: initGriddedTiles │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * featureId - {String} │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + initGriddedTiles: function(bounds) { │ │ │ │ + this.events.triggerEvent("retile"); │ │ │ │ + │ │ │ │ + // work out mininum number of rows and columns; this is the number of │ │ │ │ + // tiles required to cover the viewport plus at least one for panning │ │ │ │ + │ │ │ │ + var viewSize = this.map.getSize(); │ │ │ │ + │ │ │ │ + var origin = this.getTileOrigin(); │ │ │ │ + var resolution = this.map.getResolution(), │ │ │ │ + serverResolution = this.getServerResolution(), │ │ │ │ + ratio = resolution / serverResolution, │ │ │ │ + tileSize = { │ │ │ │ + w: this.tileSize.w / ratio, │ │ │ │ + h: this.tileSize.h / ratio │ │ │ │ + }; │ │ │ │ + │ │ │ │ + var minRows = Math.ceil(viewSize.h / tileSize.h) + │ │ │ │ + 2 * this.buffer + 1; │ │ │ │ + var minCols = Math.ceil(viewSize.w / tileSize.w) + │ │ │ │ + 2 * this.buffer + 1; │ │ │ │ + │ │ │ │ + var tileLayout = this.calculateGridLayout(bounds, origin, serverResolution); │ │ │ │ + this.gridLayout = tileLayout; │ │ │ │ + │ │ │ │ + var tilelon = tileLayout.tilelon; │ │ │ │ + var tilelat = tileLayout.tilelat; │ │ │ │ + │ │ │ │ + var layerContainerDivLeft = this.map.layerContainerOriginPx.x; │ │ │ │ + var layerContainerDivTop = this.map.layerContainerOriginPx.y; │ │ │ │ + │ │ │ │ + var tileBounds = this.getTileBoundsForGridIndex(0, 0); │ │ │ │ + var startPx = this.map.getViewPortPxFromLonLat( │ │ │ │ + new OpenLayers.LonLat(tileBounds.left, tileBounds.top) │ │ │ │ + ); │ │ │ │ + startPx.x = Math.round(startPx.x) - layerContainerDivLeft; │ │ │ │ + startPx.y = Math.round(startPx.y) - layerContainerDivTop; │ │ │ │ + │ │ │ │ + var tileData = [], │ │ │ │ + center = this.map.getCenter(); │ │ │ │ + │ │ │ │ + var rowidx = 0; │ │ │ │ + do { │ │ │ │ + var row = this.grid[rowidx]; │ │ │ │ + if (!row) { │ │ │ │ + row = []; │ │ │ │ + this.grid.push(row); │ │ │ │ + } │ │ │ │ + │ │ │ │ + var colidx = 0; │ │ │ │ + do { │ │ │ │ + tileBounds = this.getTileBoundsForGridIndex(rowidx, colidx); │ │ │ │ + var px = startPx.clone(); │ │ │ │ + px.x = px.x + colidx * Math.round(tileSize.w); │ │ │ │ + px.y = px.y + rowidx * Math.round(tileSize.h); │ │ │ │ + var tile = row[colidx]; │ │ │ │ + if (!tile) { │ │ │ │ + tile = this.addTile(tileBounds, px); │ │ │ │ + this.addTileMonitoringHooks(tile); │ │ │ │ + row.push(tile); │ │ │ │ + } else { │ │ │ │ + tile.moveTo(tileBounds, px, false); │ │ │ │ + } │ │ │ │ + var tileCenter = tileBounds.getCenterLonLat(); │ │ │ │ + tileData.push({ │ │ │ │ + tile: tile, │ │ │ │ + distance: Math.pow(tileCenter.lon - center.lon, 2) + │ │ │ │ + Math.pow(tileCenter.lat - center.lat, 2) │ │ │ │ + }); │ │ │ │ + │ │ │ │ + colidx += 1; │ │ │ │ + } while ((tileBounds.right <= bounds.right + tilelon * this.buffer) || │ │ │ │ + colidx < minCols); │ │ │ │ + │ │ │ │ + rowidx += 1; │ │ │ │ + } while ((tileBounds.bottom >= bounds.bottom - tilelat * this.buffer) || │ │ │ │ + rowidx < minRows); │ │ │ │ + │ │ │ │ + //shave off exceess rows and colums │ │ │ │ + this.removeExcessTiles(rowidx, colidx); │ │ │ │ + │ │ │ │ + var resolution = this.getServerResolution(); │ │ │ │ + // store the resolution of the grid │ │ │ │ + this.gridResolution = resolution; │ │ │ │ + │ │ │ │ + //now actually draw the tiles │ │ │ │ + tileData.sort(function(a, b) { │ │ │ │ + return a.distance - b.distance; │ │ │ │ + }); │ │ │ │ + for (var i = 0, ii = tileData.length; i < ii; ++i) { │ │ │ │ + tileData[i].tile.draw(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getFeatureIdFromEvent │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Object} An <OpenLayers.Event> object │ │ │ │ + * Method: getMaxExtent │ │ │ │ + * Get this layer's maximum extent. (Implemented as a getter for │ │ │ │ + * potential specific implementations in sub-classes.) │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A feature id or undefined. │ │ │ │ + * {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - getFeatureIdFromEvent: function(evt) { │ │ │ │ - var target = evt.target; │ │ │ │ - var useElement = target && target.correspondingUseElement; │ │ │ │ - var node = useElement ? useElement : (target || evt.srcElement); │ │ │ │ - return node._featureId; │ │ │ │ + getMaxExtent: function() { │ │ │ │ + return this.maxExtent; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: addTile │ │ │ │ + * Create a tile, initialize it, and add it to the layer div. │ │ │ │ + * │ │ │ │ + * Parameters │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * position - {<OpenLayers.Pixel>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Tile>} The added OpenLayers.Tile │ │ │ │ + */ │ │ │ │ + addTile: function(bounds, position) { │ │ │ │ + var tile = new this.tileClass( │ │ │ │ + this, position, bounds, null, this.tileSize, this.tileOptions │ │ │ │ + ); │ │ │ │ + this.events.triggerEvent("addtile", { │ │ │ │ + tile: tile │ │ │ │ + }); │ │ │ │ + return tile; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: 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. │ │ │ │ + * 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: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * featureId - {String} │ │ │ │ + * Parameters: │ │ │ │ + * tile - {<OpenLayers.Tile>} │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + 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"); │ │ │ │ } │ │ │ │ - } 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); │ │ │ │ + this.events.triggerEvent("tileloadstart", { │ │ │ │ + tile: tile │ │ │ │ + }); │ │ │ │ + this.numLoadingTiles++; │ │ │ │ + if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ + OpenLayers.Element.addClass(tile.getTile(), replacingCls); │ │ │ │ + } │ │ │ │ + }; │ │ │ │ │ │ │ │ - if (this.indexer) { │ │ │ │ - this.indexer.remove(element); │ │ │ │ + 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); │ │ │ │ + } │ │ │ │ } │ │ │ │ - │ │ │ │ - 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); │ │ │ │ + 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: 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. │ │ │ │ + * Method: removeTileMonitoringHooks │ │ │ │ + * This function takes a tile as input and removes the tile hooks │ │ │ │ + * that were added in addTileMonitoringHooks() │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A new node of the given type and id. │ │ │ │ + * Parameters: │ │ │ │ + * tile - {<OpenLayers.Tile>} │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - node = this.createNode(type, id); │ │ │ │ } │ │ │ │ - return node; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: nodeTypeCompare │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: shiftRow │ │ │ │ + * Shifty grid work │ │ │ │ + * │ │ │ │ * 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. │ │ │ │ + * prepend - {Boolean} if true, prepend to beginning. │ │ │ │ + * if false, then append to end │ │ │ │ + * tileSize - {Object} rendered tile size; object with w and h properties │ │ │ │ */ │ │ │ │ - nodeTypeCompare: function(node, type) {}, │ │ │ │ + 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; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: createNode │ │ │ │ - * │ │ │ │ + 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: │ │ │ │ - * 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. │ │ │ │ + * prepend - {Boolean} if true, prepend to beginning. │ │ │ │ + * if false, then append to end │ │ │ │ + * tileSize - {Object} rendered tile size; object with w and h properties │ │ │ │ */ │ │ │ │ - createNode: function(type, id) {}, │ │ │ │ + shiftColumn: function(prepend, tileSize) { │ │ │ │ + var grid = this.grid; │ │ │ │ + var colIndex = prepend ? 0 : (grid[0].length - 1); │ │ │ │ + var sign = prepend ? -1 : 1; │ │ │ │ + var tileLayout = this.gridLayout; │ │ │ │ + tileLayout.startcol += sign; │ │ │ │ + │ │ │ │ + for (var i = 0, len = grid.length; i < len; i++) { │ │ │ │ + var row = grid[i]; │ │ │ │ + var position = row[colIndex].position.clone(); │ │ │ │ + var tile = row[prepend ? 'pop' : 'shift'](); │ │ │ │ + position.x += tileSize.w * sign; │ │ │ │ + tile.moveTo(this.getTileBoundsForGridIndex(i, colIndex), position); │ │ │ │ + row[prepend ? 'unshift' : 'push'](tile); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveRoot │ │ │ │ - * moves this renderer's root to a different renderer. │ │ │ │ + * Method: removeExcessTiles │ │ │ │ + * When the size of the map or the buffer changes, we may need to │ │ │ │ + * remove some excess rows and columns. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * renderer - {<OpenLayers.Renderer>} target renderer for the moved root │ │ │ │ + * rows - {Integer} Maximum number of rows we want our grid to have. │ │ │ │ + * columns - {Integer} Maximum number of columns we want our grid to have. │ │ │ │ */ │ │ │ │ - moveRoot: function(renderer) { │ │ │ │ - var root = this.root; │ │ │ │ - if (renderer.root.parentNode == this.rendererRoot) { │ │ │ │ - root = renderer.root; │ │ │ │ + removeExcessTiles: function(rows, columns) { │ │ │ │ + var i, l; │ │ │ │ + │ │ │ │ + // remove extra rows │ │ │ │ + while (this.grid.length > rows) { │ │ │ │ + var row = this.grid.pop(); │ │ │ │ + for (i = 0, l = row.length; i < l; i++) { │ │ │ │ + var tile = row[i]; │ │ │ │ + this.destroyTile(tile); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + // remove extra columns │ │ │ │ + for (i = 0, l = this.grid.length; i < l; i++) { │ │ │ │ + while (this.grid[i].length > columns) { │ │ │ │ + var row = this.grid[i]; │ │ │ │ + var tile = row.pop(); │ │ │ │ + this.destroyTile(tile); │ │ │ │ + } │ │ │ │ } │ │ │ │ - root.parentNode.removeChild(root); │ │ │ │ - renderer.rendererRoot.appendChild(root); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getRenderLayerId │ │ │ │ - * Gets the layer that this renderer's output appears on. If moveRoot was │ │ │ │ - * used, this will be different from the id of the layer containing the │ │ │ │ - * features rendered by this renderer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} the id of the output layer. │ │ │ │ + * Method: onMapResize │ │ │ │ + * For singleTile layers, this will set a new tile size according to the │ │ │ │ + * dimensions of the map pane. │ │ │ │ */ │ │ │ │ - getRenderLayerId: function() { │ │ │ │ - return this.root.parentNode.parentNode.id; │ │ │ │ + onMapResize: function() { │ │ │ │ + if (this.singleTile) { │ │ │ │ + this.clearGrid(); │ │ │ │ + this.setTileSize(); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: isComplexSymbol │ │ │ │ - * Determines if a symbol cannot be rendered using drawCircle │ │ │ │ - * │ │ │ │ + * APIMethod: getTileBounds │ │ │ │ + * Returns The tile bounds for a layer given a pixel location. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * graphicName - {String} │ │ │ │ - * │ │ │ │ - * Returns │ │ │ │ - * {Boolean} true if the symbol is complex, false if not │ │ │ │ + * viewPortPx - {<OpenLayers.Pixel>} The location in the viewport. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} Bounds of the tile at the given pixel location. │ │ │ │ */ │ │ │ │ - isComplexSymbol: function(graphicName) { │ │ │ │ - return (graphicName != "circle") && !!graphicName; │ │ │ │ + getTileBounds: function(viewPortPx) { │ │ │ │ + var maxExtent = this.maxExtent; │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var tileMapWidth = resolution * this.tileSize.w; │ │ │ │ + var tileMapHeight = resolution * this.tileSize.h; │ │ │ │ + var mapPoint = this.getLonLatFromViewPortPx(viewPortPx); │ │ │ │ + var tileLeft = maxExtent.left + (tileMapWidth * │ │ │ │ + Math.floor((mapPoint.lon - │ │ │ │ + maxExtent.left) / │ │ │ │ + tileMapWidth)); │ │ │ │ + var tileBottom = maxExtent.bottom + (tileMapHeight * │ │ │ │ + Math.floor((mapPoint.lat - │ │ │ │ + maxExtent.bottom) / │ │ │ │ + tileMapHeight)); │ │ │ │ + return new OpenLayers.Bounds(tileLeft, tileBottom, │ │ │ │ + tileLeft + tileMapWidth, │ │ │ │ + tileBottom + tileMapHeight); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer.Elements" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Grid" │ │ │ │ }); │ │ │ │ - │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Renderer/SVG.js │ │ │ │ + OpenLayers/Layer/XYZ.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Renderer/Elements.js │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Renderer.SVG │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.XYZ │ │ │ │ + * The XYZ class is designed to make it easier for people who have tiles │ │ │ │ + * arranged by a standard XYZ grid. │ │ │ │ * │ │ │ │ - * Inherits: │ │ │ │ - * - <OpenLayers.Renderer.Elements> │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ */ │ │ │ │ -OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: xmlns │ │ │ │ - * {String} │ │ │ │ - */ │ │ │ │ - xmlns: "http://www.w3.org/2000/svg", │ │ │ │ +OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: xlinkns │ │ │ │ - * {String} │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * Default is true, as this is designed to be a base tile source. │ │ │ │ */ │ │ │ │ - xlinkns: "http://www.w3.org/1999/xlink", │ │ │ │ + isBaseLayer: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIProperty: sphericalMercator │ │ │ │ + * Whether the tile extents should be set to the defaults for │ │ │ │ + * spherical mercator. Useful for things like OpenStreetMap. │ │ │ │ + * Default is false, except for the OSM subclass. │ │ │ │ */ │ │ │ │ - MAX_PIXEL: 15000, │ │ │ │ + sphericalMercator: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: translationParameters │ │ │ │ - * {Object} Hash with "x" and "y" properties │ │ │ │ + * APIProperty: zoomOffset │ │ │ │ + * {Number} If your cache has more zoom levels than you want to provide │ │ │ │ + * access to with this layer, supply a zoomOffset. This zoom offset │ │ │ │ + * is added to the current map zoom level to determine the level │ │ │ │ + * for a requested tile. For example, if you supply a zoomOffset │ │ │ │ + * of 3, when the map is at the zoom 0, tiles will be requested from │ │ │ │ + * level 3 of your cache. Default is 0 (assumes cache level and map │ │ │ │ + * zoom are equivalent). Using <zoomOffset> is an alternative to │ │ │ │ + * setting <serverResolutions> if you only want to expose a subset │ │ │ │ + * of the server resolutions. │ │ │ │ */ │ │ │ │ - translationParameters: null, │ │ │ │ + zoomOffset: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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]. │ │ │ │ + * APIProperty: serverResolutions │ │ │ │ + * {Array} A list of all resolutions available on the server. Only set this │ │ │ │ + * property if the map resolutions differ from the server. This │ │ │ │ + * property serves two purposes. (a) <serverResolutions> can include │ │ │ │ + * resolutions that the server supports and that you don't want to │ │ │ │ + * provide with this layer; you can also look at <zoomOffset>, which is │ │ │ │ + * an alternative to <serverResolutions> for that specific purpose. │ │ │ │ + * (b) The map can work with resolutions that aren't supported by │ │ │ │ + * the server, i.e. that aren't in <serverResolutions>. When the │ │ │ │ + * map is displayed in such a resolution data for the closest │ │ │ │ + * server-supported resolution is loaded and the layer div is │ │ │ │ + * stretched as necessary. │ │ │ │ */ │ │ │ │ - symbolMetrics: null, │ │ │ │ + serverResolutions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Renderer.SVG │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Layer.XYZ │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * containerID - {String} │ │ │ │ + * name - {String} │ │ │ │ + * url - {String} │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ */ │ │ │ │ - initialize: function(containerID) { │ │ │ │ - if (!this.supported()) { │ │ │ │ - return; │ │ │ │ + initialize: function(name, url, options) { │ │ │ │ + if (options && options.sphericalMercator || this.sphericalMercator) { │ │ │ │ + options = OpenLayers.Util.extend({ │ │ │ │ + projection: "EPSG:900913", │ │ │ │ + numZoomLevels: 19 │ │ │ │ + }, options); │ │ │ │ } │ │ │ │ - OpenLayers.Renderer.Elements.prototype.initialize.apply(this, │ │ │ │ - arguments); │ │ │ │ - this.translationParameters = { │ │ │ │ - x: 0, │ │ │ │ - y: 0 │ │ │ │ - }; │ │ │ │ - │ │ │ │ - this.symbolMetrics = {}; │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, [ │ │ │ │ + name || this.name, url || this.url, {}, │ │ │ │ + options │ │ │ │ + ]); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: supported │ │ │ │ + * APIMethod: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * obj - {Object} Is this ever used? │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Whether or not the browser supports the SVG renderer │ │ │ │ + * {<OpenLayers.Layer.XYZ>} An exact clone of this OpenLayers.Layer.XYZ │ │ │ │ */ │ │ │ │ - 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"))); │ │ │ │ + clone: function(obj) { │ │ │ │ + │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.XYZ(this.name, │ │ │ │ + this.url, │ │ │ │ + this.getOptions()); │ │ │ │ + } │ │ │ │ + │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: inValidRange │ │ │ │ - * See #669 for more information │ │ │ │ + * Method: getURL │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * x - {Integer} │ │ │ │ - * y - {Integer} │ │ │ │ - * xyOnly - {Boolean} whether or not to just check for x and y, which means │ │ │ │ - * to not take the current translation parameters into account if true. │ │ │ │ - * │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} Whether or not the 'x' and 'y' coordinates are in the │ │ │ │ - * valid range. │ │ │ │ + * {String} A string with the layer's url and parameters and also the │ │ │ │ + * passed-in bounds and appropriate tile size specified as │ │ │ │ + * parameters │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + getURL: function(bounds) { │ │ │ │ + var xyz = this.getXYZ(bounds); │ │ │ │ + var url = this.url; │ │ │ │ + if (OpenLayers.Util.isArray(url)) { │ │ │ │ + var s = '' + xyz.x + xyz.y + xyz.z; │ │ │ │ + url = this.selectUrl(s, url); │ │ │ │ + } │ │ │ │ + │ │ │ │ + return OpenLayers.String.format(url, xyz); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setExtent │ │ │ │ - * │ │ │ │ + * Method: getXYZ │ │ │ │ + * Calculates x, y and z for the given bounds. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * extent - {<OpenLayers.Bounds>} │ │ │ │ - * resolutionChanged - {Boolean} │ │ │ │ - * │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * │ │ │ │ * 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. │ │ │ │ + * {Object} - an object with x, y and z properties. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + getXYZ: function(bounds) { │ │ │ │ + var res = this.getServerResolution(); │ │ │ │ + var x = Math.round((bounds.left - this.maxExtent.left) / │ │ │ │ + (res * this.tileSize.w)); │ │ │ │ + var y = Math.round((this.maxExtent.top - bounds.top) / │ │ │ │ + (res * this.tileSize.h)); │ │ │ │ + var z = this.getServerZoom(); │ │ │ │ │ │ │ │ - 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; │ │ │ │ + if (this.wrapDateLine) { │ │ │ │ + var limit = Math.pow(2, z); │ │ │ │ + x = ((x % limit) + limit) % limit; │ │ │ │ } │ │ │ │ + │ │ │ │ + return { │ │ │ │ + 'x': x, │ │ │ │ + 'y': y, │ │ │ │ + 'z': z │ │ │ │ + }; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: translate │ │ │ │ - * Transforms the SVG coordinate system │ │ │ │ + /* APIMethod: setMap │ │ │ │ + * When the layer is added to a map, then we can fetch our origin │ │ │ │ + * (if we don't have one.) │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * x - {Float} │ │ │ │ - * y - {Float} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} true if the translation parameters are in the valid coordinates │ │ │ │ - * range, false otherwise. │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ + if (!this.tileOrigin) { │ │ │ │ + this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, │ │ │ │ + this.maxExtent.bottom); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.XYZ" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/OSM.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/XYZ.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.OSM │ │ │ │ + * This layer allows accessing OpenStreetMap tiles. By default the OpenStreetMap │ │ │ │ + * hosted tile.openstreetmap.org Mapnik tileset is used. If you wish to use │ │ │ │ + * a different layer instead, you need to provide a different │ │ │ │ + * URL to the constructor. Here's an example for using OpenCycleMap: │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * new OpenLayers.Layer.OSM("OpenCycleMap", │ │ │ │ + * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ + * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ + * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.XYZ> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: setSize │ │ │ │ - * Sets the size of the drawing surface. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * size - {<OpenLayers.Size>} The size of the drawing surface │ │ │ │ + * APIProperty: name │ │ │ │ + * {String} The layer name. Defaults to "OpenStreetMap" if the first │ │ │ │ + * argument to the constructor is null or undefined. │ │ │ │ */ │ │ │ │ - setSize: function(size) { │ │ │ │ - OpenLayers.Renderer.prototype.setSize.apply(this, arguments); │ │ │ │ + name: "OpenStreetMap", │ │ │ │ │ │ │ │ - this.rendererRoot.setAttributeNS(null, "width", this.size.w); │ │ │ │ - this.rendererRoot.setAttributeNS(null, "height", this.size.h); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: url │ │ │ │ + * {String} The tileset URL scheme. Defaults to │ │ │ │ + * : http://[a|b|c].tile.openstreetmap.org/${z}/${x}/${y}.png │ │ │ │ + * (the official OSM tileset) if the second argument to the constructor │ │ │ │ + * is null or undefined. To use another tileset you can have something │ │ │ │ + * like this: │ │ │ │ + * (code) │ │ │ │ + * new OpenLayers.Layer.OSM("OpenCycleMap", │ │ │ │ + * ["http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ + * "http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png", │ │ │ │ + * "http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"]); │ │ │ │ + * (end) │ │ │ │ + */ │ │ │ │ + url: [ │ │ │ │ + 'http://a.tile.openstreetmap.org/${z}/${x}/${y}.png', │ │ │ │ + 'http://b.tile.openstreetmap.org/${z}/${x}/${y}.png', │ │ │ │ + 'http://c.tile.openstreetmap.org/${z}/${x}/${y}.png' │ │ │ │ + ], │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getNodeType │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The corresponding node type for the specified geometry │ │ │ │ + /** │ │ │ │ + * Property: attribution │ │ │ │ + * {String} The layer attribution. │ │ │ │ */ │ │ │ │ - getNodeType: function(geometry, style) { │ │ │ │ - var nodeType = null; │ │ │ │ - switch (geometry.CLASS_NAME) { │ │ │ │ - case "OpenLayers.Geometry.Point": │ │ │ │ - if (style.externalGraphic) { │ │ │ │ - nodeType = "image"; │ │ │ │ - } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ - nodeType = "svg"; │ │ │ │ - } else { │ │ │ │ - nodeType = "circle"; │ │ │ │ - } │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.Rectangle": │ │ │ │ - nodeType = "rect"; │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.LineString": │ │ │ │ - nodeType = "polyline"; │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.LinearRing": │ │ │ │ - nodeType = "polygon"; │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.Polygon": │ │ │ │ - case "OpenLayers.Geometry.Curve": │ │ │ │ - nodeType = "path"; │ │ │ │ - break; │ │ │ │ - default: │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - return nodeType; │ │ │ │ - }, │ │ │ │ + attribution: "© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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 │ │ │ │ + /** │ │ │ │ + * Property: sphericalMercator │ │ │ │ + * {Boolean} │ │ │ │ + */ │ │ │ │ + sphericalMercator: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: wrapDateLine │ │ │ │ + * {Boolean} │ │ │ │ + */ │ │ │ │ + wrapDateLine: true, │ │ │ │ + │ │ │ │ + /** APIProperty: tileOptions │ │ │ │ + * {Object} optional configuration options for <OpenLayers.Tile> instances │ │ │ │ + * created by this Layer. Default is │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * {crossOriginKeyword: 'anonymous'} │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * When using OSM tilesets other than the default ones, it may be │ │ │ │ + * necessary to set this to │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * {crossOriginKeyword: null} │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * if the server does not send Access-Control-Allow-Origin headers. │ │ │ │ + */ │ │ │ │ + tileOptions: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.OSM │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {SVGDomElement} An SVG element to decorate │ │ │ │ - * style - {Object} │ │ │ │ - * options - {Object} Currently supported options include │ │ │ │ - * 'isFilled' {Boolean} and │ │ │ │ - * 'isStroked' {Boolean} │ │ │ │ + * name - {String} The layer name. │ │ │ │ + * url - {String} The tileset URL scheme. │ │ │ │ + * options - {Object} Configuration options for the layer. Any inherited │ │ │ │ + * layer option can be set in this object (e.g. │ │ │ │ + * <OpenLayers.Layer.Grid.buffer>). │ │ │ │ */ │ │ │ │ - setStyle: function(node, style, options) { │ │ │ │ - style = style || node._style; │ │ │ │ - options = options || node._options; │ │ │ │ + initialize: function(name, url, options) { │ │ │ │ + OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); │ │ │ │ + this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ + crossOriginKeyword: 'anonymous' │ │ │ │ + }, this.options && this.options.tileOptions); │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: clone │ │ │ │ + */ │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.OSM( │ │ │ │ + this.name, this.url, this.getOptions()); │ │ │ │ } │ │ │ │ + obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ + return obj; │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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_NAME: "OpenLayers.Layer.OSM" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/Bing.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - 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; │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/XYZ.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); │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.Bing │ │ │ │ + * Bing layer using direct tile access as provided by Bing Maps REST Services. │ │ │ │ + * See http://msdn.microsoft.com/en-us/library/ff701713.aspx for more │ │ │ │ + * information. Note: Terms of Service compliant use requires the map to be │ │ │ │ + * configured with an <OpenLayers.Control.Attribution> control and the │ │ │ │ + * attribution placed on or near the map. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.XYZ> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ │ │ │ │ - // 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")); │ │ │ │ + /** │ │ │ │ + * Property: key │ │ │ │ + * {String} API key for Bing maps, get your own key │ │ │ │ + * at http://bingmapsportal.com/ . │ │ │ │ + */ │ │ │ │ + key: null, │ │ │ │ │ │ │ │ - node.setAttributeNS(null, "width", size); │ │ │ │ - node.setAttributeNS(null, "height", size); │ │ │ │ - node.setAttributeNS(null, "x", pos.x - offset); │ │ │ │ - node.setAttributeNS(null, "y", pos.y - offset); │ │ │ │ + /** │ │ │ │ + * Property: serverResolutions │ │ │ │ + * {Array} the resolutions provided by the Bing servers. │ │ │ │ + */ │ │ │ │ + serverResolutions: [ │ │ │ │ + 156543.03390625, 78271.516953125, 39135.7584765625, │ │ │ │ + 19567.87923828125, 9783.939619140625, 4891.9698095703125, │ │ │ │ + 2445.9849047851562, 1222.9924523925781, 611.4962261962891, │ │ │ │ + 305.74811309814453, 152.87405654907226, 76.43702827453613, │ │ │ │ + 38.218514137268066, 19.109257068634033, 9.554628534317017, │ │ │ │ + 4.777314267158508, 2.388657133579254, 1.194328566789627, │ │ │ │ + 0.5971642833948135, 0.29858214169740677, 0.14929107084870338, │ │ │ │ + 0.07464553542435169 │ │ │ │ + ], │ │ │ │ │ │ │ │ - // 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); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: attributionTemplate │ │ │ │ + * {String} │ │ │ │ + */ │ │ │ │ + attributionTemplate: '<span class="olBingAttribution ${type}">' + │ │ │ │ + '<div><a target="_blank" href="http://www.bing.com/maps/">' + │ │ │ │ + '<img src="${logo}" /></a></div>${copyrights}' + │ │ │ │ + '<a style="white-space: nowrap" target="_blank" ' + │ │ │ │ + 'href="http://www.microsoft.com/maps/product/terms.html">' + │ │ │ │ + 'Terms of Use</a></span>', │ │ │ │ │ │ │ │ - var rotation = style.rotation; │ │ │ │ + /** │ │ │ │ + * Property: metadata │ │ │ │ + * {Object} Metadata for this layer, as returned by the callback script │ │ │ │ + */ │ │ │ │ + metadata: 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: protocolRegex │ │ │ │ + * {RegExp} Regular expression to match and replace http: in bing urls │ │ │ │ + */ │ │ │ │ + protocolRegex: /^http:/i, │ │ │ │ │ │ │ │ - if (options.isFilled) { │ │ │ │ - node.setAttributeNS(null, "fill", style.fillColor); │ │ │ │ - node.setAttributeNS(null, "fill-opacity", style.fillOpacity); │ │ │ │ - } else { │ │ │ │ - node.setAttributeNS(null, "fill", "none"); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: type │ │ │ │ + * {String} The layer identifier. Any non-birdseye imageryType │ │ │ │ + * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be │ │ │ │ + * used. Default is "Road". │ │ │ │ + */ │ │ │ │ + type: "Road", │ │ │ │ │ │ │ │ - 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"); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: culture │ │ │ │ + * {String} The culture identifier. See http://msdn.microsoft.com/en-us/library/ff701709.aspx │ │ │ │ + * for the definition and the possible values. Default is "en-US". │ │ │ │ + */ │ │ │ │ + culture: "en-US", │ │ │ │ │ │ │ │ - if (style.pointerEvents) { │ │ │ │ - node.setAttributeNS(null, "pointer-events", style.pointerEvents); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIProperty: metadataParams │ │ │ │ + * {Object} Optional url parameters for the Get Imagery Metadata request │ │ │ │ + * as described here: http://msdn.microsoft.com/en-us/library/ff701716.aspx │ │ │ │ + */ │ │ │ │ + metadataParams: null, │ │ │ │ │ │ │ │ - if (style.cursor != null) { │ │ │ │ - node.setAttributeNS(null, "cursor", style.cursor); │ │ │ │ - } │ │ │ │ + /** APIProperty: tileOptions │ │ │ │ + * {Object} optional configuration options for <OpenLayers.Tile> instances │ │ │ │ + * created by this Layer. Default is │ │ │ │ + * │ │ │ │ + * (code) │ │ │ │ + * {crossOriginKeyword: 'anonymous'} │ │ │ │ + * (end) │ │ │ │ + */ │ │ │ │ + tileOptions: null, │ │ │ │ + │ │ │ │ + /** APIProperty: protocol │ │ │ │ + * {String} Protocol to use to fetch Imagery Metadata, tiles and bing logo │ │ │ │ + * Can be 'http:' 'https:' or '' │ │ │ │ + * │ │ │ │ + * Warning: tiles may not be available under both HTTP and HTTPS protocols. │ │ │ │ + * Microsoft approved use of both HTTP and HTTPS urls for tiles. However │ │ │ │ + * this is undocumented and the Imagery Metadata API always returns HTTP │ │ │ │ + * urls. │ │ │ │ + * │ │ │ │ + * Default is '', unless when executed from a file:/// uri, in which case │ │ │ │ + * it is 'http:'. │ │ │ │ + */ │ │ │ │ + protocol: ~window.location.href.indexOf('http') ? '' : 'http:', │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Bing │ │ │ │ + * Create a new Bing layer. │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * (code) │ │ │ │ + * var road = new OpenLayers.Layer.Bing({ │ │ │ │ + * name: "My Bing Aerial Layer", │ │ │ │ + * type: "Aerial", │ │ │ │ + * key: "my-api-key-here", │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} Configuration properties for the layer. │ │ │ │ + * │ │ │ │ + * Required configuration properties: │ │ │ │ + * key - {String} Bing Maps API key for your application. Get one at │ │ │ │ + * http://bingmapsportal.com/. │ │ │ │ + * type - {String} The layer identifier. Any non-birdseye imageryType │ │ │ │ + * from http://msdn.microsoft.com/en-us/library/ff701716.aspx can be │ │ │ │ + * used. │ │ │ │ + * │ │ │ │ + * Any other documented layer properties can be provided in the config object. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults({ │ │ │ │ + sphericalMercator: true │ │ │ │ + }, options); │ │ │ │ + var name = options.name || "Bing " + (options.type || this.type); │ │ │ │ + │ │ │ │ + var newArgs = [name, null, options]; │ │ │ │ + OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs); │ │ │ │ + this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ + crossOriginKeyword: 'anonymous' │ │ │ │ + }, this.options.tileOptions); │ │ │ │ + this.loadMetadata(); │ │ │ │ + }, │ │ │ │ │ │ │ │ - return node; │ │ │ │ + /** │ │ │ │ + * Method: loadMetadata │ │ │ │ + */ │ │ │ │ + loadMetadata: function() { │ │ │ │ + this._callbackId = "_callback_" + this.id.replace(/\./g, "_"); │ │ │ │ + // link the processMetadata method to the global scope and bind it │ │ │ │ + // to this instance │ │ │ │ + window[this._callbackId] = OpenLayers.Function.bind( │ │ │ │ + OpenLayers.Layer.Bing.processMetadata, this │ │ │ │ + ); │ │ │ │ + var params = OpenLayers.Util.applyDefaults({ │ │ │ │ + key: this.key, │ │ │ │ + jsonp: this._callbackId, │ │ │ │ + include: "ImageryProviders" │ │ │ │ + }, this.metadataParams); │ │ │ │ + var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" + │ │ │ │ + this.type + "?" + OpenLayers.Util.getParameterString(params); │ │ │ │ + var script = document.createElement("script"); │ │ │ │ + script.type = "text/javascript"; │ │ │ │ + script.src = url; │ │ │ │ + script.id = this._callbackId; │ │ │ │ + document.getElementsByTagName("head")[0].appendChild(script); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: dashStyle │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * style - {Object} │ │ │ │ - * widthFactor - {Number} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A SVG compliant 'stroke-dasharray' value │ │ │ │ + /** │ │ │ │ + * Method: initLayer │ │ │ │ + * │ │ │ │ + * Sets layer properties according to the metadata provided by the API │ │ │ │ */ │ │ │ │ - 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, ","); │ │ │ │ + initLayer: function() { │ │ │ │ + var res = this.metadata.resourceSets[0].resources[0]; │ │ │ │ + var url = res.imageUrl.replace("{quadkey}", "${quadkey}"); │ │ │ │ + url = url.replace("{culture}", this.culture); │ │ │ │ + url = url.replace(this.protocolRegex, this.protocol); │ │ │ │ + this.url = []; │ │ │ │ + for (var i = 0; i < res.imageUrlSubdomains.length; ++i) { │ │ │ │ + this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i])); │ │ │ │ + } │ │ │ │ + this.addOptions({ │ │ │ │ + maxResolution: Math.min( │ │ │ │ + this.serverResolutions[res.zoomMin], │ │ │ │ + this.maxResolution || Number.POSITIVE_INFINITY │ │ │ │ + ), │ │ │ │ + numZoomLevels: Math.min( │ │ │ │ + res.zoomMax + 1 - res.zoomMin, this.numZoomLevels │ │ │ │ + ) │ │ │ │ + }, true); │ │ │ │ + if (!this.isBaseLayer) { │ │ │ │ + this.redraw(); │ │ │ │ } │ │ │ │ + this.updateAttribution(); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: createNode │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * type - {String} Kind of node to draw │ │ │ │ - * id - {String} Id for node │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A new node of the given type and id │ │ │ │ + /** │ │ │ │ + * Method: getURL │ │ │ │ + * │ │ │ │ + * Paramters: │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - createNode: function(type, id) { │ │ │ │ - var node = document.createElementNS(this.xmlns, type); │ │ │ │ - if (id) { │ │ │ │ - node.setAttributeNS(null, "id", id); │ │ │ │ + getURL: function(bounds) { │ │ │ │ + if (!this.url) { │ │ │ │ + return; │ │ │ │ } │ │ │ │ - return node; │ │ │ │ + var xyz = this.getXYZ(bounds), │ │ │ │ + x = xyz.x, │ │ │ │ + y = xyz.y, │ │ │ │ + z = xyz.z; │ │ │ │ + var quadDigits = []; │ │ │ │ + for (var i = z; i > 0; --i) { │ │ │ │ + var digit = '0'; │ │ │ │ + var mask = 1 << (i - 1); │ │ │ │ + if ((x & mask) != 0) { │ │ │ │ + digit++; │ │ │ │ + } │ │ │ │ + if ((y & mask) != 0) { │ │ │ │ + digit++; │ │ │ │ + digit++; │ │ │ │ + } │ │ │ │ + quadDigits.push(digit); │ │ │ │ + } │ │ │ │ + var quadKey = quadDigits.join(""); │ │ │ │ + var url = this.selectUrl('' + x + y + z, this.url); │ │ │ │ + │ │ │ │ + return OpenLayers.String.format(url, { │ │ │ │ + 'quadkey': quadKey │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: nodeTypeCompare │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {SVGDomElement} An SVG element │ │ │ │ - * type - {String} Kind of node │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ + /** │ │ │ │ + * Method: updateAttribution │ │ │ │ + * Updates the attribution according to the requirements outlined in │ │ │ │ + * http://gis.638310.n2.nabble.com/Bing-imagery-td5789168.html │ │ │ │ */ │ │ │ │ - nodeTypeCompare: function(node, type) { │ │ │ │ - return (type == node.nodeName); │ │ │ │ + updateAttribution: function() { │ │ │ │ + var metadata = this.metadata; │ │ │ │ + if (!metadata.resourceSets || !this.map || !this.map.center) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + var res = metadata.resourceSets[0].resources[0]; │ │ │ │ + var extent = this.map.getExtent().transform( │ │ │ │ + this.map.getProjectionObject(), │ │ │ │ + new OpenLayers.Projection("EPSG:4326") │ │ │ │ + ); │ │ │ │ + var providers = res.imageryProviders || [], │ │ │ │ + zoom = OpenLayers.Util.indexOf(this.serverResolutions, │ │ │ │ + this.getServerResolution()), │ │ │ │ + copyrights = "", │ │ │ │ + provider, i, ii, j, jj, bbox, coverage; │ │ │ │ + for (i = 0, ii = providers.length; i < ii; ++i) { │ │ │ │ + provider = providers[i]; │ │ │ │ + for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) { │ │ │ │ + coverage = provider.coverageAreas[j]; │ │ │ │ + // axis order provided is Y,X │ │ │ │ + bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true); │ │ │ │ + if (extent.intersectsBounds(bbox) && │ │ │ │ + zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) { │ │ │ │ + copyrights += provider.attribution + " "; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol); │ │ │ │ + this.attribution = OpenLayers.String.format(this.attributionTemplate, { │ │ │ │ + type: this.type.toLowerCase(), │ │ │ │ + logo: logo, │ │ │ │ + copyrights: copyrights │ │ │ │ + }); │ │ │ │ + this.map && this.map.events.triggerEvent("changelayer", { │ │ │ │ + layer: this, │ │ │ │ + property: "attribution" │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createRenderRoot │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} The specific render engine's root element │ │ │ │ + * Method: setMap │ │ │ │ */ │ │ │ │ - createRenderRoot: function() { │ │ │ │ - var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg"); │ │ │ │ - svg.style.display = "block"; │ │ │ │ - return svg; │ │ │ │ + setMap: function() { │ │ │ │ + OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments); │ │ │ │ + this.map.events.register("moveend", this, this.updateAttribution); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createRoot │ │ │ │ + * APIMethod: clone │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * suffix - {String} suffix to append to the id │ │ │ │ + * obj - {Object} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * {<OpenLayers.Layer.Bing>} An exact clone of this <OpenLayers.Layer.Bing> │ │ │ │ */ │ │ │ │ - createRoot: function(suffix) { │ │ │ │ - return this.nodeFactory(this.container.id + suffix, "g"); │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.Bing(this.options); │ │ │ │ + } │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createDefs │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} The element to which we'll add the symbol definitions │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - createDefs: function() { │ │ │ │ - var defs = this.nodeFactory(this.container.id + "_defs", "defs"); │ │ │ │ - this.rendererRoot.appendChild(defs); │ │ │ │ - return defs; │ │ │ │ + destroy: function() { │ │ │ │ + this.map && │ │ │ │ + this.map.events.unregister("moveend", this, this.updateAttribution); │ │ │ │ + OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - /************************************** │ │ │ │ - * * │ │ │ │ - * GEOMETRY DRAWING FUNCTIONS * │ │ │ │ - * * │ │ │ │ - **************************************/ │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Bing" │ │ │ │ +}); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawPoint │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or false if the renderer could not draw the point │ │ │ │ - */ │ │ │ │ - drawPoint: function(node, geometry) { │ │ │ │ - return this.drawCircle(node, geometry, 1); │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * Function: OpenLayers.Layer.Bing.processMetadata │ │ │ │ + * This function will be bound to an instance, linked to the global scope with │ │ │ │ + * an id, and called by the JSONP script returned by the API. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * metadata - {Object} metadata as returned by the API │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Bing.processMetadata = function(metadata) { │ │ │ │ + this.metadata = metadata; │ │ │ │ + this.initLayer(); │ │ │ │ + var script = document.getElementById(this._callbackId); │ │ │ │ + script.parentNode.removeChild(script); │ │ │ │ + window[this._callbackId] = undefined; // cannot delete from window in IE │ │ │ │ + delete this._callbackId; │ │ │ │ +}; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/WMS.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawCircle │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * radius - {Float} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or false if the renderer could not draw the circle │ │ │ │ - */ │ │ │ │ - drawCircle: function(node, geometry, radius) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var x = ((geometry.x - this.featureDx) / resolution + this.left); │ │ │ │ - var y = (this.top - geometry.y / resolution); │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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.inValidRange(x, y)) { │ │ │ │ - node.setAttributeNS(null, "cx", x); │ │ │ │ - node.setAttributeNS(null, "cy", y); │ │ │ │ - node.setAttributeNS(null, "r", radius); │ │ │ │ - return node; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Grid.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.WMS │ │ │ │ + * Instances of OpenLayers.Layer.WMS are used to display data from OGC Web │ │ │ │ + * Mapping Services. Create a new WMS layer with the <OpenLayers.Layer.WMS> │ │ │ │ + * constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Grid> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawLineString │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or null if the renderer could not draw all components of │ │ │ │ - * the linestring, or false if nothing could be drawn │ │ │ │ + * Constant: DEFAULT_PARAMS │ │ │ │ + * {Object} Hashtable of default parameter key/value pairs │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ + DEFAULT_PARAMS: { │ │ │ │ + service: "WMS", │ │ │ │ + version: "1.1.1", │ │ │ │ + request: "GetMap", │ │ │ │ + styles: "", │ │ │ │ + format: "image/jpeg" │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawLinearRing │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or null if the renderer could not draw all components │ │ │ │ - * of the linear ring, or false if nothing could be drawn │ │ │ │ + * APIProperty: isBaseLayer │ │ │ │ + * {Boolean} Default is true for WMS layer │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + isBaseLayer: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawPolygon │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or null if the renderer could not draw all components │ │ │ │ - * of the polygon, or false if nothing could be drawn │ │ │ │ + * APIProperty: encodeBBOX │ │ │ │ + * {Boolean} Should the BBOX commas be encoded? The WMS spec says 'no', │ │ │ │ + * but some services want it that way. Default false. │ │ │ │ */ │ │ │ │ - drawPolygon: function(node, geometry) { │ │ │ │ - var d = ""; │ │ │ │ - var draw = true; │ │ │ │ - var complete = true; │ │ │ │ - var linearRingResult, path; │ │ │ │ - for (var j = 0, len = geometry.components.length; j < len; j++) { │ │ │ │ - d += " M"; │ │ │ │ - linearRingResult = this.getComponentsString( │ │ │ │ - geometry.components[j].components, " "); │ │ │ │ - path = linearRingResult.path; │ │ │ │ - if (path) { │ │ │ │ - d += " " + path; │ │ │ │ - complete = linearRingResult.complete && complete; │ │ │ │ - } else { │ │ │ │ - draw = false; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - d += " z"; │ │ │ │ - if (draw) { │ │ │ │ - node.setAttributeNS(null, "d", d); │ │ │ │ - node.setAttributeNS(null, "fill-rule", "evenodd"); │ │ │ │ - return complete ? node : null; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + encodeBBOX: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawRectangle │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or false if the renderer could not draw the rectangle │ │ │ │ + /** │ │ │ │ + * APIProperty: noMagic │ │ │ │ + * {Boolean} If true, the image format will not be automagicaly switched │ │ │ │ + * from image/jpeg to image/png or image/gif when using │ │ │ │ + * TRANSPARENT=TRUE. Also isBaseLayer will not changed by the │ │ │ │ + * constructor. Default false. │ │ │ │ */ │ │ │ │ - drawRectangle: function(node, geometry) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var x = ((geometry.x - this.featureDx) / resolution + this.left); │ │ │ │ - var y = (this.top - geometry.y / resolution); │ │ │ │ + noMagic: false, │ │ │ │ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: yx │ │ │ │ + * {Object} Keys in this object are EPSG codes for which the axis order │ │ │ │ + * is to be reversed (yx instead of xy, LatLon instead of LonLat), with │ │ │ │ + * true as value. This is only relevant for WMS versions >= 1.3.0, and │ │ │ │ + * only if yx is not set in <OpenLayers.Projection.defaults> for the │ │ │ │ + * used projection. │ │ │ │ + */ │ │ │ │ + yx: {}, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawText │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ + * Constructor: OpenLayers.Layer.WMS │ │ │ │ + * Create a new WMS layer object │ │ │ │ + * │ │ │ │ + * Examples: │ │ │ │ + * │ │ │ │ + * The code below creates a simple WMS layer using the image/jpeg format. │ │ │ │ + * (code) │ │ │ │ + * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic", │ │ │ │ + * "http://wms.jpl.nasa.gov/wms.cgi", │ │ │ │ + * {layers: "modis,global_mosaic"}); │ │ │ │ + * (end) │ │ │ │ + * Note the 3rd argument (params). Properties added to this object will be │ │ │ │ + * added to the WMS GetMap requests used for this layer's tiles. The only │ │ │ │ + * mandatory parameter is "layers". Other common WMS params include │ │ │ │ + * "transparent", "styles" and "format". Note that the "srs" param will │ │ │ │ + * always be ignored. Instead, it will be derived from the baseLayer's or │ │ │ │ + * map's projection. │ │ │ │ + * │ │ │ │ + * The code below creates a transparent WMS layer with additional options. │ │ │ │ + * (code) │ │ │ │ + * var wms = new OpenLayers.Layer.WMS("NASA Global Mosaic", │ │ │ │ + * "http://wms.jpl.nasa.gov/wms.cgi", │ │ │ │ + * { │ │ │ │ + * layers: "modis,global_mosaic", │ │ │ │ + * transparent: true │ │ │ │ + * }, { │ │ │ │ + * opacity: 0.5, │ │ │ │ + * singleTile: true │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ + * Note that by default, a WMS layer is configured as baseLayer. Setting │ │ │ │ + * the "transparent" param to true will apply some magic (see <noMagic>). │ │ │ │ + * The default image format changes from image/jpeg to image/png, and the │ │ │ │ + * layer is not configured as baseLayer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * featureId - {String} │ │ │ │ - * style - │ │ │ │ - * location - {<OpenLayers.Geometry.Point>} │ │ │ │ + * name - {String} A name for the layer │ │ │ │ + * url - {String} Base url for the WMS │ │ │ │ + * (e.g. http://wms.jpl.nasa.gov/wms.cgi) │ │ │ │ + * params - {Object} An object with key/value pairs representing the │ │ │ │ + * GetMap query string parameters and parameter values. │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer. │ │ │ │ + * These options include all properties listed above, plus the ones │ │ │ │ + * inherited from superclasses. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + initialize: function(name, url, params, options) { │ │ │ │ + var newArguments = []; │ │ │ │ + //uppercase params │ │ │ │ + params = OpenLayers.Util.upperCaseObject(params); │ │ │ │ + if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) { │ │ │ │ + params.EXCEPTIONS = "INIMAGE"; │ │ │ │ } │ │ │ │ + newArguments.push(name, url, params, options); │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ + OpenLayers.Util.applyDefaults( │ │ │ │ + this.params, │ │ │ │ + OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS) │ │ │ │ + ); │ │ │ │ │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - │ │ │ │ - var x = ((location.x - this.featureDx) / resolution + this.left); │ │ │ │ - var y = (location.y / resolution - this.top); │ │ │ │ - │ │ │ │ - var suffix = (drawOutline) ? this.LABEL_OUTLINE_SUFFIX : this.LABEL_ID_SUFFIX; │ │ │ │ - var label = this.nodeFactory(featureId + suffix, "text"); │ │ │ │ - │ │ │ │ - label.setAttributeNS(null, "x", x); │ │ │ │ - label.setAttributeNS(null, "y", -y); │ │ │ │ - │ │ │ │ - if (style.fontColor) { │ │ │ │ - label.setAttributeNS(null, "fill", style.fontColor); │ │ │ │ - } │ │ │ │ - if (style.fontStrokeColor) { │ │ │ │ - label.setAttributeNS(null, "stroke", style.fontStrokeColor); │ │ │ │ - } │ │ │ │ - if (style.fontStrokeWidth) { │ │ │ │ - label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth); │ │ │ │ - } │ │ │ │ - if (style.fontOpacity) { │ │ │ │ - label.setAttributeNS(null, "opacity", style.fontOpacity); │ │ │ │ - } │ │ │ │ - if (style.fontFamily) { │ │ │ │ - label.setAttributeNS(null, "font-family", style.fontFamily); │ │ │ │ - } │ │ │ │ - if (style.fontSize) { │ │ │ │ - label.setAttributeNS(null, "font-size", style.fontSize); │ │ │ │ - } │ │ │ │ - if (style.fontWeight) { │ │ │ │ - label.setAttributeNS(null, "font-weight", style.fontWeight); │ │ │ │ - } │ │ │ │ - if (style.fontStyle) { │ │ │ │ - label.setAttributeNS(null, "font-style", style.fontStyle); │ │ │ │ - } │ │ │ │ - if (style.labelSelect === true) { │ │ │ │ - label.setAttributeNS(null, "pointer-events", "visible"); │ │ │ │ - label._featureId = featureId; │ │ │ │ - } else { │ │ │ │ - label.setAttributeNS(null, "pointer-events", "none"); │ │ │ │ - } │ │ │ │ - var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign; │ │ │ │ - label.setAttributeNS(null, "text-anchor", │ │ │ │ - OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle"); │ │ │ │ │ │ │ │ - if (OpenLayers.IS_GECKO === true) { │ │ │ │ - label.setAttributeNS(null, "dominant-baseline", │ │ │ │ - OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central"); │ │ │ │ - } │ │ │ │ + //layer is transparent │ │ │ │ + if (!this.noMagic && this.params.TRANSPARENT && │ │ │ │ + this.params.TRANSPARENT.toString().toLowerCase() == "true") { │ │ │ │ │ │ │ │ - var labelRows = style.label.split('\n'); │ │ │ │ - var numRows = labelRows.length; │ │ │ │ - while (label.childNodes.length > numRows) { │ │ │ │ - label.removeChild(label.lastChild); │ │ │ │ - } │ │ │ │ - for (var i = 0; i < numRows; i++) { │ │ │ │ - var tspan = this.nodeFactory(featureId + suffix + "_tspan_" + i, "tspan"); │ │ │ │ - if (style.labelSelect === true) { │ │ │ │ - tspan._featureId = featureId; │ │ │ │ - tspan._geometry = location; │ │ │ │ - tspan._geometryClass = location.CLASS_NAME; │ │ │ │ - } │ │ │ │ - if (OpenLayers.IS_GECKO === false) { │ │ │ │ - tspan.setAttributeNS(null, "baseline-shift", │ │ │ │ - OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%"); │ │ │ │ - } │ │ │ │ - tspan.setAttribute("x", x); │ │ │ │ - if (i == 0) { │ │ │ │ - var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]]; │ │ │ │ - if (vfactor == null) { │ │ │ │ - vfactor = -.5; │ │ │ │ - } │ │ │ │ - tspan.setAttribute("dy", (vfactor * (numRows - 1)) + "em"); │ │ │ │ - } else { │ │ │ │ - tspan.setAttribute("dy", "1em"); │ │ │ │ + // unless explicitly set in options, make layer an overlay │ │ │ │ + if ((options == null) || (!options.isBaseLayer)) { │ │ │ │ + this.isBaseLayer = false; │ │ │ │ } │ │ │ │ - tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i]; │ │ │ │ - if (!tspan.parentNode) { │ │ │ │ - label.appendChild(tspan); │ │ │ │ + │ │ │ │ + // jpegs can never be transparent, so intelligently switch the │ │ │ │ + // format, depending on the browser's capabilities │ │ │ │ + if (this.params.FORMAT == "image/jpeg") { │ │ │ │ + this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif" : │ │ │ │ + "image/png"; │ │ │ │ } │ │ │ │ } │ │ │ │ │ │ │ │ - if (!label.parentNode) { │ │ │ │ - this.textRoot.appendChild(label); │ │ │ │ - } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getComponentString │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * components - {Array(<OpenLayers.Geometry.Point>)} Array of points │ │ │ │ - * separator - {String} character between coordinate pairs. Defaults to "," │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Object} hash with properties "path" (the string created from the │ │ │ │ - * components and "complete" (false if the renderer was unable to │ │ │ │ - * draw all components) │ │ │ │ + * {<OpenLayers.Layer.WMS>} An exact clone of this layer │ │ │ │ */ │ │ │ │ - getComponentsString: function(components, separator) { │ │ │ │ - var renderCmp = []; │ │ │ │ - var complete = true; │ │ │ │ - var len = components.length; │ │ │ │ - var strings = []; │ │ │ │ - var str, component; │ │ │ │ - for (var i = 0; i < len; i++) { │ │ │ │ - component = components[i]; │ │ │ │ - renderCmp.push(component); │ │ │ │ - str = this.getShortString(component); │ │ │ │ - if (str) { │ │ │ │ - strings.push(str); │ │ │ │ - } else { │ │ │ │ - // The current component is outside the valid range. Let's │ │ │ │ - // see if the previous or next component is inside the range. │ │ │ │ - // If so, add the coordinate of the intersection with the │ │ │ │ - // valid range bounds. │ │ │ │ - if (i > 0) { │ │ │ │ - if (this.getShortString(components[i - 1])) { │ │ │ │ - strings.push(this.clipLine(components[i], │ │ │ │ - components[i - 1])); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (i < len - 1) { │ │ │ │ - if (this.getShortString(components[i + 1])) { │ │ │ │ - strings.push(this.clipLine(components[i], │ │ │ │ - components[i + 1])); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - complete = false; │ │ │ │ - } │ │ │ │ + clone: function(obj) { │ │ │ │ + │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.WMS(this.name, │ │ │ │ + this.url, │ │ │ │ + this.params, │ │ │ │ + this.getOptions()); │ │ │ │ } │ │ │ │ │ │ │ │ - return { │ │ │ │ - path: strings.join(separator || ","), │ │ │ │ - complete: complete │ │ │ │ - }; │ │ │ │ + //get all additions from superclasses │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + │ │ │ │ + // copy/set any non-init, non-simple values here │ │ │ │ + │ │ │ │ + return obj; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clipLine │ │ │ │ - * Given two points (one inside the valid range, and one outside), │ │ │ │ - * clips the line betweeen the two points so that the new points are both │ │ │ │ - * inside the valid range. │ │ │ │ + * APIMethod: reverseAxisOrder │ │ │ │ + * Returns true if the axis order is reversed for the WMS version and │ │ │ │ + * projection of the layer. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * badComponent - {<OpenLayers.Geometry.Point>} original geometry of the │ │ │ │ - * invalid point │ │ │ │ - * goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the │ │ │ │ - * valid point │ │ │ │ - * Returns │ │ │ │ - * {String} the SVG coordinate pair of the clipped point (like │ │ │ │ - * getShortString), or an empty string if both passed componets are at │ │ │ │ - * the same point. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} true if the axis order is reversed, false otherwise. │ │ │ │ */ │ │ │ │ - clipLine: function(badComponent, goodComponent) { │ │ │ │ - if (goodComponent.equals(badComponent)) { │ │ │ │ - return ""; │ │ │ │ - } │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var maxX = this.MAX_PIXEL - this.translationParameters.x; │ │ │ │ - var maxY = this.MAX_PIXEL - this.translationParameters.y; │ │ │ │ - var x1 = (goodComponent.x - this.featureDx) / resolution + this.left; │ │ │ │ - var y1 = this.top - goodComponent.y / resolution; │ │ │ │ - var x2 = (badComponent.x - this.featureDx) / resolution + this.left; │ │ │ │ - var y2 = this.top - badComponent.y / resolution; │ │ │ │ - var k; │ │ │ │ - if (x2 < -maxX || x2 > maxX) { │ │ │ │ - k = (y2 - y1) / (x2 - x1); │ │ │ │ - x2 = x2 < 0 ? -maxX : maxX; │ │ │ │ - y2 = y1 + (x2 - x1) * k; │ │ │ │ - } │ │ │ │ - if (y2 < -maxY || y2 > maxY) { │ │ │ │ - k = (x2 - x1) / (y2 - y1); │ │ │ │ - y2 = y2 < 0 ? -maxY : maxY; │ │ │ │ - x2 = x1 + (y2 - y1) * k; │ │ │ │ - } │ │ │ │ - return x2 + "," + y2; │ │ │ │ + reverseAxisOrder: function() { │ │ │ │ + var projCode = this.projection.getCode(); │ │ │ │ + return parseFloat(this.params.VERSION) >= 1.3 && │ │ │ │ + !!(this.yx[projCode] || (OpenLayers.Projection.defaults[projCode] && │ │ │ │ + OpenLayers.Projection.defaults[projCode].yx)); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getShortString │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: getURL │ │ │ │ + * Return a GetMap query string for this layer │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} │ │ │ │ - * │ │ │ │ + * bounds - {<OpenLayers.Bounds>} A bounds representing the bbox for the │ │ │ │ + * request. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} or false if point is outside the valid range │ │ │ │ + * {String} A string with the layer's url and parameters and also the │ │ │ │ + * passed-in bounds and appropriate tile size specified as │ │ │ │ + * parameters. │ │ │ │ */ │ │ │ │ - getShortString: function(point) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var x = ((point.x - this.featureDx) / resolution + this.left); │ │ │ │ - var y = (this.top - point.y / resolution); │ │ │ │ + getURL: function(bounds) { │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ │ │ │ │ - if (this.inValidRange(x, y)) { │ │ │ │ - return x + "," + y; │ │ │ │ - } else { │ │ │ │ - return false; │ │ │ │ - } │ │ │ │ + var imageSize = this.getImageSize(); │ │ │ │ + var newParams = {}; │ │ │ │ + // WMS 1.3 introduced axis order │ │ │ │ + var reverseAxisOrder = this.reverseAxisOrder(); │ │ │ │ + newParams.BBOX = this.encodeBBOX ? │ │ │ │ + bounds.toBBOX(null, reverseAxisOrder) : │ │ │ │ + bounds.toArray(reverseAxisOrder); │ │ │ │ + newParams.WIDTH = imageSize.w; │ │ │ │ + newParams.HEIGHT = imageSize.h; │ │ │ │ + var requestString = this.getFullRequestString(newParams); │ │ │ │ + return requestString; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getPosition │ │ │ │ - * Finds the position of an svg node. │ │ │ │ + * APIMethod: mergeNewParams │ │ │ │ + * Catch changeParams and uppercase the new params to be merged in │ │ │ │ + * before calling changeParams on the super class. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ + * Once params have been changed, the tiles will be reloaded with │ │ │ │ + * the new parameters. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Object} hash with x and y properties, representing the coordinates │ │ │ │ - * within the svg coordinate system │ │ │ │ + * Parameters: │ │ │ │ + * newParams - {Object} Hashtable of new params to use │ │ │ │ */ │ │ │ │ - getPosition: function(node) { │ │ │ │ - return ({ │ │ │ │ - x: parseFloat(node.getAttributeNS(null, "cx")), │ │ │ │ - y: parseFloat(node.getAttributeNS(null, "cy")) │ │ │ │ - }); │ │ │ │ + mergeNewParams: function(newParams) { │ │ │ │ + var upperParams = OpenLayers.Util.upperCaseObject(newParams); │ │ │ │ + var newArguments = [upperParams]; │ │ │ │ + return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, │ │ │ │ + newArguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: importSymbol │ │ │ │ - * add a new symbol definition from the rendererer's symbol hash │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * APIMethod: getFullRequestString │ │ │ │ + * Combine the layer's url with its params and these newParams. │ │ │ │ + * │ │ │ │ + * Add the SRS parameter from projection -- this is probably │ │ │ │ + * more eloquently done via a setProjection() method, but this │ │ │ │ + * works for now and always. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * graphicName - {String} name of the symbol to import │ │ │ │ + * newParams - {Object} │ │ │ │ + * altUrl - {String} Use this as the url instead of the layer's url │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} - the imported symbol │ │ │ │ + * {String} │ │ │ │ */ │ │ │ │ - importSymbol: function(graphicName) { │ │ │ │ - if (!this.defs) { │ │ │ │ - // create svg defs tag │ │ │ │ - this.defs = this.createDefs(); │ │ │ │ + getFullRequestString: function(newParams, altUrl) { │ │ │ │ + var mapProjection = this.map.getProjectionObject(); │ │ │ │ + var projectionCode = this.projection && this.projection.equals(mapProjection) ? │ │ │ │ + this.projection.getCode() : │ │ │ │ + mapProjection.getCode(); │ │ │ │ + var value = (projectionCode == "none") ? null : projectionCode; │ │ │ │ + if (parseFloat(this.params.VERSION) >= 1.3) { │ │ │ │ + this.params.CRS = value; │ │ │ │ + } else { │ │ │ │ + this.params.SRS = value; │ │ │ │ } │ │ │ │ - var id = this.container.id + "-" + graphicName; │ │ │ │ │ │ │ │ - // check if symbol already exists in the defs │ │ │ │ - var existing = document.getElementById(id); │ │ │ │ - if (existing != null) { │ │ │ │ - return existing; │ │ │ │ + if (typeof this.params.TRANSPARENT == "boolean") { │ │ │ │ + newParams.TRANSPARENT = this.params.TRANSPARENT ? "TRUE" : "FALSE"; │ │ │ │ } │ │ │ │ │ │ │ │ - var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ - if (!symbol) { │ │ │ │ - throw new Error(graphicName + ' is not a valid symbol name'); │ │ │ │ - } │ │ │ │ + return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply( │ │ │ │ + this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - var symbolNode = this.nodeFactory(id, "symbol"); │ │ │ │ - var node = this.nodeFactory(null, "polygon"); │ │ │ │ - symbolNode.appendChild(node); │ │ │ │ - var symbolExtent = new OpenLayers.Bounds( │ │ │ │ - Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.WMS" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/SphericalMercator.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - var points = []; │ │ │ │ - var x, y; │ │ │ │ - for (var i = 0; i < symbol.length; i = i + 2) { │ │ │ │ - x = symbol[i]; │ │ │ │ - y = symbol[i + 1]; │ │ │ │ - symbolExtent.left = Math.min(symbolExtent.left, x); │ │ │ │ - symbolExtent.bottom = Math.min(symbolExtent.bottom, y); │ │ │ │ - symbolExtent.right = Math.max(symbolExtent.right, x); │ │ │ │ - symbolExtent.top = Math.max(symbolExtent.top, y); │ │ │ │ - points.push(x, ",", y); │ │ │ │ - } │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - node.setAttributeNS(null, "points", points.join(" ")); │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ + * @requires OpenLayers/Projection.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - var width = symbolExtent.getWidth(); │ │ │ │ - var height = symbolExtent.getHeight(); │ │ │ │ - // create a viewBox three times as large as the symbol itself, │ │ │ │ - // to allow for strokeWidth being displayed correctly at the corners. │ │ │ │ - var viewBox = [symbolExtent.left - width, │ │ │ │ - symbolExtent.bottom - height, width * 3, height * 3 │ │ │ │ - ]; │ │ │ │ - symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" ")); │ │ │ │ - this.symbolMetrics[id] = [ │ │ │ │ - Math.max(width, height), │ │ │ │ - symbolExtent.getCenterLonLat().lon, │ │ │ │ - symbolExtent.getCenterLonLat().lat │ │ │ │ - ]; │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.SphericalMercator │ │ │ │ + * A mixin for layers that wraps up the pieces neccesary to have a coordinate │ │ │ │ + * conversion for working with commercial APIs which use a spherical │ │ │ │ + * mercator projection. Using this layer as a base layer, additional │ │ │ │ + * layers can be used as overlays if they are in the same projection. │ │ │ │ + * │ │ │ │ + * A layer is given properties of this object by setting the sphericalMercator │ │ │ │ + * property to true. │ │ │ │ + * │ │ │ │ + * More projection information: │ │ │ │ + * - http://spatialreference.org/ref/user/google-projection/ │ │ │ │ + * │ │ │ │ + * Proj4 Text: │ │ │ │ + * +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 │ │ │ │ + * +k=1.0 +units=m +nadgrids=@null +no_defs │ │ │ │ + * │ │ │ │ + * WKT: │ │ │ │ + * 900913=PROJCS["WGS84 / Simple Mercator", GEOGCS["WGS 84", │ │ │ │ + * DATUM["WGS_1984", SPHEROID["WGS_1984", 6378137.0, 298.257223563]], │ │ │ │ + * PRIMEM["Greenwich", 0.0], UNIT["degree", 0.017453292519943295], │ │ │ │ + * AXIS["Longitude", EAST], AXIS["Latitude", NORTH]], │ │ │ │ + * PROJECTION["Mercator_1SP_Google"], │ │ │ │ + * PARAMETER["latitude_of_origin", 0.0], PARAMETER["central_meridian", 0.0], │ │ │ │ + * PARAMETER["scale_factor", 1.0], PARAMETER["false_easting", 0.0], │ │ │ │ + * PARAMETER["false_northing", 0.0], UNIT["m", 1.0], AXIS["x", EAST], │ │ │ │ + * AXIS["y", NORTH], AUTHORITY["EPSG","900913"]] │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.SphericalMercator = { │ │ │ │ │ │ │ │ - this.defs.appendChild(symbolNode); │ │ │ │ - return symbolNode; │ │ │ │ + /** │ │ │ │ + * Method: getExtent │ │ │ │ + * Get the map's extent. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} The map extent. │ │ │ │ + */ │ │ │ │ + getExtent: function() { │ │ │ │ + var extent = null; │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + extent = this.map.calculateBounds(); │ │ │ │ + } else { │ │ │ │ + extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this); │ │ │ │ + } │ │ │ │ + return extent; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getFeatureIdFromEvent │ │ │ │ + * Method: getLonLatFromViewPortPx │ │ │ │ + * Get a map location from a pixel location │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * evt - {Object} An <OpenLayers.Event> object │ │ │ │ + * viewPortPx - {<OpenLayers.Pixel>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A feature id or undefined. │ │ │ │ + * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view │ │ │ │ + * port OpenLayers.Pixel, translated into lon/lat by map lib │ │ │ │ + * If the map lib is not loaded or not centered, returns null │ │ │ │ */ │ │ │ │ - getFeatureIdFromEvent: function(evt) { │ │ │ │ - var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments); │ │ │ │ - if (!featureId) { │ │ │ │ - var target = evt.target; │ │ │ │ - featureId = target.parentNode && target != this.rendererRoot ? │ │ │ │ - target.parentNode._featureId : undefined; │ │ │ │ - } │ │ │ │ - return featureId; │ │ │ │ + getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ + return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer.SVG" │ │ │ │ -}); │ │ │ │ + /** │ │ │ │ + * Method: getViewPortPxFromLonLat │ │ │ │ + * Get a pixel location from a map location │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * lonlat - {<OpenLayers.LonLat>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in │ │ │ │ + * OpenLayers.LonLat, translated into view port pixels by map lib │ │ │ │ + * If map lib is not loaded or not centered, returns null │ │ │ │ + */ │ │ │ │ + getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ + return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Renderer.SVG.LABEL_ALIGN │ │ │ │ - * {Object} │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.SVG.LABEL_ALIGN = { │ │ │ │ - "l": "start", │ │ │ │ - "r": "end", │ │ │ │ - "b": "bottom", │ │ │ │ - "t": "hanging" │ │ │ │ -}; │ │ │ │ + /** │ │ │ │ + * Method: initMercatorParameters │ │ │ │ + * Set up the mercator parameters on the layer: resolutions, │ │ │ │ + * projection, units. │ │ │ │ + */ │ │ │ │ + initMercatorParameters: function() { │ │ │ │ + // set up properties for Mercator - assume EPSG:900913 │ │ │ │ + this.RESOLUTIONS = []; │ │ │ │ + var maxResolution = 156543.03390625; │ │ │ │ + for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) { │ │ │ │ + this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom); │ │ │ │ + } │ │ │ │ + this.units = "m"; │ │ │ │ + this.projection = this.projection || "EPSG:900913"; │ │ │ │ + }, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Renderer.SVG.LABEL_VSHIFT │ │ │ │ - * {Object} │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.SVG.LABEL_VSHIFT = { │ │ │ │ - // according to │ │ │ │ - // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html │ │ │ │ - // a baseline-shift of -70% shifts the text exactly from the │ │ │ │ - // bottom to the top of the baseline, so -35% moves the text to │ │ │ │ - // the center of the baseline. │ │ │ │ - "t": "-70%", │ │ │ │ - "b": "0" │ │ │ │ -}; │ │ │ │ + /** │ │ │ │ + * APIMethod: forwardMercator │ │ │ │ + * Given a lon,lat in EPSG:4326, return a point in Spherical Mercator. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * lon - {float} │ │ │ │ + * lat - {float} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.LonLat>} The coordinates transformed to Mercator. │ │ │ │ + */ │ │ │ │ + forwardMercator: (function() { │ │ │ │ + var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ + return function(lon, lat) { │ │ │ │ + var point = OpenLayers.Projection.transform({ │ │ │ │ + x: lon, │ │ │ │ + y: lat │ │ │ │ + }, gg, sm); │ │ │ │ + return new OpenLayers.LonLat(point.x, point.y); │ │ │ │ + }; │ │ │ │ + })(), │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR │ │ │ │ - * {Object} │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.SVG.LABEL_VFACTOR = { │ │ │ │ - "t": 0, │ │ │ │ - "b": -1 │ │ │ │ -}; │ │ │ │ + /** │ │ │ │ + * APIMethod: inverseMercator │ │ │ │ + * Given a x,y in Spherical Mercator, return a point in EPSG:4326. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * x - {float} A map x in Spherical Mercator. │ │ │ │ + * y - {float} A map y in Spherical Mercator. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.LonLat>} The coordinates transformed to EPSG:4326. │ │ │ │ + */ │ │ │ │ + inverseMercator: (function() { │ │ │ │ + var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ + return function(x, y) { │ │ │ │ + var point = OpenLayers.Projection.transform({ │ │ │ │ + x: x, │ │ │ │ + y: y │ │ │ │ + }, sm, gg); │ │ │ │ + return new OpenLayers.LonLat(point.x, point.y); │ │ │ │ + }; │ │ │ │ + })() │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Function: OpenLayers.Renderer.SVG.preventDefault │ │ │ │ - * *Deprecated*. Use <OpenLayers.Event.preventDefault> method instead. │ │ │ │ - * Used to prevent default events (especially opening images in a new tab on │ │ │ │ - * ctrl-click) from being executed for externalGraphic symbols │ │ │ │ - */ │ │ │ │ -OpenLayers.Renderer.SVG.preventDefault = function(e) { │ │ │ │ - OpenLayers.Event.preventDefault(e); │ │ │ │ }; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Renderer/VML.js │ │ │ │ + OpenLayers/Layer/EventPane.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Renderer/Elements.js │ │ │ │ + * @requires OpenLayers/Layer.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Renderer.VML │ │ │ │ - * Render vector features in browsers with VML capability. Construct a new │ │ │ │ - * VML renderer with the <OpenLayers.Renderer.VML> constructor. │ │ │ │ - * │ │ │ │ - * Note that for all calculations in this class, we use (num | 0) to truncate a │ │ │ │ - * float value to an integer. This is done because it seems that VML doesn't │ │ │ │ - * support float values. │ │ │ │ + * Class: OpenLayers.Layer.EventPane │ │ │ │ + * Base class for 3rd party layers, providing a DOM element which isolates │ │ │ │ + * the 3rd-party layer from mouse events. │ │ │ │ + * Only used by Google layers. │ │ │ │ * │ │ │ │ + * Automatically instantiated by the Google constructor, and not usually instantiated directly. │ │ │ │ + * │ │ │ │ + * Create a new event pane layer with the │ │ │ │ + * <OpenLayers.Layer.EventPane> constructor. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Renderer.Elements> │ │ │ │ + * - <OpenLayers.Layer> │ │ │ │ */ │ │ │ │ -OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ +OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: xmlns │ │ │ │ - * {String} XML Namespace URN │ │ │ │ + * APIProperty: smoothDragPan │ │ │ │ + * {Boolean} smoothDragPan determines whether non-public/internal API │ │ │ │ + * methods are used for better performance while dragging EventPane │ │ │ │ + * layers. When not in sphericalMercator mode, the smoother dragging │ │ │ │ + * doesn't actually move north/south directly with the number of │ │ │ │ + * pixels moved, resulting in a slight offset when you drag your mouse │ │ │ │ + * north south with this option on. If this visual disparity bothers │ │ │ │ + * you, you should turn this option off, or use spherical mercator. │ │ │ │ + * Default is on. │ │ │ │ */ │ │ │ │ - xmlns: "urn:schemas-microsoft-com:vml", │ │ │ │ + smoothDragPan: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Property: isBaseLayer │ │ │ │ + * {Boolean} EventPaned layers are always base layers, by necessity. │ │ │ │ */ │ │ │ │ - symbolCache: {}, │ │ │ │ + isBaseLayer: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: offset │ │ │ │ - * {Object} Hash with "x" and "y" properties │ │ │ │ + * APIProperty: isFixed │ │ │ │ + * {Boolean} EventPaned layers are fixed by default. │ │ │ │ */ │ │ │ │ - offset: null, │ │ │ │ + isFixed: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Renderer.VML │ │ │ │ - * Create a new VML renderer. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * containerID - {String} The id for the element that contains the renderer │ │ │ │ + * Property: pane │ │ │ │ + * {DOMElement} A reference to the element that controls the events. │ │ │ │ */ │ │ │ │ - initialize: function(containerID) { │ │ │ │ - if (!this.supported()) { │ │ │ │ - return; │ │ │ │ - } │ │ │ │ - if (!document.namespaces.olv) { │ │ │ │ - document.namespaces.add("olv", this.xmlns); │ │ │ │ - var style = document.createStyleSheet(); │ │ │ │ - var shapes = ['shape', 'rect', 'oval', 'fill', 'stroke', 'imagedata', 'group', 'textbox']; │ │ │ │ - for (var i = 0, len = shapes.length; i < len; i++) { │ │ │ │ - │ │ │ │ - style.addRule('olv\\:' + shapes[i], "behavior: url(#default#VML); " + │ │ │ │ - "position: absolute; display: inline-block;"); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - OpenLayers.Renderer.Elements.prototype.initialize.apply(this, │ │ │ │ - arguments); │ │ │ │ - }, │ │ │ │ + pane: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: supported │ │ │ │ - * Determine whether a browser supports this renderer. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The browser supports the VML renderer │ │ │ │ - */ │ │ │ │ - supported: function() { │ │ │ │ - return !!(document.namespaces); │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setExtent │ │ │ │ - * Set the renderer's extent │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * extent - {<OpenLayers.Bounds>} │ │ │ │ - * resolutionChanged - {Boolean} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} true to notify the layer that the new extent does not exceed │ │ │ │ - * the coordinate range, and the features will not need to be redrawn. │ │ │ │ + * Property: mapObject │ │ │ │ + * {Object} This is the object which will be used to load the 3rd party library │ │ │ │ + * in the case of the google layer, this will be of type GMap, │ │ │ │ + * in the case of the ve layer, this will be of type VEMap │ │ │ │ */ │ │ │ │ - setExtent: function(extent, resolutionChanged) { │ │ │ │ - var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - │ │ │ │ - var left = (extent.left / resolution) | 0; │ │ │ │ - var top = (extent.top / resolution - this.size.h) | 0; │ │ │ │ - if (resolutionChanged || !this.offset) { │ │ │ │ - this.offset = { │ │ │ │ - x: left, │ │ │ │ - y: top │ │ │ │ - }; │ │ │ │ - left = 0; │ │ │ │ - top = 0; │ │ │ │ - } else { │ │ │ │ - left = left - this.offset.x; │ │ │ │ - top = top - this.offset.y; │ │ │ │ - } │ │ │ │ - │ │ │ │ - │ │ │ │ - var org = (left - this.xOffset) + " " + top; │ │ │ │ - this.root.coordorigin = org; │ │ │ │ - var roots = [this.root, this.vectorRoot, this.textRoot]; │ │ │ │ - var root; │ │ │ │ - for (var i = 0, len = roots.length; i < len; ++i) { │ │ │ │ - root = roots[i]; │ │ │ │ - │ │ │ │ - var size = this.size.w + " " + this.size.h; │ │ │ │ - root.coordsize = size; │ │ │ │ - │ │ │ │ - } │ │ │ │ - // flip the VML display Y axis upside down so it │ │ │ │ - // matches the display Y axis of the map │ │ │ │ - this.root.style.flip = "y"; │ │ │ │ - │ │ │ │ - return coordSysUnchanged; │ │ │ │ - }, │ │ │ │ + mapObject: null, │ │ │ │ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setSize │ │ │ │ - * Set the size of the drawing surface │ │ │ │ + * Constructor: OpenLayers.Layer.EventPane │ │ │ │ + * Create a new event pane layer │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * size - {<OpenLayers.Size>} the size of the drawing surface │ │ │ │ + * name - {String} │ │ │ │ + * options - {Object} Hashtable of extra options to tag onto the layer │ │ │ │ */ │ │ │ │ - setSize: function(size) { │ │ │ │ - OpenLayers.Renderer.prototype.setSize.apply(this, arguments); │ │ │ │ - │ │ │ │ - // setting width and height on all roots to avoid flicker which we │ │ │ │ - // would get with 100% width and height on child roots │ │ │ │ - var roots = [ │ │ │ │ - this.rendererRoot, │ │ │ │ - this.root, │ │ │ │ - this.vectorRoot, │ │ │ │ - this.textRoot │ │ │ │ - ]; │ │ │ │ - var w = this.size.w + "px"; │ │ │ │ - var h = this.size.h + "px"; │ │ │ │ - var root; │ │ │ │ - for (var i = 0, len = roots.length; i < len; ++i) { │ │ │ │ - root = roots[i]; │ │ │ │ - root.style.width = w; │ │ │ │ - root.style.height = h; │ │ │ │ + initialize: function(name, options) { │ │ │ │ + OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ + if (this.pane == null) { │ │ │ │ + this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane"); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getNodeType │ │ │ │ - * Get the node type for a geometry and style │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * style - {Object} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The corresponding node type for the specified geometry │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Deconstruct this layer. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + destroy: function() { │ │ │ │ + this.mapObject = null; │ │ │ │ + this.pane = null; │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: setStyle │ │ │ │ - * Use to set all the style attributes to a VML node. │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the layer. This is done through an accessor │ │ │ │ + * so that subclasses can override this and take special action once │ │ │ │ + * they have their map variable set. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} An VML element to decorate │ │ │ │ - * style - {Object} │ │ │ │ - * options - {Object} Currently supported options include │ │ │ │ - * 'isFilled' {Boolean} and │ │ │ │ - * 'isStroked' {Boolean} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - setStyle: function(node, style, options, geometry) { │ │ │ │ - style = style || node._style; │ │ │ │ - options = options || node._options; │ │ │ │ - var fillColor = style.fillColor; │ │ │ │ - │ │ │ │ - var title = style.title || style.graphicTitle; │ │ │ │ - if (title) { │ │ │ │ - node.title = title; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ - if (style.externalGraphic) { │ │ │ │ - options.isFilled = true; │ │ │ │ - var width = style.graphicWidth || style.graphicHeight; │ │ │ │ - var height = style.graphicHeight || style.graphicWidth; │ │ │ │ - width = width ? width : style.pointRadius * 2; │ │ │ │ - height = height ? height : style.pointRadius * 2; │ │ │ │ - │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var xOffset = (style.graphicXOffset != undefined) ? │ │ │ │ - style.graphicXOffset : -(0.5 * width); │ │ │ │ - var yOffset = (style.graphicYOffset != undefined) ? │ │ │ │ - style.graphicYOffset : -(0.5 * height); │ │ │ │ - │ │ │ │ - node.style.left = ((((geometry.x - this.featureDx) / resolution - this.offset.x) + xOffset) | 0) + "px"; │ │ │ │ - node.style.top = (((geometry.y / resolution - this.offset.y) - (yOffset + height)) | 0) + "px"; │ │ │ │ - node.style.width = width + "px"; │ │ │ │ - node.style.height = height + "px"; │ │ │ │ - node.style.flip = "y"; │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ │ │ │ │ - // 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.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; │ │ │ │ + this.pane.style.display = this.div.style.display; │ │ │ │ + this.pane.style.width = "100%"; │ │ │ │ + this.pane.style.height = "100%"; │ │ │ │ + if (OpenLayers.BROWSER_NAME == "msie") { │ │ │ │ + this.pane.style.background = │ │ │ │ + "url(" + OpenLayers.Util.getImageLocation("blank.gif") + ")"; │ │ │ │ } │ │ │ │ │ │ │ │ - // 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); │ │ │ │ - } │ │ │ │ + if (this.isFixed) { │ │ │ │ + this.map.viewPortDiv.appendChild(this.pane); │ │ │ │ } else { │ │ │ │ - if (!fill) { │ │ │ │ - fill = this.createNode('olv:fill', node.id + "_fill"); │ │ │ │ - } │ │ │ │ - fill.opacity = style.fillOpacity; │ │ │ │ - │ │ │ │ - if (node._geometryClass === "OpenLayers.Geometry.Point" && │ │ │ │ - style.externalGraphic) { │ │ │ │ - │ │ │ │ - // override fillOpacity │ │ │ │ - if (style.graphicOpacity) { │ │ │ │ - fill.opacity = style.graphicOpacity; │ │ │ │ - } │ │ │ │ - │ │ │ │ - fill.src = style.externalGraphic; │ │ │ │ - fill.type = "frame"; │ │ │ │ - │ │ │ │ - if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ - fill.aspect = "atmost"; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (fill.parentNode != node) { │ │ │ │ - node.appendChild(fill); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - // additional rendering for rotated graphics or symbols │ │ │ │ - var rotation = style.rotation; │ │ │ │ - if ((rotation !== undefined || node._rotation !== undefined)) { │ │ │ │ - node._rotation = rotation; │ │ │ │ - if (style.externalGraphic) { │ │ │ │ - this.graphicRotate(node, xOffset, yOffset, style); │ │ │ │ - // make the fill fully transparent, because we now have │ │ │ │ - // the graphic as imagedata element. We cannot just remove │ │ │ │ - // the fill, because this is part of the hack described │ │ │ │ - // in graphicRotate │ │ │ │ - fill.opacity = 0; │ │ │ │ - } else if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ - node.style.rotation = rotation || 0; │ │ │ │ - } │ │ │ │ + this.map.layerContainerDiv.appendChild(this.pane); │ │ │ │ } │ │ │ │ │ │ │ │ - // 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); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + // once our layer has been added to the map, we can load it │ │ │ │ + this.loadMapObject(); │ │ │ │ │ │ │ │ - if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ - node.style.cursor = style.cursor; │ │ │ │ + // if map didn't load, display warning │ │ │ │ + if (this.mapObject == null) { │ │ │ │ + this.loadWarningMessage(); │ │ │ │ } │ │ │ │ - return node; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: graphicRotate │ │ │ │ - * If a point is to be styled with externalGraphic and rotation, VML fills │ │ │ │ - * cannot be used to display the graphic, because rotation of graphic │ │ │ │ - * fills is not supported by the VML implementation of Internet Explorer. │ │ │ │ - * This method creates a olv:imagedata element inside the VML node, │ │ │ │ - * DXImageTransform.Matrix and BasicImage filters for rotation and │ │ │ │ - * opacity, and a 3-step hack to remove rendering artefacts from the │ │ │ │ - * graphic and preserve the ability of graphics to trigger events. │ │ │ │ - * Finally, OpenLayers methods are used to determine the correct │ │ │ │ - * insertion point of the rotated image, because DXImageTransform.Matrix │ │ │ │ - * does the rotation without the ability to specify a rotation center │ │ │ │ - * point. │ │ │ │ + * APIMethod: removeMap │ │ │ │ + * On being removed from the map, we'll like to remove the invisible 'pane' │ │ │ │ + * div that we added to it on creation. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * xOffset - {Number} rotation center relative to image, x coordinate │ │ │ │ - * yOffset - {Number} rotation center relative to image, y coordinate │ │ │ │ - * style - {Object} │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - graphicRotate: function(node, xOffset, yOffset, style) { │ │ │ │ - var style = style || node._style; │ │ │ │ - var rotation = style.rotation || 0; │ │ │ │ - │ │ │ │ - var aspectRatio, size; │ │ │ │ - if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ - // load the image to determine its size │ │ │ │ - var img = new Image(); │ │ │ │ - img.onreadystatechange = OpenLayers.Function.bind(function() { │ │ │ │ - if (img.readyState == "complete" || │ │ │ │ - img.readyState == "interactive") { │ │ │ │ - aspectRatio = img.width / img.height; │ │ │ │ - size = Math.max(style.pointRadius * 2, │ │ │ │ - style.graphicWidth || 0, │ │ │ │ - style.graphicHeight || 0); │ │ │ │ - xOffset = xOffset * aspectRatio; │ │ │ │ - style.graphicWidth = size * aspectRatio; │ │ │ │ - style.graphicHeight = size; │ │ │ │ - this.graphicRotate(node, xOffset, yOffset, style); │ │ │ │ - } │ │ │ │ - }, this); │ │ │ │ - img.src = style.externalGraphic; │ │ │ │ - │ │ │ │ - // will be called again by the onreadystate handler │ │ │ │ - return; │ │ │ │ - } else { │ │ │ │ - size = Math.max(style.graphicWidth, style.graphicHeight); │ │ │ │ - aspectRatio = style.graphicWidth / style.graphicHeight; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var width = Math.round(style.graphicWidth || size * aspectRatio); │ │ │ │ - var height = Math.round(style.graphicHeight || size); │ │ │ │ - node.style.width = width + "px"; │ │ │ │ - node.style.height = height + "px"; │ │ │ │ - │ │ │ │ - // Three steps are required to remove artefacts for images with │ │ │ │ - // transparent backgrounds (resulting from using DXImageTransform │ │ │ │ - // filters on svg objects), while preserving awareness for browser │ │ │ │ - // events on images: │ │ │ │ - // - Use the fill as usual (like for unrotated images) to handle │ │ │ │ - // events │ │ │ │ - // - specify an imagedata element with the same src as the fill │ │ │ │ - // - style the imagedata element with an AlphaImageLoader filter │ │ │ │ - // with empty src │ │ │ │ - var image = document.getElementById(node.id + "_image"); │ │ │ │ - if (!image) { │ │ │ │ - image = this.createNode("olv:imagedata", node.id + "_image"); │ │ │ │ - node.appendChild(image); │ │ │ │ - } │ │ │ │ - image.style.width = width + "px"; │ │ │ │ - image.style.height = height + "px"; │ │ │ │ - image.src = style.externalGraphic; │ │ │ │ - image.style.filter = │ │ │ │ - "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + │ │ │ │ - "src='', sizingMethod='scale')"; │ │ │ │ - │ │ │ │ - var rot = rotation * Math.PI / 180; │ │ │ │ - var sintheta = Math.sin(rot); │ │ │ │ - var costheta = Math.cos(rot); │ │ │ │ - │ │ │ │ - // do the rotation on the image │ │ │ │ - var filter = │ │ │ │ - "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta + │ │ │ │ - ",M12=" + (-sintheta) + ",M21=" + sintheta + ",M22=" + costheta + │ │ │ │ - ",SizingMethod='auto expand')\n"; │ │ │ │ - │ │ │ │ - // set the opacity (needed for the imagedata) │ │ │ │ - var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ - if (opacity && opacity != 1) { │ │ │ │ - filter += │ │ │ │ - "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + │ │ │ │ - opacity + ")\n"; │ │ │ │ + removeMap: function(map) { │ │ │ │ + if (this.pane && this.pane.parentNode) { │ │ │ │ + this.pane.parentNode.removeChild(this.pane); │ │ │ │ } │ │ │ │ - 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"; │ │ │ │ + OpenLayers.Layer.prototype.removeMap.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: loadWarningMessage │ │ │ │ + * If we can't load the map lib, then display an error message to the │ │ │ │ + * user and tell them where to go for help. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ + * This function sets up the layout for the warning message. Each 3rd │ │ │ │ + * party layer must implement its own getWarningHTML() function to │ │ │ │ + * provide the actual warning message. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + loadWarningMessage: function() { │ │ │ │ │ │ │ │ + this.div.style.backgroundColor = "darkblue"; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setNodeDimension │ │ │ │ - * Get the geometry's bounds, convert it to our vml coordinate system, │ │ │ │ - * then set the node's position, size, and local coordinate system. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - */ │ │ │ │ - setNodeDimension: function(node, geometry) { │ │ │ │ + var viewSize = this.map.getSize(); │ │ │ │ │ │ │ │ - var bbox = geometry.getBounds(); │ │ │ │ - if (bbox) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ + var msgW = Math.min(viewSize.w, 300); │ │ │ │ + var msgH = Math.min(viewSize.h, 200); │ │ │ │ + var size = new OpenLayers.Size(msgW, msgH); │ │ │ │ │ │ │ │ - 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); │ │ │ │ + var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2); │ │ │ │ │ │ │ │ - // 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"; │ │ │ │ + var topLeft = centerPx.add(-size.w / 2, -size.h / 2); │ │ │ │ │ │ │ │ - node.coordorigin = scaledBox.left + " " + scaledBox.top; │ │ │ │ - node.coordsize = scaledBox.getWidth() + " " + scaledBox.getHeight(); │ │ │ │ - } │ │ │ │ + var div = OpenLayers.Util.createDiv(this.name + "_warning", │ │ │ │ + topLeft, │ │ │ │ + size, │ │ │ │ + null, │ │ │ │ + null, │ │ │ │ + null, │ │ │ │ + "auto"); │ │ │ │ + │ │ │ │ + div.style.padding = "7px"; │ │ │ │ + div.style.backgroundColor = "yellow"; │ │ │ │ + │ │ │ │ + div.innerHTML = this.getWarningHTML(); │ │ │ │ + this.div.appendChild(div); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: dashStyle │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * style - {Object} │ │ │ │ + * Method: getWarningHTML │ │ │ │ + * To be implemented by subclasses. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A VML compliant 'stroke-dasharray' value │ │ │ │ + * {String} String with information on why layer is broken, how to get │ │ │ │ + * it working. │ │ │ │ */ │ │ │ │ - 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"; │ │ │ │ - } │ │ │ │ + getWarningHTML: function() { │ │ │ │ + //should be implemented by subclasses │ │ │ │ + return ""; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createNode │ │ │ │ - * Create a new node │ │ │ │ + * Method: display │ │ │ │ + * Set the display on the pane │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * type - {String} Kind of node to draw │ │ │ │ - * id - {String} Id for node │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} A new node of the given type and id │ │ │ │ + * display - {Boolean} │ │ │ │ */ │ │ │ │ - createNode: function(type, id) { │ │ │ │ - var node = document.createElement(type); │ │ │ │ - if (id) { │ │ │ │ - node.id = id; │ │ │ │ - } │ │ │ │ - │ │ │ │ - // IE hack to make elements unselectable, to prevent 'blue flash' │ │ │ │ - // while dragging vectors; #1410 │ │ │ │ - node.unselectable = 'on'; │ │ │ │ - node.onselectstart = OpenLayers.Function.False; │ │ │ │ - │ │ │ │ - return node; │ │ │ │ + display: function(display) { │ │ │ │ + OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ + this.pane.style.display = this.div.style.display; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: nodeTypeCompare │ │ │ │ - * Determine whether a node is of a given type │ │ │ │ - * │ │ │ │ + * Method: setZIndex │ │ │ │ + * Set the z-index order for the pane. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} An VML element │ │ │ │ - * type - {String} Kind of node │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Whether or not the specified node is of the specified type │ │ │ │ + * zIndex - {int} │ │ │ │ */ │ │ │ │ - nodeTypeCompare: function(node, type) { │ │ │ │ - │ │ │ │ - //split type │ │ │ │ - var subType = type; │ │ │ │ - var splitIndex = subType.indexOf(":"); │ │ │ │ - if (splitIndex != -1) { │ │ │ │ - subType = subType.substr(splitIndex + 1); │ │ │ │ - } │ │ │ │ - │ │ │ │ - //split nodeName │ │ │ │ - var nodeName = node.nodeName; │ │ │ │ - splitIndex = nodeName.indexOf(":"); │ │ │ │ - if (splitIndex != -1) { │ │ │ │ - nodeName = nodeName.substr(splitIndex + 1); │ │ │ │ - } │ │ │ │ - │ │ │ │ - return (subType == nodeName); │ │ │ │ + setZIndex: function(zIndex) { │ │ │ │ + OpenLayers.Layer.prototype.setZIndex.apply(this, arguments); │ │ │ │ + this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createRenderRoot │ │ │ │ - * Create the renderer root │ │ │ │ + * Method: moveByPx │ │ │ │ + * Move the layer based on pixel vector. To be implemented by subclasses. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} The specific render engine's root element │ │ │ │ + * Parameters: │ │ │ │ + * dx - {Number} The x coord of the displacement vector. │ │ │ │ + * dy - {Number} The y coord of the displacement vector. │ │ │ │ */ │ │ │ │ - createRenderRoot: function() { │ │ │ │ - return this.nodeFactory(this.container.id + "_vmlRoot", "div"); │ │ │ │ + moveByPx: function(dx, dy) { │ │ │ │ + OpenLayers.Layer.prototype.moveByPx.apply(this, arguments); │ │ │ │ + │ │ │ │ + if (this.dragPanMapObject) { │ │ │ │ + this.dragPanMapObject(dx, -dy); │ │ │ │ + } else { │ │ │ │ + this.moveTo(this.map.getCachedCenter()); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createRoot │ │ │ │ - * Create the main root element │ │ │ │ + * Method: moveTo │ │ │ │ + * Handle calls to move the layer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * suffix - {String} suffix to append to the id │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * zoomChanged - {Boolean} │ │ │ │ + * dragging - {Boolean} │ │ │ │ */ │ │ │ │ - createRoot: function(suffix) { │ │ │ │ - return this.nodeFactory(this.container.id + suffix, "olv:group"); │ │ │ │ - }, │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ │ │ │ │ - /************************************** │ │ │ │ - * * │ │ │ │ - * GEOMETRY DRAWING FUNCTIONS * │ │ │ │ - * * │ │ │ │ - **************************************/ │ │ │ │ + if (this.mapObject != null) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawPoint │ │ │ │ - * Render a point │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or false if the point could not be drawn │ │ │ │ - */ │ │ │ │ - drawPoint: function(node, geometry) { │ │ │ │ - return this.drawCircle(node, geometry, 1); │ │ │ │ - }, │ │ │ │ + var newCenter = this.map.getCenter(); │ │ │ │ + var newZoom = this.map.getZoom(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawCircle │ │ │ │ - * Render a circle. │ │ │ │ - * Size and Center a circle given geometry (x,y center) and radius │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * radius - {float} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} or false if the circle could not ne drawn │ │ │ │ - */ │ │ │ │ - drawCircle: function(node, geometry, radius) { │ │ │ │ - if (!isNaN(geometry.x) && !isNaN(geometry.y)) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ + if (newCenter != null) { │ │ │ │ │ │ │ │ - node.style.left = ((((geometry.x - this.featureDx) / resolution - this.offset.x) | 0) - radius) + "px"; │ │ │ │ - node.style.top = (((geometry.y / resolution - this.offset.y) | 0) - radius) + "px"; │ │ │ │ + var moOldCenter = this.getMapObjectCenter(); │ │ │ │ + var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter); │ │ │ │ │ │ │ │ - var diameter = radius * 2; │ │ │ │ + var moOldZoom = this.getMapObjectZoom(); │ │ │ │ + var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom); │ │ │ │ │ │ │ │ - node.style.width = diameter + "px"; │ │ │ │ - node.style.height = diameter + "px"; │ │ │ │ - return node; │ │ │ │ + if (!(newCenter.equals(oldCenter)) || newZoom != oldZoom) { │ │ │ │ + │ │ │ │ + if (!zoomChanged && oldCenter && this.dragPanMapObject && │ │ │ │ + this.smoothDragPan) { │ │ │ │ + var oldPx = this.map.getViewPortPxFromLonLat(oldCenter); │ │ │ │ + var newPx = this.map.getViewPortPxFromLonLat(newCenter); │ │ │ │ + this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y); │ │ │ │ + } else { │ │ │ │ + var center = this.getMapObjectLonLatFromOLLonLat(newCenter); │ │ │ │ + var zoom = this.getMapObjectZoomFromOLZoom(newZoom); │ │ │ │ + this.setMapObjectCenter(center, zoom, dragging); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - return false; │ │ │ │ }, │ │ │ │ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: drawLineString │ │ │ │ - * Render a linestring. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - drawLineString: function(node, geometry) { │ │ │ │ - return this.drawLine(node, geometry, false); │ │ │ │ - }, │ │ │ │ + /********************************************************/ │ │ │ │ + /* */ │ │ │ │ + /* Baselayer Functions */ │ │ │ │ + /* */ │ │ │ │ + /********************************************************/ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawLinearRing │ │ │ │ - * Render a linearring │ │ │ │ + * Method: getLonLatFromViewPortPx │ │ │ │ + * Get a map location from a pixel location │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ + * viewPortPx - {<OpenLayers.Pixel>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * {<OpenLayers.LonLat>} An OpenLayers.LonLat which is the passed-in view │ │ │ │ + * port OpenLayers.Pixel, translated into lon/lat by map lib │ │ │ │ + * If the map lib is not loaded or not centered, returns null │ │ │ │ */ │ │ │ │ - drawLinearRing: function(node, geometry) { │ │ │ │ - return this.drawLine(node, geometry, true); │ │ │ │ + getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ + var lonlat = null; │ │ │ │ + if ((this.mapObject != null) && │ │ │ │ + (this.getMapObjectCenter() != null)) { │ │ │ │ + var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx); │ │ │ │ + var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel); │ │ │ │ + lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat); │ │ │ │ + } │ │ │ │ + return lonlat; │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: DrawLine │ │ │ │ - * Render a line. │ │ │ │ - * │ │ │ │ + * Method: getViewPortPxFromLonLat │ │ │ │ + * Get a pixel location from a map location │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * closeLine - {Boolean} Close the line? (make it a ring?) │ │ │ │ - * │ │ │ │ + * lonlat - {<OpenLayers.LonLat>} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * {<OpenLayers.Pixel>} An OpenLayers.Pixel which is the passed-in │ │ │ │ + * OpenLayers.LonLat, translated into view port pixels by map lib │ │ │ │ + * If map lib is not loaded or not centered, returns null │ │ │ │ */ │ │ │ │ - drawLine: function(node, geometry, closeLine) { │ │ │ │ - │ │ │ │ - this.setNodeDimension(node, geometry); │ │ │ │ + getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ + var viewPortPx = null; │ │ │ │ + if ((this.mapObject != null) && │ │ │ │ + (this.getMapObjectCenter() != null)) { │ │ │ │ │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var numComponents = geometry.components.length; │ │ │ │ - var parts = new Array(numComponents); │ │ │ │ + var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat); │ │ │ │ + var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat); │ │ │ │ │ │ │ │ - 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 "; │ │ │ │ + viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel); │ │ │ │ } │ │ │ │ - var end = (closeLine) ? " x e" : " e"; │ │ │ │ - node.path = "m" + parts.join("") + end; │ │ │ │ - return node; │ │ │ │ + return viewPortPx; │ │ │ │ }, │ │ │ │ │ │ │ │ + /********************************************************/ │ │ │ │ + /* */ │ │ │ │ + /* Translation Functions */ │ │ │ │ + /* */ │ │ │ │ + /* The following functions translate Map Object and */ │ │ │ │ + /* OL formats for Pixel, LonLat */ │ │ │ │ + /* */ │ │ │ │ + /********************************************************/ │ │ │ │ + │ │ │ │ + // │ │ │ │ + // TRANSLATION: MapObject LatLng <-> OpenLayers.LonLat │ │ │ │ + // │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: drawPolygon │ │ │ │ - * Render a polygon │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * Method: getOLLonLatFromMapObjectLonLat │ │ │ │ + * Get an OL style map location from a 3rd party style map location │ │ │ │ + * │ │ │ │ + * Parameters │ │ │ │ + * moLonLat - {Object} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ + * {<OpenLayers.LonLat>} An OpenLayers.LonLat, translated from the passed in │ │ │ │ + * MapObject LonLat │ │ │ │ + * Returns null if null value is passed in │ │ │ │ */ │ │ │ │ - drawPolygon: function(node, geometry) { │ │ │ │ - this.setNodeDimension(node, geometry); │ │ │ │ - │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - │ │ │ │ - var path = []; │ │ │ │ - var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y; │ │ │ │ - for (j = 0, jj = geometry.components.length; j < jj; j++) { │ │ │ │ - path.push("m"); │ │ │ │ - points = geometry.components[j].components; │ │ │ │ - // we only close paths of interior rings with area │ │ │ │ - area = (j === 0); │ │ │ │ - first = null; │ │ │ │ - second = null; │ │ │ │ - for (i = 0, ii = points.length; i < ii; i++) { │ │ │ │ - comp = points[i]; │ │ │ │ - x = ((comp.x - this.featureDx) / resolution - this.offset.x) | 0; │ │ │ │ - y = (comp.y / resolution - this.offset.y) | 0; │ │ │ │ - pathComp = " " + x + "," + y; │ │ │ │ - path.push(pathComp); │ │ │ │ - if (i == 0) { │ │ │ │ - path.push(" l"); │ │ │ │ - } │ │ │ │ - if (!area) { │ │ │ │ - // IE improperly renders sub-paths that have no area. │ │ │ │ - // Instead of checking the area of every ring, we confirm │ │ │ │ - // the ring has at least three distinct points. This does │ │ │ │ - // not catch all non-zero area cases, but it greatly improves │ │ │ │ - // interior ring digitizing and is a minor performance hit │ │ │ │ - // when rendering rings with many points. │ │ │ │ - if (!first) { │ │ │ │ - first = pathComp; │ │ │ │ - } else if (first != pathComp) { │ │ │ │ - if (!second) { │ │ │ │ - second = pathComp; │ │ │ │ - } else if (second != pathComp) { │ │ │ │ - // stop looking │ │ │ │ - area = true; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - path.push(area ? " x " : " "); │ │ │ │ + getOLLonLatFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + var olLonLat = null; │ │ │ │ + if (moLonLat != null) { │ │ │ │ + var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ + var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ + olLonLat = new OpenLayers.LonLat(lon, lat); │ │ │ │ } │ │ │ │ - path.push("e"); │ │ │ │ - node.path = path.join(""); │ │ │ │ - return node; │ │ │ │ + return olLonLat; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: drawRectangle │ │ │ │ - * Render a rectangle │ │ │ │ - * │ │ │ │ + * Method: getMapObjectLonLatFromOLLonLat │ │ │ │ + * Get a 3rd party map location from an OL map location. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * node - {DOMElement} │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * olLonLat - {<OpenLayers.LonLat>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} │ │ │ │ - */ │ │ │ │ - drawRectangle: function(node, geometry) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - │ │ │ │ - node.style.left = (((geometry.x - this.featureDx) / resolution - this.offset.x) | 0) + "px"; │ │ │ │ - node.style.top = ((geometry.y / resolution - this.offset.y) | 0) + "px"; │ │ │ │ - node.style.width = ((geometry.width / resolution) | 0) + "px"; │ │ │ │ - node.style.height = ((geometry.height / resolution) | 0) + "px"; │ │ │ │ - │ │ │ │ - return node; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: drawText │ │ │ │ - * This method is only called by the renderer itself. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * featureId - {String} │ │ │ │ - * style - │ │ │ │ - * location - {<OpenLayers.Geometry.Point>} │ │ │ │ + * {Object} A MapObject LonLat, translated from the passed in │ │ │ │ + * OpenLayers.LonLat │ │ │ │ + * Returns null if null value is passed in │ │ │ │ */ │ │ │ │ - drawText: function(featureId, style, location) { │ │ │ │ - var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect"); │ │ │ │ - var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox"); │ │ │ │ - │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - label.style.left = (((location.x - this.featureDx) / resolution - this.offset.x) | 0) + "px"; │ │ │ │ - label.style.top = ((location.y / resolution - this.offset.y) | 0) + "px"; │ │ │ │ - label.style.flip = "y"; │ │ │ │ - │ │ │ │ - textbox.innerText = style.label; │ │ │ │ - │ │ │ │ - if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ - textbox.style.cursor = style.cursor; │ │ │ │ - } │ │ │ │ - if (style.fontColor) { │ │ │ │ - textbox.style.color = style.fontColor; │ │ │ │ - } │ │ │ │ - if (style.fontOpacity) { │ │ │ │ - textbox.style.filter = 'alpha(opacity=' + (style.fontOpacity * 100) + ')'; │ │ │ │ - } │ │ │ │ - if (style.fontFamily) { │ │ │ │ - textbox.style.fontFamily = style.fontFamily; │ │ │ │ - } │ │ │ │ - if (style.fontSize) { │ │ │ │ - textbox.style.fontSize = style.fontSize; │ │ │ │ - } │ │ │ │ - if (style.fontWeight) { │ │ │ │ - textbox.style.fontWeight = style.fontWeight; │ │ │ │ - } │ │ │ │ - if (style.fontStyle) { │ │ │ │ - textbox.style.fontStyle = style.fontStyle; │ │ │ │ - } │ │ │ │ - if (style.labelSelect === true) { │ │ │ │ - label._featureId = featureId; │ │ │ │ - textbox._featureId = featureId; │ │ │ │ - textbox._geometry = location; │ │ │ │ - textbox._geometryClass = location.CLASS_NAME; │ │ │ │ - } │ │ │ │ - textbox.style.whiteSpace = "nowrap"; │ │ │ │ - // fun with IE: IE7 in standards compliant mode does not display any │ │ │ │ - // text with a left inset of 0. So we set this to 1px and subtract one │ │ │ │ - // pixel later when we set label.style.left │ │ │ │ - textbox.inset = "1px,0px,0px,0px"; │ │ │ │ - │ │ │ │ - if (!label.parentNode) { │ │ │ │ - label.appendChild(textbox); │ │ │ │ - this.textRoot.appendChild(label); │ │ │ │ + getMapObjectLonLatFromOLLonLat: function(olLonLat) { │ │ │ │ + var moLatLng = null; │ │ │ │ + if (olLonLat != null) { │ │ │ │ + moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, │ │ │ │ + olLonLat.lat); │ │ │ │ } │ │ │ │ + return moLatLng; │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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"; │ │ │ │ │ │ │ │ - }, │ │ │ │ + // │ │ │ │ + // TRANSLATION: MapObject Pixel <-> OpenLayers.Pixel │ │ │ │ + // │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveRoot │ │ │ │ - * moves this renderer's root to a different renderer. │ │ │ │ - * │ │ │ │ + * Method: getOLPixelFromMapObjectPixel │ │ │ │ + * Get an OL pixel location from a 3rd party pixel location. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * renderer - {<OpenLayers.Renderer>} target renderer for the moved root │ │ │ │ - * root - {DOMElement} optional root node. To be used when this renderer │ │ │ │ - * holds roots from multiple layers to tell this method which one to │ │ │ │ - * detach │ │ │ │ + * moPixel - {Object} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} true if successful, false otherwise │ │ │ │ + * {<OpenLayers.Pixel>} An OpenLayers.Pixel, translated from the passed in │ │ │ │ + * MapObject Pixel │ │ │ │ + * Returns null if null value is passed in │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ + getOLPixelFromMapObjectPixel: function(moPixel) { │ │ │ │ + var olPixel = null; │ │ │ │ + if (moPixel != null) { │ │ │ │ + var x = this.getXFromMapObjectPixel(moPixel); │ │ │ │ + var y = this.getYFromMapObjectPixel(moPixel); │ │ │ │ + olPixel = new OpenLayers.Pixel(x, y); │ │ │ │ } │ │ │ │ - layer && layer.renderer.clear(); │ │ │ │ - OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments); │ │ │ │ - layer && layer.redraw(); │ │ │ │ + return olPixel; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: importSymbol │ │ │ │ - * add a new symbol definition from the rendererer's symbol hash │ │ │ │ - * │ │ │ │ + * Method: getMapObjectPixelFromOLPixel │ │ │ │ + * Get a 3rd party pixel location from an OL pixel location │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * graphicName - {String} name of the symbol to import │ │ │ │ + * olPixel - {<OpenLayers.Pixel>} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} - hash of {DOMElement} "symbol" and {Number} "size" │ │ │ │ + * {Object} A MapObject Pixel, translated from the passed in │ │ │ │ + * OpenLayers.Pixel │ │ │ │ + * Returns null if null value is passed in │ │ │ │ */ │ │ │ │ - importSymbol: function(graphicName) { │ │ │ │ - var id = this.container.id + "-" + graphicName; │ │ │ │ - │ │ │ │ - // check if symbol already exists in the cache │ │ │ │ - var cache = this.symbolCache[id]; │ │ │ │ - if (cache) { │ │ │ │ - return cache; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ - if (!symbol) { │ │ │ │ - throw new Error(graphicName + ' is not a valid symbol name'); │ │ │ │ - } │ │ │ │ - │ │ │ │ - var symbolExtent = new OpenLayers.Bounds( │ │ │ │ - Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ - │ │ │ │ - var pathitems = ["m"]; │ │ │ │ - for (var i = 0; i < symbol.length; i = i + 2) { │ │ │ │ - var x = symbol[i]; │ │ │ │ - var y = symbol[i + 1]; │ │ │ │ - symbolExtent.left = Math.min(symbolExtent.left, x); │ │ │ │ - symbolExtent.bottom = Math.min(symbolExtent.bottom, y); │ │ │ │ - symbolExtent.right = Math.max(symbolExtent.right, x); │ │ │ │ - symbolExtent.top = Math.max(symbolExtent.top, y); │ │ │ │ - │ │ │ │ - pathitems.push(x); │ │ │ │ - pathitems.push(y); │ │ │ │ - if (i == 0) { │ │ │ │ - pathitems.push("l"); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - pathitems.push("x e"); │ │ │ │ - var path = pathitems.join(" "); │ │ │ │ - │ │ │ │ - var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2; │ │ │ │ - if (diff > 0) { │ │ │ │ - symbolExtent.bottom = symbolExtent.bottom - diff; │ │ │ │ - symbolExtent.top = symbolExtent.top + diff; │ │ │ │ - } else { │ │ │ │ - symbolExtent.left = symbolExtent.left + diff; │ │ │ │ - symbolExtent.right = symbolExtent.right - diff; │ │ │ │ + getMapObjectPixelFromOLPixel: function(olPixel) { │ │ │ │ + var moPixel = null; │ │ │ │ + if (olPixel != null) { │ │ │ │ + moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y); │ │ │ │ } │ │ │ │ - │ │ │ │ - cache = { │ │ │ │ - path: path, │ │ │ │ - size: symbolExtent.getWidth(), // equals getHeight() now │ │ │ │ - left: symbolExtent.left, │ │ │ │ - bottom: symbolExtent.bottom │ │ │ │ - }; │ │ │ │ - this.symbolCache[id] = cache; │ │ │ │ - │ │ │ │ - return cache; │ │ │ │ + return moPixel; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer.VML" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.EventPane" │ │ │ │ }); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * 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/Format.js │ │ │ │ + OpenLayers/Layer/FixedZoomLevels.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ + * @requires OpenLayers/Layer.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.Layer.FixedZoomLevels │ │ │ │ + * Some Layers will already have established zoom levels (like google │ │ │ │ + * or ve). Instead of trying to determine them and populate a resolutions[] │ │ │ │ + * Array with those values, we will hijack the resolution functionality │ │ │ │ + * here. │ │ │ │ + * │ │ │ │ + * When you subclass FixedZoomLevels: │ │ │ │ + * │ │ │ │ + * The initResolutions() call gets nullified, meaning no resolutions[] array │ │ │ │ + * is set up. Which would be a big problem getResolution() in Layer, since │ │ │ │ + * it merely takes map.zoom and indexes into resolutions[]... but.... │ │ │ │ + * │ │ │ │ + * The getResolution() call is also overridden. Instead of using the │ │ │ │ + * resolutions[] array, we simply calculate the current resolution based │ │ │ │ + * on the current extent and the current map size. But how will we be able │ │ │ │ + * to calculate the current extent without knowing the resolution...? │ │ │ │ + * │ │ │ │ + * The getExtent() function is also overridden. Instead of calculating extent │ │ │ │ + * based on the center point and the current resolution, we instead │ │ │ │ + * calculate the extent by getting the lonlats at the top-left and │ │ │ │ + * bottom-right by using the getLonLatFromViewPortPx() translation function, │ │ │ │ + * taken from the pixel locations (0,0) and the size of the map. But how │ │ │ │ + * will we be able to do lonlat-px translation without resolution....? │ │ │ │ + * │ │ │ │ + * The getZoomForResolution() method is overridden. Instead of indexing into │ │ │ │ + * the resolutions[] array, we call OpenLayers.Layer.getExent(), passing in │ │ │ │ + * the desired resolution. With this extent, we then call getZoomForExtent() │ │ │ │ + * │ │ │ │ + * │ │ │ │ + * Whenever you implement a layer using OpenLayers.Layer.FixedZoomLevels, │ │ │ │ + * it is your responsibility to provide the following three functions: │ │ │ │ + * │ │ │ │ + * - getLonLatFromViewPortPx │ │ │ │ + * - getViewPortPxFromLonLat │ │ │ │ + * - getZoomForExtent │ │ │ │ + * │ │ │ │ + * ...those three functions should generally be provided by any reasonable │ │ │ │ + * API that you might be working from. │ │ │ │ + * │ │ │ │ */ │ │ │ │ -OpenLayers.Format = OpenLayers.Class({ │ │ │ │ +OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: options │ │ │ │ - * {Object} A reference to options passed to the constructor. │ │ │ │ - */ │ │ │ │ - options: null, │ │ │ │ + /********************************************************/ │ │ │ │ + /* */ │ │ │ │ + /* Baselayer Functions */ │ │ │ │ + /* */ │ │ │ │ + /* The following functions must all be implemented */ │ │ │ │ + /* by all base layers */ │ │ │ │ + /* */ │ │ │ │ + /********************************************************/ │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: externalProjection │ │ │ │ - * {<OpenLayers.Projection>} When passed a externalProjection and │ │ │ │ - * internalProjection, the format will reproject the geometries it │ │ │ │ - * reads or writes. The externalProjection is the projection used by │ │ │ │ - * the content which is passed into read or which comes out of write. │ │ │ │ - * In order to reproject, a projection transformation function for the │ │ │ │ - * specified projections must be available. This support may be │ │ │ │ - * provided via proj4js or via a custom transformation function. See │ │ │ │ - * {<OpenLayers.Projection.addTransform>} for more information on │ │ │ │ - * custom transformations. │ │ │ │ + * Constructor: OpenLayers.Layer.FixedZoomLevels │ │ │ │ + * Create a new fixed zoom levels layer. │ │ │ │ */ │ │ │ │ - externalProjection: null, │ │ │ │ + initialize: function() { │ │ │ │ + //this class is only just to add the following functions... │ │ │ │ + // nothing to actually do here... but it is probably a good │ │ │ │ + // idea to have layers that use these functions call this │ │ │ │ + // inititalize() anyways, in case at some point we decide we │ │ │ │ + // do want to put some functionality or state in here. │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: internalProjection │ │ │ │ - * {<OpenLayers.Projection>} When passed a externalProjection and │ │ │ │ - * internalProjection, the format will reproject the geometries it │ │ │ │ - * reads or writes. The internalProjection is the projection used by │ │ │ │ - * the geometries which are returned by read or which are passed into │ │ │ │ - * write. In order to reproject, a projection transformation function │ │ │ │ - * for the specified projections must be available. This support may be │ │ │ │ - * provided via proj4js or via a custom transformation function. See │ │ │ │ - * {<OpenLayers.Projection.addTransform>} for more information on │ │ │ │ - * custom transformations. │ │ │ │ + * Method: initResolutions │ │ │ │ + * Populate the resolutions array │ │ │ │ */ │ │ │ │ - internalProjection: null, │ │ │ │ + initResolutions: function() { │ │ │ │ + │ │ │ │ + var props = ['minZoomLevel', 'maxZoomLevel', 'numZoomLevels']; │ │ │ │ + │ │ │ │ + for (var i = 0, len = props.length; i < len; i++) { │ │ │ │ + var property = props[i]; │ │ │ │ + this[property] = (this.options[property] != null) ? │ │ │ │ + this.options[property] : │ │ │ │ + this.map[property]; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if ((this.minZoomLevel == null) || │ │ │ │ + (this.minZoomLevel < this.MIN_ZOOM_LEVEL)) { │ │ │ │ + this.minZoomLevel = this.MIN_ZOOM_LEVEL; │ │ │ │ + } │ │ │ │ + │ │ │ │ + // │ │ │ │ + // At this point, we know what the minimum desired zoom level is, and │ │ │ │ + // we must calculate the total number of zoom levels. │ │ │ │ + // │ │ │ │ + // Because we allow for the setting of either the 'numZoomLevels' │ │ │ │ + // or the 'maxZoomLevel' properties... on either the layer or the │ │ │ │ + // map, we have to define some rules to see which we take into │ │ │ │ + // account first in this calculation. │ │ │ │ + // │ │ │ │ + // The following is the precedence list for these properties: │ │ │ │ + // │ │ │ │ + // (1) numZoomLevels set on layer │ │ │ │ + // (2) maxZoomLevel set on layer │ │ │ │ + // (3) numZoomLevels set on map │ │ │ │ + // (4) maxZoomLevel set on map* │ │ │ │ + // (5) none of the above* │ │ │ │ + // │ │ │ │ + // *Note that options (4) and (5) are only possible if the user │ │ │ │ + // _explicitly_ sets the 'numZoomLevels' property on the map to │ │ │ │ + // null, since it is set by default to 16. │ │ │ │ + // │ │ │ │ + │ │ │ │ + // │ │ │ │ + // Note to future: In 3.0, I think we should remove the default │ │ │ │ + // value of 16 for map.numZoomLevels. Rather, I think that value │ │ │ │ + // should be set as a default on the Layer.WMS class. If someone │ │ │ │ + // creates a 3rd party layer and does not specify any 'minZoomLevel', │ │ │ │ + // 'maxZoomLevel', or 'numZoomLevels', and has not explicitly │ │ │ │ + // specified any of those on the map object either.. then I think │ │ │ │ + // it is fair to say that s/he wants all the zoom levels available. │ │ │ │ + // │ │ │ │ + // By making map.numZoomLevels *null* by default, that will be the │ │ │ │ + // case. As it is, I don't feel comfortable changing that right now │ │ │ │ + // as it would be a glaring API change and actually would probably │ │ │ │ + // break many peoples' codes. │ │ │ │ + // │ │ │ │ + │ │ │ │ + //the number of zoom levels we'd like to have. │ │ │ │ + var desiredZoomLevels; │ │ │ │ + │ │ │ │ + //this is the maximum number of zoom levels the layer will allow, │ │ │ │ + // given the specified starting minimum zoom level. │ │ │ │ + var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1; │ │ │ │ + │ │ │ │ + if (((this.options.numZoomLevels == null) && │ │ │ │ + (this.options.maxZoomLevel != null)) // (2) │ │ │ │ + || │ │ │ │ + ((this.numZoomLevels == null) && │ │ │ │ + (this.maxZoomLevel != null)) // (4) │ │ │ │ + ) { │ │ │ │ + //calculate based on specified maxZoomLevel (on layer or map) │ │ │ │ + desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1; │ │ │ │ + } else { │ │ │ │ + //calculate based on specified numZoomLevels (on layer or map) │ │ │ │ + // this covers cases (1) and (3) │ │ │ │ + desiredZoomLevels = this.numZoomLevels; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (desiredZoomLevels != null) { │ │ │ │ + //Now that we know what we would *like* the number of zoom levels │ │ │ │ + // to be, based on layer or map options, we have to make sure that │ │ │ │ + // it does not conflict with the actual limit, as specified by │ │ │ │ + // the constants on the layer itself (and calculated into the │ │ │ │ + // 'limitZoomLevels' variable). │ │ │ │ + this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels); │ │ │ │ + } else { │ │ │ │ + // case (5) -- neither 'numZoomLevels' not 'maxZoomLevel' was │ │ │ │ + // set on either the layer or the map. So we just use the │ │ │ │ + // maximum limit as calculated by the layer's constants. │ │ │ │ + this.numZoomLevels = limitZoomLevels; │ │ │ │ + } │ │ │ │ + │ │ │ │ + //now that the 'numZoomLevels' is appropriately, safely set, │ │ │ │ + // we go back and re-calculate the 'maxZoomLevel'. │ │ │ │ + this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1; │ │ │ │ + │ │ │ │ + if (this.RESOLUTIONS != null) { │ │ │ │ + var resolutionsIndex = 0; │ │ │ │ + this.resolutions = []; │ │ │ │ + for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) { │ │ │ │ + this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i]; │ │ │ │ + } │ │ │ │ + this.maxResolution = this.resolutions[0]; │ │ │ │ + this.minResolution = this.resolutions[this.resolutions.length - 1]; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: data │ │ │ │ - * {Object} When <keepData> is true, this is the parsed string sent to │ │ │ │ - * <read>. │ │ │ │ + * APIMethod: getResolution │ │ │ │ + * Get the current map resolution │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Float} Map units per Pixel │ │ │ │ */ │ │ │ │ - data: null, │ │ │ │ + getResolution: function() { │ │ │ │ + │ │ │ │ + if (this.resolutions != null) { │ │ │ │ + return OpenLayers.Layer.prototype.getResolution.apply(this, arguments); │ │ │ │ + } else { │ │ │ │ + var resolution = null; │ │ │ │ + │ │ │ │ + var viewSize = this.map.getSize(); │ │ │ │ + var extent = this.getExtent(); │ │ │ │ + │ │ │ │ + if ((viewSize != null) && (extent != null)) { │ │ │ │ + resolution = Math.max(extent.getWidth() / viewSize.w, │ │ │ │ + extent.getHeight() / viewSize.h); │ │ │ │ + } │ │ │ │ + return resolution; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: keepData │ │ │ │ - * {Object} Maintain a reference (<data>) to the most recently read data. │ │ │ │ - * Default is false. │ │ │ │ + * APIMethod: getExtent │ │ │ │ + * Calculates using px-> lonlat translation functions on tl and br │ │ │ │ + * corners of viewport │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Bounds>} A Bounds object which represents the lon/lat │ │ │ │ + * bounds of the current viewPort. │ │ │ │ */ │ │ │ │ - keepData: false, │ │ │ │ + getExtent: function() { │ │ │ │ + var size = this.map.getSize(); │ │ │ │ + var tl = this.getLonLatFromViewPortPx({ │ │ │ │ + x: 0, │ │ │ │ + y: 0 │ │ │ │ + }); │ │ │ │ + var br = this.getLonLatFromViewPortPx({ │ │ │ │ + x: size.w, │ │ │ │ + y: size.h │ │ │ │ + }); │ │ │ │ + │ │ │ │ + if ((tl != null) && (br != null)) { │ │ │ │ + return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat); │ │ │ │ + } else { │ │ │ │ + return null; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format │ │ │ │ - * Instances of this class are not useful. See one of the subclasses. │ │ │ │ + * Method: getZoomForResolution │ │ │ │ + * Get the zoom level for a given resolution │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object with properties to set on the │ │ │ │ - * format │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * keepData - {Boolean} If true, upon <read>, the data property will be │ │ │ │ - * set to the parsed object (e.g. the json or xml object). │ │ │ │ + * resolution - {Float} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * An instance of OpenLayers.Format │ │ │ │ + * {Integer} A suitable zoom level for the specified resolution. │ │ │ │ + * If no baselayer is set, returns null. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.options = options; │ │ │ │ + getZoomForResolution: function(resolution) { │ │ │ │ + │ │ │ │ + if (this.resolutions != null) { │ │ │ │ + return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments); │ │ │ │ + } else { │ │ │ │ + var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []); │ │ │ │ + return this.getZoomForExtent(extent); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Clean up. │ │ │ │ - */ │ │ │ │ - destroy: function() {}, │ │ │ │ + │ │ │ │ + │ │ │ │ + │ │ │ │ + /********************************************************/ │ │ │ │ + /* */ │ │ │ │ + /* Translation Functions */ │ │ │ │ + /* */ │ │ │ │ + /* The following functions translate GMaps and OL */ │ │ │ │ + /* formats for Pixel, LonLat, Bounds, and Zoom */ │ │ │ │ + /* */ │ │ │ │ + /********************************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // │ │ │ │ + // TRANSLATION: MapObject Zoom <-> OpenLayers Zoom │ │ │ │ + // │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: read │ │ │ │ - * Read data from a string, and return an object whose type depends on the │ │ │ │ - * subclass. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * data - {string} Data to read/parse. │ │ │ │ + * Method: getOLZoomFromMapObjectZoom │ │ │ │ + * Get the OL zoom index from the map object zoom level │ │ │ │ * │ │ │ │ + * Parameters: │ │ │ │ + * moZoom - {Integer} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * Depends on the subclass │ │ │ │ + * {Integer} An OpenLayers Zoom level, translated from the passed in zoom │ │ │ │ + * Returns null if null value is passed in │ │ │ │ */ │ │ │ │ - read: function(data) { │ │ │ │ - throw new Error('Read not implemented.'); │ │ │ │ + getOLZoomFromMapObjectZoom: function(moZoom) { │ │ │ │ + var zoom = null; │ │ │ │ + if (moZoom != null) { │ │ │ │ + zoom = moZoom - this.minZoomLevel; │ │ │ │ + if (this.map.baseLayer !== this) { │ │ │ │ + zoom = this.map.baseLayer.getZoomForResolution( │ │ │ │ + this.getResolutionForZoom(zoom) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return zoom; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: write │ │ │ │ - * Accept an object, and return a string. │ │ │ │ + * Method: getMapObjectZoomFromOLZoom │ │ │ │ + * Get the map object zoom level from the OL zoom level │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * object - {Object} Object to be serialized │ │ │ │ - * │ │ │ │ + * olZoom - {Integer} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} A string representation of the object. │ │ │ │ + * {Integer} A MapObject level, translated from the passed in olZoom │ │ │ │ + * Returns null if null value is passed in │ │ │ │ */ │ │ │ │ - write: function(object) { │ │ │ │ - throw new Error('Write not implemented.'); │ │ │ │ + getMapObjectZoomFromOLZoom: function(olZoom) { │ │ │ │ + var zoom = null; │ │ │ │ + if (olZoom != null) { │ │ │ │ + zoom = olZoom + this.minZoomLevel; │ │ │ │ + if (this.map.baseLayer !== this) { │ │ │ │ + zoom = this.getZoomForResolution( │ │ │ │ + this.map.baseLayer.getResolutionForZoom(zoom) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return zoom; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels" │ │ │ │ }); │ │ │ │ + │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/JSON.js │ │ │ │ + OpenLayers/Layer/Google.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ -/** │ │ │ │ - * 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/Layer/SphericalMercator.js │ │ │ │ + * @requires OpenLayers/Layer/EventPane.js │ │ │ │ + * @requires OpenLayers/Layer/FixedZoomLevels.js │ │ │ │ + * @requires OpenLayers/Lang.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.JSON │ │ │ │ - * A parser to read/write JSON safely. Create a new instance with the │ │ │ │ - * <OpenLayers.Format.JSON> constructor. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Layer.Google │ │ │ │ + * │ │ │ │ + * Provides a wrapper for Google's Maps API │ │ │ │ + * Normally the Terms of Use for this API do not allow wrapping, but Google │ │ │ │ + * have provided written consent to OpenLayers for this - see email in │ │ │ │ + * http://osgeo-org.1560.n6.nabble.com/Google-Maps-API-Terms-of-Use-changes-tp4910013p4911981.html │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format> │ │ │ │ + * - <OpenLayers.Layer.SphericalMercator> │ │ │ │ + * - <OpenLayers.Layer.EventPane> │ │ │ │ + * - <OpenLayers.Layer.FixedZoomLevels> │ │ │ │ */ │ │ │ │ -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: " ", │ │ │ │ +OpenLayers.Layer.Google = OpenLayers.Class( │ │ │ │ + OpenLayers.Layer.EventPane, │ │ │ │ + OpenLayers.Layer.FixedZoomLevels, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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", │ │ │ │ + /** │ │ │ │ + * Constant: MIN_ZOOM_LEVEL │ │ │ │ + * {Integer} 0 │ │ │ │ + */ │ │ │ │ + MIN_ZOOM_LEVEL: 0, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: level │ │ │ │ - * {Integer} For "pretty" printing, this is incremented/decremented during │ │ │ │ - * serialization. │ │ │ │ - */ │ │ │ │ - level: 0, │ │ │ │ + /** │ │ │ │ + * Constant: MAX_ZOOM_LEVEL │ │ │ │ + * {Integer} 21 │ │ │ │ + */ │ │ │ │ + MAX_ZOOM_LEVEL: 21, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: pretty │ │ │ │ - * {Boolean} Serialize with extra whitespace for structure. This is set │ │ │ │ - * by the <write> method. │ │ │ │ - */ │ │ │ │ - pretty: false, │ │ │ │ + /** │ │ │ │ + * Constant: RESOLUTIONS │ │ │ │ + * {Array(Float)} Hardcode these resolutions so that they are more closely │ │ │ │ + * tied with the standard wms projection │ │ │ │ + */ │ │ │ │ + RESOLUTIONS: [ │ │ │ │ + 1.40625, │ │ │ │ + 0.703125, │ │ │ │ + 0.3515625, │ │ │ │ + 0.17578125, │ │ │ │ + 0.087890625, │ │ │ │ + 0.0439453125, │ │ │ │ + 0.02197265625, │ │ │ │ + 0.010986328125, │ │ │ │ + 0.0054931640625, │ │ │ │ + 0.00274658203125, │ │ │ │ + 0.001373291015625, │ │ │ │ + 0.0006866455078125, │ │ │ │ + 0.00034332275390625, │ │ │ │ + 0.000171661376953125, │ │ │ │ + 0.0000858306884765625, │ │ │ │ + 0.00004291534423828125, │ │ │ │ + 0.00002145767211914062, │ │ │ │ + 0.00001072883605957031, │ │ │ │ + 0.00000536441802978515, │ │ │ │ + 0.00000268220901489257, │ │ │ │ + 0.0000013411045074462891, │ │ │ │ + 0.00000067055225372314453 │ │ │ │ + ], │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: nativeJSON │ │ │ │ - * {Boolean} Does the browser support native json? │ │ │ │ - */ │ │ │ │ - nativeJSON: (function() { │ │ │ │ - return !!(window.JSON && typeof JSON.parse == "function" && typeof JSON.stringify == "function"); │ │ │ │ - })(), │ │ │ │ + /** │ │ │ │ + * APIProperty: type │ │ │ │ + * {GMapType} │ │ │ │ + */ │ │ │ │ + type: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Format.JSON │ │ │ │ - * Create a new parser for JSON. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ - */ │ │ │ │ + /** │ │ │ │ + * APIProperty: wrapDateLine │ │ │ │ + * {Boolean} Allow user to pan forever east/west. Default is true. │ │ │ │ + * Setting this to false only restricts panning if │ │ │ │ + * <sphericalMercator> is true. │ │ │ │ + */ │ │ │ │ + wrapDateLine: true, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Deserialize a json string. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * json - {String} A JSON string │ │ │ │ - * filter - {Function} A function which will be called for every key and │ │ │ │ - * value at every level of the final result. Each value will be │ │ │ │ - * replaced by the result of the filter function. This can be used to │ │ │ │ - * reform generic objects into instances of classes, or to transform │ │ │ │ - * date strings into Date objects. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object, array, string, or number . │ │ │ │ - */ │ │ │ │ - read: function(json, filter) { │ │ │ │ - var object; │ │ │ │ - if (this.nativeJSON) { │ │ │ │ - object = JSON.parse(json, filter); │ │ │ │ - } else try { │ │ │ │ - /** │ │ │ │ - * Parsing happens in three stages. In the first stage, we run the │ │ │ │ - * text against a regular expression which looks for non-JSON │ │ │ │ - * characters. We are especially concerned with '()' and 'new' │ │ │ │ - * because they can cause invocation, and '=' because it can │ │ │ │ - * cause mutation. But just to be safe, we will reject all │ │ │ │ - * unexpected characters. │ │ │ │ - */ │ │ │ │ - if (/^[\],:{}\s]*$/.test(json.replace(/\\["\\\/bfnrtu]/g, '@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { │ │ │ │ + /** │ │ │ │ + * APIProperty: sphericalMercator │ │ │ │ + * {Boolean} Should the map act as a mercator-projected map? This will │ │ │ │ + * cause all interactions with the map to be in the actual map │ │ │ │ + * projection, which allows support for vector drawing, overlaying │ │ │ │ + * other maps, etc. │ │ │ │ + */ │ │ │ │ + sphericalMercator: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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 + ')'); │ │ │ │ + /** │ │ │ │ + * Property: version │ │ │ │ + * {Number} The version of the Google Maps API │ │ │ │ + */ │ │ │ │ + version: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Google │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} A name for the layer. │ │ │ │ + * options - {Object} An optional object whose properties will be set │ │ │ │ + * on the layer. │ │ │ │ + */ │ │ │ │ + initialize: function(name, options) { │ │ │ │ + options = options || {}; │ │ │ │ + if (!options.version) { │ │ │ │ + options.version = typeof GMap2 === "function" ? "2" : "3"; │ │ │ │ + } │ │ │ │ + var mixin = OpenLayers.Layer.Google["v" + │ │ │ │ + options.version.replace(/\./g, "_")]; │ │ │ │ + if (mixin) { │ │ │ │ + OpenLayers.Util.applyDefaults(options, mixin); │ │ │ │ + } else { │ │ │ │ + throw "Unsupported Google Maps API version: " + options.version; │ │ │ │ } │ │ │ │ - } catch (e) { │ │ │ │ - // Fall through if the regexp test fails. │ │ │ │ - } │ │ │ │ │ │ │ │ - if (this.keepData) { │ │ │ │ - this.data = object; │ │ │ │ - } │ │ │ │ + OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS); │ │ │ │ + if (options.maxExtent) { │ │ │ │ + options.maxExtent = options.maxExtent.clone(); │ │ │ │ + } │ │ │ │ │ │ │ │ - return object; │ │ │ │ - }, │ │ │ │ + OpenLayers.Layer.EventPane.prototype.initialize.apply(this, │ │ │ │ + [name, options]); │ │ │ │ + OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, │ │ │ │ + [name, options]); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Serialize an object into a JSON string. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * value - {String} The object, array, string, number, boolean or date │ │ │ │ - * to be serialized. │ │ │ │ - * pretty - {Boolean} Structure the output with newlines and indentation. │ │ │ │ - * Default is false. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The JSON string representation of the input value. │ │ │ │ - */ │ │ │ │ - write: function(value, pretty) { │ │ │ │ - this.pretty = !!pretty; │ │ │ │ - var json = null; │ │ │ │ - var type = typeof value; │ │ │ │ - if (this.serialize[type]) { │ │ │ │ - try { │ │ │ │ - json = (!this.pretty && this.nativeJSON) ? │ │ │ │ - JSON.stringify(value) : │ │ │ │ - this.serialize[type].apply(this, [value]); │ │ │ │ - } catch (err) { │ │ │ │ - OpenLayers.Console.error("Trouble serializing: " + err); │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator); │ │ │ │ + this.initMercatorParameters(); │ │ │ │ } │ │ │ │ - } │ │ │ │ - return json; │ │ │ │ - }, │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: writeIndent │ │ │ │ - * Output an indentation string depending on the indentation level. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} An appropriate indentation string. │ │ │ │ - */ │ │ │ │ - writeIndent: function() { │ │ │ │ - var pieces = []; │ │ │ │ - if (this.pretty) { │ │ │ │ - for (var i = 0; i < this.level; ++i) { │ │ │ │ - pieces.push(this.indent); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return pieces.join(''); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: clone │ │ │ │ + * Create a clone of this layer │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.Google>} An exact clone of this layer │ │ │ │ + */ │ │ │ │ + clone: function() { │ │ │ │ + /** │ │ │ │ + * This method isn't intended to be called by a subclass and it │ │ │ │ + * doesn't call the same method on the superclass. We don't call │ │ │ │ + * the super's clone because we don't want properties that are set │ │ │ │ + * on this layer after initialize (i.e. this.mapObject etc.). │ │ │ │ + */ │ │ │ │ + return new OpenLayers.Layer.Google( │ │ │ │ + this.name, this.getOptions() │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: writeNewline │ │ │ │ - * Output a string representing a newline if in pretty printing mode. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A string representing a new line. │ │ │ │ - */ │ │ │ │ - writeNewline: function() { │ │ │ │ - return (this.pretty) ? this.newline : ''; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIMethod: setVisibility │ │ │ │ + * Set the visibility flag for the layer and hide/show & redraw │ │ │ │ + * accordingly. Fire event unless otherwise specified │ │ │ │ + * │ │ │ │ + * Note that visibility is no longer simply whether or not the layer's │ │ │ │ + * style.display is set to "block". Now we store a 'visibility' state │ │ │ │ + * property on the layer class, this allows us to remember whether or │ │ │ │ + * not we *desire* for a layer to be visible. In the case where the │ │ │ │ + * map's resolution is out of the layer's range, this desire may be │ │ │ │ + * subverted. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * visible - {Boolean} Display the layer (if in range) │ │ │ │ + */ │ │ │ │ + setVisibility: function(visible) { │ │ │ │ + // sharing a map container, opacity has to be set per layer │ │ │ │ + var opacity = this.opacity == null ? 1 : this.opacity; │ │ │ │ + OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments); │ │ │ │ + this.setOpacity(opacity); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: writeSpace │ │ │ │ - * Output a string representing a space if in pretty printing mode. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A space. │ │ │ │ - */ │ │ │ │ - writeSpace: function() { │ │ │ │ - return (this.pretty) ? this.space : ''; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIMethod: display │ │ │ │ + * Hide or show the Layer │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * visible - {Boolean} │ │ │ │ + */ │ │ │ │ + display: function(visible) { │ │ │ │ + if (!this._dragging) { │ │ │ │ + this.setGMapVisibility(visible); │ │ │ │ + } │ │ │ │ + OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: serialize │ │ │ │ - * Object with properties corresponding to the serializable data types. │ │ │ │ - * Property values are functions that do the actual serializing. │ │ │ │ - */ │ │ │ │ - serialize: { │ │ │ │ /** │ │ │ │ - * Method: serialize.object │ │ │ │ - * Transform an object into a JSON string. │ │ │ │ - * │ │ │ │ + * Method: moveTo │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * object - {Object} The object to be serialized. │ │ │ │ + * bounds - {<OpenLayers.Bounds>} │ │ │ │ + * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to │ │ │ │ + * do some init work in that case. │ │ │ │ + * dragging - {Boolean} │ │ │ │ + */ │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + this._dragging = dragging; │ │ │ │ + OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments); │ │ │ │ + delete this._dragging; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: setOpacity │ │ │ │ + * Sets the opacity for the entire layer (all images) │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {String} A JSON string representing the object. │ │ │ │ + * Parameters: │ │ │ │ + * opacity - {Float} │ │ │ │ */ │ │ │ │ - 'object': function(object) { │ │ │ │ - // three special objects that we want to treat differently │ │ │ │ - if (object == null) { │ │ │ │ - return "null"; │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ + if (opacity !== this.opacity) { │ │ │ │ + if (this.map != null) { │ │ │ │ + this.map.events.triggerEvent("changelayer", { │ │ │ │ + layer: this, │ │ │ │ + property: "opacity" │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + this.opacity = opacity; │ │ │ │ } │ │ │ │ - if (object.constructor == Date) { │ │ │ │ - return this.serialize.date.apply(this, [object]); │ │ │ │ + // Though this layer's opacity may not change, we're sharing a container │ │ │ │ + // and need to update the opacity for the entire container. │ │ │ │ + if (this.getVisibility()) { │ │ │ │ + var container = this.getMapContainer(); │ │ │ │ + OpenLayers.Util.modifyDOMElement( │ │ │ │ + container, null, null, null, null, null, null, opacity │ │ │ │ + ); │ │ │ │ } │ │ │ │ - if (object.constructor == Array) { │ │ │ │ - return this.serialize.array.apply(this, [object]); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + * Clean up this layer. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + /** │ │ │ │ + * We have to override this method because the event pane destroy │ │ │ │ + * deletes the mapObject reference before removing this layer from │ │ │ │ + * the map. │ │ │ │ + */ │ │ │ │ + if (this.map) { │ │ │ │ + this.setGMapVisibility(false); │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache && cache.count <= 1) { │ │ │ │ + this.removeGMapElements(); │ │ │ │ + } │ │ │ │ } │ │ │ │ - var pieces = ['{']; │ │ │ │ - this.level += 1; │ │ │ │ - var key, keyJSON, valueJSON; │ │ │ │ + OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: removeGMapElements │ │ │ │ + * Remove all elements added to the dom. This should only be called if │ │ │ │ + * this is the last of the Google layers for the given map. │ │ │ │ + */ │ │ │ │ + removeGMapElements: function() { │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache) { │ │ │ │ + // remove shared elements from dom │ │ │ │ + var container = this.mapObject && this.getMapContainer(); │ │ │ │ + if (container && container.parentNode) { │ │ │ │ + container.parentNode.removeChild(container); │ │ │ │ + } │ │ │ │ + var termsOfUse = cache.termsOfUse; │ │ │ │ + if (termsOfUse && termsOfUse.parentNode) { │ │ │ │ + termsOfUse.parentNode.removeChild(termsOfUse); │ │ │ │ + } │ │ │ │ + var poweredBy = cache.poweredBy; │ │ │ │ + if (poweredBy && poweredBy.parentNode) { │ │ │ │ + poweredBy.parentNode.removeChild(poweredBy); │ │ │ │ + } │ │ │ │ + if (this.mapObject && window.google && google.maps && │ │ │ │ + google.maps.event && google.maps.event.clearListeners) { │ │ │ │ + google.maps.event.clearListeners(this.mapObject, 'tilesloaded'); │ │ │ │ } │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.level -= 1; │ │ │ │ - pieces.push(this.writeNewline(), this.writeIndent(), '}'); │ │ │ │ - return pieces.join(''); │ │ │ │ + /** │ │ │ │ + * APIMethod: removeMap │ │ │ │ + * On being removed from the map, also remove termsOfUse and poweredBy divs │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + removeMap: function(map) { │ │ │ │ + // hide layer before removing │ │ │ │ + if (this.visibility && this.mapObject) { │ │ │ │ + this.setGMapVisibility(false); │ │ │ │ + } │ │ │ │ + // check to see if last Google layer in this map │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[map.id]; │ │ │ │ + if (cache) { │ │ │ │ + if (cache.count <= 1) { │ │ │ │ + this.removeGMapElements(); │ │ │ │ + delete OpenLayers.Layer.Google.cache[map.id]; │ │ │ │ + } else { │ │ │ │ + // decrement the layer count │ │ │ │ + --cache.count; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + // remove references to gmap elements │ │ │ │ + delete this.termsOfUse; │ │ │ │ + delete this.poweredBy; │ │ │ │ + delete this.mapObject; │ │ │ │ + delete this.dragObject; │ │ │ │ + OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ + // │ │ │ │ + // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds │ │ │ │ + // │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: serialize.array │ │ │ │ - * Transform an array into a JSON string. │ │ │ │ - * │ │ │ │ + * APIMethod: getOLBoundsFromMapObjectBounds │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * array - {Array} The array to be serialized │ │ │ │ + * moBounds - {Object} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A JSON string representing the array. │ │ │ │ + * {<OpenLayers.Bounds>} An <OpenLayers.Bounds>, translated from the │ │ │ │ + * passed-in MapObject Bounds. │ │ │ │ + * Returns null if null value is passed in. │ │ │ │ */ │ │ │ │ - 'array': function(array) { │ │ │ │ - var json; │ │ │ │ - var pieces = ['[']; │ │ │ │ - this.level += 1; │ │ │ │ - │ │ │ │ - for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ - // recursive calls need to allow for sub-classing │ │ │ │ - json = OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ - [array[i], this.pretty]); │ │ │ │ - if (json != null) { │ │ │ │ - if (i > 0) { │ │ │ │ - pieces.push(','); │ │ │ │ - } │ │ │ │ - pieces.push(this.writeNewline(), this.writeIndent(), json); │ │ │ │ + getOLBoundsFromMapObjectBounds: function(moBounds) { │ │ │ │ + var olBounds = null; │ │ │ │ + if (moBounds != null) { │ │ │ │ + var sw = moBounds.getSouthWest(); │ │ │ │ + var ne = moBounds.getNorthEast(); │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + sw = this.forwardMercator(sw.lng(), sw.lat()); │ │ │ │ + ne = this.forwardMercator(ne.lng(), ne.lat()); │ │ │ │ + } else { │ │ │ │ + sw = new OpenLayers.LonLat(sw.lng(), sw.lat()); │ │ │ │ + ne = new OpenLayers.LonLat(ne.lng(), ne.lat()); │ │ │ │ } │ │ │ │ + olBounds = new OpenLayers.Bounds(sw.lon, │ │ │ │ + sw.lat, │ │ │ │ + ne.lon, │ │ │ │ + ne.lat); │ │ │ │ } │ │ │ │ + return olBounds; │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.level -= 1; │ │ │ │ - pieces.push(this.writeNewline(), this.writeIndent(), ']'); │ │ │ │ - return pieces.join(''); │ │ │ │ + /** │ │ │ │ + * APIMethod: getWarningHTML │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} String with information on why layer is broken, how to get │ │ │ │ + * it working. │ │ │ │ + */ │ │ │ │ + getWarningHTML: function() { │ │ │ │ + return OpenLayers.i18n("googleWarning"); │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ + /************************************ │ │ │ │ + * * │ │ │ │ + * MapObject Interface Controls * │ │ │ │ + * * │ │ │ │ + ************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // Get&Set Center, Zoom │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: serialize.string │ │ │ │ - * Transform a string into a JSON string. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * string - {String} The string to be serialized │ │ │ │ + * APIMethod: getMapObjectCenter │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} The mapObject's current center in Map Object format │ │ │ │ + */ │ │ │ │ + getMapObjectCenter: function() { │ │ │ │ + return this.mapObject.getCenter(); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getMapObjectZoom │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A JSON string representing the string. │ │ │ │ + * {Integer} The mapObject's current zoom, in Map Object format │ │ │ │ */ │ │ │ │ - '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 + '"'; │ │ │ │ + getMapObjectZoom: function() { │ │ │ │ + return this.mapObject.getZoom(); │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ + /************************************ │ │ │ │ + * * │ │ │ │ + * MapObject Primitives * │ │ │ │ + * * │ │ │ │ + ************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // LonLat │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: serialize.number │ │ │ │ - * Transform a number into a JSON string. │ │ │ │ - * │ │ │ │ + * APIMethod: getLongitudeFromMapObjectLonLat │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * number - {Number} The number to be serialized. │ │ │ │ - * │ │ │ │ + * moLonLat - {Object} MapObject LonLat format │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} A JSON string representing the number. │ │ │ │ + * {Float} Longitude of the given MapObject LonLat │ │ │ │ */ │ │ │ │ - 'number': function(number) { │ │ │ │ - return isFinite(number) ? String(number) : "null"; │ │ │ │ + getLongitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + return this.sphericalMercator ? │ │ │ │ + this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : │ │ │ │ + moLonLat.lng(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: serialize.boolean │ │ │ │ - * Transform a boolean into a JSON string. │ │ │ │ - * │ │ │ │ + * APIMethod: getLatitudeFromMapObjectLonLat │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * bool - {Boolean} The boolean to be serialized. │ │ │ │ + * moLonLat - {Object} MapObject LonLat format │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A JSON string representing the boolean. │ │ │ │ + * {Float} Latitude of the given MapObject LonLat │ │ │ │ */ │ │ │ │ - 'boolean': function(bool) { │ │ │ │ - return String(bool); │ │ │ │ + getLatitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + var lat = this.sphericalMercator ? │ │ │ │ + this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : │ │ │ │ + moLonLat.lat(); │ │ │ │ + return lat; │ │ │ │ }, │ │ │ │ │ │ │ │ + // Pixel │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Method: serialize.object │ │ │ │ - * Transform a date into a JSON string. │ │ │ │ - * │ │ │ │ + * APIMethod: getXFromMapObjectPixel │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * date - {Date} The date to be serialized. │ │ │ │ + * moPixel - {Object} MapObject Pixel format │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} A JSON string representing the date. │ │ │ │ + * {Integer} X value of the MapObject Pixel │ │ │ │ */ │ │ │ │ - 'date': function(date) { │ │ │ │ - function format(number) { │ │ │ │ - // Format integers to have at least two digits. │ │ │ │ - return (number < 10) ? '0' + number : number; │ │ │ │ - } │ │ │ │ - return '"' + date.getFullYear() + '-' + │ │ │ │ - format(date.getMonth() + 1) + '-' + │ │ │ │ - format(date.getDate()) + 'T' + │ │ │ │ - format(date.getHours()) + ':' + │ │ │ │ - format(date.getMinutes()) + ':' + │ │ │ │ - format(date.getSeconds()) + '"'; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Format.JSON" │ │ │ │ + getXFromMapObjectPixel: function(moPixel) { │ │ │ │ + return moPixel.x; │ │ │ │ + }, │ │ │ │ │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Geometry.js │ │ │ │ - ====================================================================== */ │ │ │ │ + /** │ │ │ │ + * APIMethod: getYFromMapObjectPixel │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moPixel - {Object} MapObject Pixel format │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Integer} Y value of the MapObject Pixel │ │ │ │ + */ │ │ │ │ + getYFromMapObjectPixel: function(moPixel) { │ │ │ │ + return moPixel.y; │ │ │ │ + }, │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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.Layer.Google" │ │ │ │ + }); │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * Property: OpenLayers.Layer.Google.cache │ │ │ │ + * {Object} Cache for elements that should only be created once per map. │ │ │ │ */ │ │ │ │ +OpenLayers.Layer.Google.cache = {}; │ │ │ │ + │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Geometry │ │ │ │ - * A Geometry is a description of a geographic object. Create an instance of │ │ │ │ - * this class with the <OpenLayers.Geometry> constructor. This is a base class, │ │ │ │ - * typical geometry types are described by subclasses of this class. │ │ │ │ - * │ │ │ │ - * Note that if you use the <OpenLayers.Geometry.fromWKT> method, you must │ │ │ │ - * explicitly include the OpenLayers.Format.WKT in your build. │ │ │ │ + * Constant: OpenLayers.Layer.Google.v2 │ │ │ │ + * │ │ │ │ + * Mixin providing functionality specific to the Google Maps API v2. │ │ │ │ + * │ │ │ │ + * This API has been deprecated by Google. │ │ │ │ + * Developers are encouraged to migrate to v3 of the API; support for this │ │ │ │ + * is provided by <OpenLayers.Layer.Google.v3> │ │ │ │ */ │ │ │ │ -OpenLayers.Geometry = OpenLayers.Class({ │ │ │ │ +OpenLayers.Layer.Google.v2 = { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: id │ │ │ │ - * {String} A unique identifier for this geometry. │ │ │ │ + * Property: termsOfUse │ │ │ │ + * {DOMElement} Div for Google's copyright and terms of use link │ │ │ │ */ │ │ │ │ - id: null, │ │ │ │ + termsOfUse: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: parent │ │ │ │ - * {<OpenLayers.Geometry>}This is set when a Geometry is added as component │ │ │ │ - * of another geometry │ │ │ │ + * Property: poweredBy │ │ │ │ + * {DOMElement} Div for Google's powered by logo and link │ │ │ │ */ │ │ │ │ - parent: null, │ │ │ │ + poweredBy: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: bounds │ │ │ │ - * {<OpenLayers.Bounds>} The bounds of this geometry │ │ │ │ + * Property: dragObject │ │ │ │ + * {GDraggableObject} Since 2.93, Google has exposed the ability to get │ │ │ │ + * the maps GDraggableObject. We can now use this for smooth panning │ │ │ │ */ │ │ │ │ - bounds: null, │ │ │ │ + dragObject: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Geometry │ │ │ │ - * Creates a geometry object. │ │ │ │ + /** │ │ │ │ + * Method: loadMapObject │ │ │ │ + * Load the GMap and register appropriate event listeners. If we can't │ │ │ │ + * load GMap2, then display a warning message. │ │ │ │ */ │ │ │ │ - initialize: function() { │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ - }, │ │ │ │ + loadMapObject: function() { │ │ │ │ + if (!this.type) { │ │ │ │ + this.type = G_NORMAL_MAP; │ │ │ │ + } │ │ │ │ + var mapObject, termsOfUse, poweredBy; │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache) { │ │ │ │ + // there are already Google layers added to this map │ │ │ │ + mapObject = cache.mapObject; │ │ │ │ + termsOfUse = cache.termsOfUse; │ │ │ │ + poweredBy = cache.poweredBy; │ │ │ │ + // increment the layer count │ │ │ │ + ++cache.count; │ │ │ │ + } else { │ │ │ │ + // this is the first Google layer for this map │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * Destroy this geometry. │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - this.id = null; │ │ │ │ - this.bounds = null; │ │ │ │ - }, │ │ │ │ + var container = this.map.viewPortDiv; │ │ │ │ + var div = document.createElement("div"); │ │ │ │ + div.id = this.map.id + "_GMap2Container"; │ │ │ │ + div.style.position = "absolute"; │ │ │ │ + div.style.width = "100%"; │ │ │ │ + div.style.height = "100%"; │ │ │ │ + container.appendChild(div); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Create a clone of this geometry. Does not set any non-standard │ │ │ │ - * properties of the cloned geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} An exact clone of this geometry. │ │ │ │ - */ │ │ │ │ - clone: function() { │ │ │ │ - return new OpenLayers.Geometry(); │ │ │ │ - }, │ │ │ │ + // create GMap and shuffle elements │ │ │ │ + try { │ │ │ │ + mapObject = new GMap2(div); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setBounds │ │ │ │ - * Set the bounds for this Geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * bounds - {<OpenLayers.Bounds>} │ │ │ │ - */ │ │ │ │ - setBounds: function(bounds) { │ │ │ │ - if (bounds) { │ │ │ │ - this.bounds = bounds.clone(); │ │ │ │ + // move the ToS and branding stuff up to the container div │ │ │ │ + termsOfUse = div.lastChild; │ │ │ │ + container.appendChild(termsOfUse); │ │ │ │ + termsOfUse.style.zIndex = "1100"; │ │ │ │ + termsOfUse.style.right = ""; │ │ │ │ + termsOfUse.style.bottom = ""; │ │ │ │ + termsOfUse.className = "olLayerGoogleCopyright"; │ │ │ │ + │ │ │ │ + poweredBy = div.lastChild; │ │ │ │ + container.appendChild(poweredBy); │ │ │ │ + poweredBy.style.zIndex = "1100"; │ │ │ │ + poweredBy.style.right = ""; │ │ │ │ + poweredBy.style.bottom = ""; │ │ │ │ + poweredBy.className = "olLayerGooglePoweredBy gmnoprint"; │ │ │ │ + │ │ │ │ + } catch (e) { │ │ │ │ + throw (e); │ │ │ │ + } │ │ │ │ + // cache elements for use by any other google layers added to │ │ │ │ + // this same map │ │ │ │ + OpenLayers.Layer.Google.cache[this.map.id] = { │ │ │ │ + mapObject: mapObject, │ │ │ │ + termsOfUse: termsOfUse, │ │ │ │ + poweredBy: poweredBy, │ │ │ │ + count: 1 │ │ │ │ + }; │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.mapObject = mapObject; │ │ │ │ + this.termsOfUse = termsOfUse; │ │ │ │ + this.poweredBy = poweredBy; │ │ │ │ + │ │ │ │ + // ensure this layer type is one of the mapObject types │ │ │ │ + if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), │ │ │ │ + this.type) === -1) { │ │ │ │ + this.mapObject.addMapType(this.type); │ │ │ │ + } │ │ │ │ + │ │ │ │ + //since v 2.93 getDragObject is now available. │ │ │ │ + if (typeof mapObject.getDragObject == "function") { │ │ │ │ + this.dragObject = mapObject.getDragObject(); │ │ │ │ + } else { │ │ │ │ + this.dragPanMapObject = null; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.isBaseLayer === false) { │ │ │ │ + this.setGMapVisibility(this.div.style.display !== "none"); │ │ │ │ } │ │ │ │ + │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: clearBounds │ │ │ │ - * Nullify this components bounds and that of its parent as well. │ │ │ │ + * APIMethod: onMapResize │ │ │ │ */ │ │ │ │ - clearBounds: function() { │ │ │ │ - this.bounds = null; │ │ │ │ - if (this.parent) { │ │ │ │ - this.parent.clearBounds(); │ │ │ │ + onMapResize: function() { │ │ │ │ + // workaround for resizing of invisible or not yet fully loaded layers │ │ │ │ + // where GMap2.checkResize() does not work. We need to load the GMap │ │ │ │ + // for the old div size, then checkResize(), and then call │ │ │ │ + // layer.moveTo() to trigger GMap.setCenter() (which will finish │ │ │ │ + // the GMap initialization). │ │ │ │ + if (this.visibility && this.mapObject.isLoaded()) { │ │ │ │ + this.mapObject.checkResize(); │ │ │ │ + } else { │ │ │ │ + if (!this._resized) { │ │ │ │ + var layer = this; │ │ │ │ + var handle = GEvent.addListener(this.mapObject, "load", function() { │ │ │ │ + GEvent.removeListener(handle); │ │ │ │ + delete layer._resized; │ │ │ │ + layer.mapObject.checkResize(); │ │ │ │ + layer.moveTo(layer.map.getCenter(), layer.map.getZoom()); │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + this._resized = true; │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: extendBounds │ │ │ │ - * Extend the existing bounds to include the new bounds. │ │ │ │ - * If geometry's bounds is not yet set, then set a new Bounds. │ │ │ │ + * Method: setGMapVisibility │ │ │ │ + * Display the GMap container and associated elements. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * newBounds - {<OpenLayers.Bounds>} │ │ │ │ + * visible - {Boolean} Display the GMap elements. │ │ │ │ */ │ │ │ │ - extendBounds: function(newBounds) { │ │ │ │ - var bounds = this.getBounds(); │ │ │ │ - if (!bounds) { │ │ │ │ - this.setBounds(newBounds); │ │ │ │ - } else { │ │ │ │ - this.bounds.extend(newBounds); │ │ │ │ + setGMapVisibility: function(visible) { │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache) { │ │ │ │ + var container = this.mapObject.getContainer(); │ │ │ │ + if (visible === true) { │ │ │ │ + this.mapObject.setMapType(this.type); │ │ │ │ + container.style.display = ""; │ │ │ │ + this.termsOfUse.style.left = ""; │ │ │ │ + this.termsOfUse.style.display = ""; │ │ │ │ + this.poweredBy.style.display = ""; │ │ │ │ + cache.displayed = this.id; │ │ │ │ + } else { │ │ │ │ + if (cache.displayed === this.id) { │ │ │ │ + delete cache.displayed; │ │ │ │ + } │ │ │ │ + if (!cache.displayed) { │ │ │ │ + container.style.display = "none"; │ │ │ │ + this.termsOfUse.style.display = "none"; │ │ │ │ + // move ToU far to the left in addition to setting display │ │ │ │ + // to "none", because at the end of the GMap2 load │ │ │ │ + // sequence, display: none will be unset and ToU would be │ │ │ │ + // visible after loading a map with a google layer that is │ │ │ │ + // initially hidden. │ │ │ │ + this.termsOfUse.style.left = "-9999px"; │ │ │ │ + this.poweredBy.style.display = "none"; │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getBounds │ │ │ │ - * Get the bounds for this Geometry. If bounds is not set, it │ │ │ │ - * is calculated again, this makes queries faster. │ │ │ │ + * Method: getMapContainer │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} │ │ │ │ + * {DOMElement} the GMap container's div │ │ │ │ */ │ │ │ │ - getBounds: function() { │ │ │ │ - if (this.bounds == null) { │ │ │ │ - this.calculateBounds(); │ │ │ │ - } │ │ │ │ - return this.bounds; │ │ │ │ + getMapContainer: function() { │ │ │ │ + return this.mapObject.getContainer(); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: calculateBounds │ │ │ │ - * Recalculate the bounds for the geometry. │ │ │ │ - */ │ │ │ │ - calculateBounds: function() { │ │ │ │ - // │ │ │ │ - // This should be overridden by subclasses. │ │ │ │ - // │ │ │ │ - }, │ │ │ │ + // │ │ │ │ + // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds │ │ │ │ + // │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: distanceTo │ │ │ │ - * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ - * │ │ │ │ + * APIMethod: getMapObjectBoundsFromOLBounds │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ - * options - {Object} Optional properties for configuring the distance │ │ │ │ - * calculation. │ │ │ │ - * │ │ │ │ - * Valid options depend on the specific geometry type. │ │ │ │ + * olBounds - {<OpenLayers.Bounds>} │ │ │ │ * │ │ │ │ * 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. │ │ │ │ + * {Object} A MapObject Bounds, translated from olBounds │ │ │ │ + * Returns null if null value is passed in │ │ │ │ */ │ │ │ │ - distanceTo: function(geometry, options) {}, │ │ │ │ + getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ + var moBounds = null; │ │ │ │ + if (olBounds != null) { │ │ │ │ + var sw = this.sphericalMercator ? │ │ │ │ + this.inverseMercator(olBounds.bottom, olBounds.left) : │ │ │ │ + new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ + var ne = this.sphericalMercator ? │ │ │ │ + this.inverseMercator(olBounds.top, olBounds.right) : │ │ │ │ + new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ + moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), │ │ │ │ + new GLatLng(ne.lat, ne.lon)); │ │ │ │ + } │ │ │ │ + return moBounds; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getVertices │ │ │ │ - * Return a list of all points in this geometry. │ │ │ │ - * │ │ │ │ + │ │ │ │ + /************************************ │ │ │ │ + * * │ │ │ │ + * MapObject Interface Controls * │ │ │ │ + * * │ │ │ │ + ************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // Get&Set Center, Zoom │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: setMapObjectCenter │ │ │ │ + * Set the mapObject to the specified center and zoom │ │ │ │ + * │ │ │ │ * 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. │ │ │ │ + * center - {Object} MapObject LonLat format │ │ │ │ + * zoom - {int} MapObject zoom format │ │ │ │ */ │ │ │ │ - getVertices: function(nodes) {}, │ │ │ │ + setMapObjectCenter: function(center, zoom) { │ │ │ │ + this.mapObject.setCenter(center, zoom); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: atPoint │ │ │ │ - * Note - This is only an approximation based on the bounds of the │ │ │ │ - * geometry. │ │ │ │ + * APIMethod: dragPanMapObject │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * lonlat - {<OpenLayers.LonLat>|Object} OpenLayers.LonLat or an │ │ │ │ - * object with a 'lon' and 'lat' properties. │ │ │ │ - * toleranceLon - {float} Optional tolerance in Geometric Coords │ │ │ │ - * toleranceLat - {float} Optional tolerance in Geographic Coords │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Whether or not the geometry is at the specified location │ │ │ │ + * dX - {Integer} │ │ │ │ + * dY - {Integer} │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + dragPanMapObject: function(dX, dY) { │ │ │ │ + this.dragObject.moveBy(new GSize(-dX, dY)); │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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; │ │ │ │ - }, │ │ │ │ + // LonLat - Pixel Translation │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getLength │ │ │ │ - * Calculate the length of this geometry. This method is defined in │ │ │ │ - * subclasses. │ │ │ │ + * APIMethod: getMapObjectLonLatFromMapObjectPixel │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moPixel - {Object} MapObject Pixel format │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Float} The length of the collection by summing its parts │ │ │ │ + * {Object} MapObject LonLat translated from MapObject Pixel │ │ │ │ */ │ │ │ │ - getLength: function() { │ │ │ │ - //to be overridden by geometries that actually have a length │ │ │ │ - // │ │ │ │ - return 0.0; │ │ │ │ + getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ + return this.mapObject.fromContainerPixelToLatLng(moPixel); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getArea │ │ │ │ - * Calculate the area of this geometry. This method is defined in subclasses. │ │ │ │ + * APIMethod: getMapObjectPixelFromMapObjectLonLat │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moLonLat - {Object} MapObject LonLat format │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Float} The area of the collection by summing its parts │ │ │ │ + * {Object} MapObject Pixel transtlated from MapObject LonLat │ │ │ │ */ │ │ │ │ - getArea: function() { │ │ │ │ - //to be overridden by geometries that actually have an area │ │ │ │ - // │ │ │ │ - return 0.0; │ │ │ │ + getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + return this.mapObject.fromLatLngToContainerPixel(moLonLat); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getCentroid │ │ │ │ - * Calculate the centroid of this geometry. This method is defined in subclasses. │ │ │ │ - * │ │ │ │ + │ │ │ │ + // Bounds │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getMapObjectZoomFromMapObjectBounds │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moBounds - {Object} MapObject Bounds format │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Geometry.Point>} The centroid of the collection │ │ │ │ + * {Object} MapObject Zoom for specified MapObject Bounds │ │ │ │ */ │ │ │ │ - getCentroid: function() { │ │ │ │ - return null; │ │ │ │ + getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ + return this.mapObject.getBoundsZoomLevel(moBounds); │ │ │ │ }, │ │ │ │ │ │ │ │ + /************************************ │ │ │ │ + * * │ │ │ │ + * MapObject Primitives * │ │ │ │ + * * │ │ │ │ + ************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // LonLat │ │ │ │ + │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ - * │ │ │ │ + * APIMethod: getMapObjectLonLatFromLonLat │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * lon - {Float} │ │ │ │ + * lat - {Float} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {String} String representation of this geometry. │ │ │ │ + * {Object} MapObject LonLat built from lon and lat params │ │ │ │ */ │ │ │ │ - toString: function() { │ │ │ │ - var string; │ │ │ │ - if (OpenLayers.Format && OpenLayers.Format.WKT) { │ │ │ │ - string = OpenLayers.Format.WKT.prototype.write( │ │ │ │ - new OpenLayers.Feature.Vector(this) │ │ │ │ - ); │ │ │ │ + getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ + var gLatLng; │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + var lonlat = this.inverseMercator(lon, lat); │ │ │ │ + gLatLng = new GLatLng(lonlat.lat, lonlat.lon); │ │ │ │ } else { │ │ │ │ - string = Object.prototype.toString.call(this); │ │ │ │ + gLatLng = new GLatLng(lat, lon); │ │ │ │ } │ │ │ │ - return string; │ │ │ │ + return gLatLng; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry" │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Function: OpenLayers.Geometry.fromWKT │ │ │ │ - * Generate a geometry given a Well-Known Text string. For this method to │ │ │ │ - * work, you must include the OpenLayers.Format.WKT in your build │ │ │ │ - * explicitly. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * wkt - {String} A string representing the geometry in Well-Known Text. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} A geometry of the appropriate class. │ │ │ │ - */ │ │ │ │ -OpenLayers.Geometry.fromWKT = function(wkt) { │ │ │ │ - var geom; │ │ │ │ - if (OpenLayers.Format && OpenLayers.Format.WKT) { │ │ │ │ - var format = OpenLayers.Geometry.fromWKT.format; │ │ │ │ - if (!format) { │ │ │ │ - format = new OpenLayers.Format.WKT(); │ │ │ │ - OpenLayers.Geometry.fromWKT.format = format; │ │ │ │ - } │ │ │ │ - var result = format.read(wkt); │ │ │ │ - if (result instanceof OpenLayers.Feature.Vector) { │ │ │ │ - geom = result.geometry; │ │ │ │ - } else if (OpenLayers.Util.isArray(result)) { │ │ │ │ - var len = result.length; │ │ │ │ - var components = new Array(len); │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ - components[i] = result[i].geometry; │ │ │ │ - } │ │ │ │ - geom = new OpenLayers.Geometry.Collection(components); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return geom; │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Method: OpenLayers.Geometry.segmentsIntersect │ │ │ │ - * Determine whether two line segments intersect. Optionally calculates │ │ │ │ - * and returns the intersection point. This function is optimized for │ │ │ │ - * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those │ │ │ │ - * obvious cases where there is no intersection, the function should │ │ │ │ - * not be called. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * seg1 - {Object} Object representing a segment with properties x1, y1, x2, │ │ │ │ - * and y2. The start point is represented by x1 and y1. The end point │ │ │ │ - * is represented by x2 and y2. Start and end are ordered so that x1 < x2. │ │ │ │ - * seg2 - {Object} Object representing a segment with properties x1, y1, x2, │ │ │ │ - * and y2. The start point is represented by x1 and y1. The end point │ │ │ │ - * is represented by x2 and y2. Start and end are ordered so that x1 < x2. │ │ │ │ - * options - {Object} Optional properties for calculating the intersection. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * point - {Boolean} Return the intersection point. If false, the actual │ │ │ │ - * intersection point will not be calculated. If true and the segments │ │ │ │ - * intersect, the intersection point will be returned. If true and │ │ │ │ - * the segments do not intersect, false will be returned. If true and │ │ │ │ - * the segments are coincident, true will be returned. │ │ │ │ - * tolerance - {Number} If a non-null value is provided, if the segments are │ │ │ │ - * within the tolerance distance, this will be considered an intersection. │ │ │ │ - * In addition, if the point option is true and the calculated intersection │ │ │ │ - * is within the tolerance distance of an end point, the endpoint will be │ │ │ │ - * returned instead of the calculated intersection. Further, if the │ │ │ │ - * intersection is within the tolerance of endpoints on both segments, or │ │ │ │ - * if two segment endpoints are within the tolerance distance of eachother │ │ │ │ - * (but no intersection is otherwise calculated), an endpoint on the │ │ │ │ - * first segment provided will be returned. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect. │ │ │ │ - * If the point argument is true, the return will be the intersection │ │ │ │ - * point or false if none exists. If point is true and the segments │ │ │ │ - * are coincident, return will be true (and the instersection is equal │ │ │ │ - * to the shorter segment). │ │ │ │ - */ │ │ │ │ -OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) { │ │ │ │ - var point = options && options.point; │ │ │ │ - var tolerance = options && options.tolerance; │ │ │ │ - var intersection = false; │ │ │ │ - var x11_21 = seg1.x1 - seg2.x1; │ │ │ │ - var y11_21 = seg1.y1 - seg2.y1; │ │ │ │ - var x12_11 = seg1.x2 - seg1.x1; │ │ │ │ - var y12_11 = seg1.y2 - seg1.y1; │ │ │ │ - var y22_21 = seg2.y2 - seg2.y1; │ │ │ │ - var x22_21 = seg2.x2 - seg2.x1; │ │ │ │ - var d = (y22_21 * x12_11) - (x22_21 * y12_11); │ │ │ │ - var n1 = (x22_21 * y11_21) - (y22_21 * x11_21); │ │ │ │ - var n2 = (x12_11 * y11_21) - (y12_11 * x11_21); │ │ │ │ - if (d == 0) { │ │ │ │ - // parallel │ │ │ │ - if (n1 == 0 && n2 == 0) { │ │ │ │ - // coincident │ │ │ │ - intersection = true; │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - var along1 = n1 / d; │ │ │ │ - var along2 = n2 / d; │ │ │ │ - if (along1 >= 0 && along1 <= 1 && along2 >= 0 && along2 <= 1) { │ │ │ │ - // intersect │ │ │ │ - if (!point) { │ │ │ │ - intersection = true; │ │ │ │ - } else { │ │ │ │ - // calculate the intersection point │ │ │ │ - var x = seg1.x1 + (along1 * x12_11); │ │ │ │ - var y = seg1.y1 + (along1 * y12_11); │ │ │ │ - intersection = new OpenLayers.Geometry.Point(x, y); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (tolerance) { │ │ │ │ - var dist; │ │ │ │ - if (intersection) { │ │ │ │ - if (point) { │ │ │ │ - var segs = [seg1, seg2]; │ │ │ │ - var seg, x, y; │ │ │ │ - // check segment endpoints for proximity to intersection │ │ │ │ - // set intersection to first endpoint within the tolerance │ │ │ │ - outer: for (var i = 0; i < 2; ++i) { │ │ │ │ - seg = segs[i]; │ │ │ │ - for (var j = 1; j < 3; ++j) { │ │ │ │ - x = seg["x" + j]; │ │ │ │ - y = seg["y" + j]; │ │ │ │ - dist = Math.sqrt( │ │ │ │ - Math.pow(x - intersection.x, 2) + │ │ │ │ - Math.pow(y - intersection.y, 2) │ │ │ │ - ); │ │ │ │ - if (dist < tolerance) { │ │ │ │ - intersection.x = x; │ │ │ │ - intersection.y = y; │ │ │ │ - break outer; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + // Pixel │ │ │ │ │ │ │ │ - } │ │ │ │ - } 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; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIMethod: getMapObjectPixelFromXY │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * x - {Integer} │ │ │ │ + * y - {Integer} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} MapObject Pixel from x and y parameters │ │ │ │ + */ │ │ │ │ + getMapObjectPixelFromXY: function(x, y) { │ │ │ │ + return new GPoint(x, y); │ │ │ │ } │ │ │ │ - return intersection; │ │ │ │ -}; │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Function: OpenLayers.Geometry.distanceToSegment │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {Object} An object with x and y properties representing the │ │ │ │ - * point coordinates. │ │ │ │ - * segment - {Object} An object with x1, y1, x2, and y2 properties │ │ │ │ - * representing endpoint coordinates. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object with distance, along, x, and y properties. The distance │ │ │ │ - * will be the shortest distance between the input point and segment. │ │ │ │ - * The x and y properties represent the coordinates along the segment │ │ │ │ - * where the shortest distance meets the segment. The along attribute │ │ │ │ - * describes how far between the two segment points the given point is. │ │ │ │ - */ │ │ │ │ -OpenLayers.Geometry.distanceToSegment = function(point, segment) { │ │ │ │ - var result = OpenLayers.Geometry.distanceSquaredToSegment(point, segment); │ │ │ │ - result.distance = Math.sqrt(result.distance); │ │ │ │ - return result; │ │ │ │ -}; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Function: OpenLayers.Geometry.distanceSquaredToSegment │ │ │ │ - * │ │ │ │ - * Usually the distanceToSegment function should be used. This variant however │ │ │ │ - * can be used for comparisons where the exact distance is not important. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {Object} An object with x and y properties representing the │ │ │ │ - * point coordinates. │ │ │ │ - * segment - {Object} An object with x1, y1, x2, and y2 properties │ │ │ │ - * representing endpoint coordinates. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object with squared distance, along, x, and y properties. │ │ │ │ - * The distance will be the shortest distance between the input point and │ │ │ │ - * segment. The x and y properties represent the coordinates along the │ │ │ │ - * segment where the shortest distance meets the segment. The along │ │ │ │ - * attribute describes how far between the two segment points the given │ │ │ │ - * point is. │ │ │ │ - */ │ │ │ │ -OpenLayers.Geometry.distanceSquaredToSegment = function(point, segment) { │ │ │ │ - var x0 = point.x; │ │ │ │ - var y0 = point.y; │ │ │ │ - var x1 = segment.x1; │ │ │ │ - var y1 = segment.y1; │ │ │ │ - var x2 = segment.x2; │ │ │ │ - var y2 = segment.y2; │ │ │ │ - var dx = x2 - x1; │ │ │ │ - var dy = y2 - y1; │ │ │ │ - var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) / │ │ │ │ - (Math.pow(dx, 2) + Math.pow(dy, 2)); │ │ │ │ - var x, y; │ │ │ │ - if (along <= 0.0) { │ │ │ │ - x = x1; │ │ │ │ - y = y1; │ │ │ │ - } else if (along >= 1.0) { │ │ │ │ - x = x2; │ │ │ │ - y = y2; │ │ │ │ - } else { │ │ │ │ - x = x1 + along * dx; │ │ │ │ - y = y1 + along * dy; │ │ │ │ - } │ │ │ │ - return { │ │ │ │ - distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2), │ │ │ │ - x: x, │ │ │ │ - y: y, │ │ │ │ - along: along │ │ │ │ - }; │ │ │ │ }; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Geometry/Point.js │ │ │ │ + OpenLayers/Layer/Google/v3.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Geometry.js │ │ │ │ + * @requires OpenLayers/Layer/Google.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Geometry.Point │ │ │ │ - * Point geometry class. │ │ │ │ + * Constant: OpenLayers.Layer.Google.v3 │ │ │ │ * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Geometry> │ │ │ │ + * Mixin providing functionality specific to the Google Maps API v3. │ │ │ │ + * │ │ │ │ + * To use this layer, you must include the GMaps v3 API in your html. │ │ │ │ + * │ │ │ │ + * Note that this layer configures the google.maps.map object with the │ │ │ │ + * "disableDefaultUI" option set to true. Using UI controls that the Google │ │ │ │ + * Maps API provides is not supported by the OpenLayers API. │ │ │ │ */ │ │ │ │ -OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: x │ │ │ │ - * {float} │ │ │ │ - */ │ │ │ │ - x: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: y │ │ │ │ - * {float} │ │ │ │ - */ │ │ │ │ - y: null, │ │ │ │ +OpenLayers.Layer.Google.v3 = { │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Geometry.Point │ │ │ │ - * Construct a point geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * x - {float} │ │ │ │ - * y - {float} │ │ │ │ + * Constant: DEFAULTS │ │ │ │ + * {Object} It is not recommended to change the properties set here. Note │ │ │ │ + * that Google.v3 layers only work when sphericalMercator is set to true. │ │ │ │ * │ │ │ │ + * (code) │ │ │ │ + * { │ │ │ │ + * sphericalMercator: true, │ │ │ │ + * projection: "EPSG:900913" │ │ │ │ + * } │ │ │ │ + * (end) │ │ │ │ */ │ │ │ │ - initialize: function(x, y) { │ │ │ │ - OpenLayers.Geometry.prototype.initialize.apply(this, arguments); │ │ │ │ - │ │ │ │ - this.x = parseFloat(x); │ │ │ │ - this.y = parseFloat(y); │ │ │ │ + DEFAULTS: { │ │ │ │ + sphericalMercator: true, │ │ │ │ + projection: "EPSG:900913" │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry.Point>} An exact clone of this OpenLayers.Geometry.Point │ │ │ │ + * APIProperty: animationEnabled │ │ │ │ + * {Boolean} If set to true, the transition between zoom levels will be │ │ │ │ + * animated (if supported by the GMaps API for the device used). Set to │ │ │ │ + * false to match the zooming experience of other layer types. Default │ │ │ │ + * is true. Note that the GMaps API does not give us control over zoom │ │ │ │ + * animation, so if set to false, when zooming, this will make the │ │ │ │ + * layer temporarily invisible, wait until GMaps reports the map being │ │ │ │ + * idle, and make it visible again. The result will be a blank layer │ │ │ │ + * for a few moments while zooming. │ │ │ │ */ │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Geometry.Point(this.x, this.y); │ │ │ │ - } │ │ │ │ + animationEnabled: true, │ │ │ │ │ │ │ │ - // catch any randomly tagged-on properties │ │ │ │ - OpenLayers.Util.applyDefaults(obj, this); │ │ │ │ + /** │ │ │ │ + * Method: loadMapObject │ │ │ │ + * Load the GMap and register appropriate event listeners. │ │ │ │ + */ │ │ │ │ + loadMapObject: function() { │ │ │ │ + if (!this.type) { │ │ │ │ + this.type = google.maps.MapTypeId.ROADMAP; │ │ │ │ + } │ │ │ │ + var mapObject; │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache) { │ │ │ │ + // there are already Google layers added to this map │ │ │ │ + mapObject = cache.mapObject; │ │ │ │ + // increment the layer count │ │ │ │ + ++cache.count; │ │ │ │ + } else { │ │ │ │ + // this is the first Google layer for this map │ │ │ │ + // create GMap │ │ │ │ + var center = this.map.getCenter(); │ │ │ │ + var container = document.createElement('div'); │ │ │ │ + container.className = "olForeignContainer"; │ │ │ │ + container.style.width = '100%'; │ │ │ │ + container.style.height = '100%'; │ │ │ │ + mapObject = new google.maps.Map(container, { │ │ │ │ + center: center ? │ │ │ │ + new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0), │ │ │ │ + zoom: this.map.getZoom() || 0, │ │ │ │ + mapTypeId: this.type, │ │ │ │ + disableDefaultUI: true, │ │ │ │ + keyboardShortcuts: false, │ │ │ │ + draggable: false, │ │ │ │ + disableDoubleClickZoom: true, │ │ │ │ + scrollwheel: false, │ │ │ │ + streetViewControl: false │ │ │ │ + }); │ │ │ │ + var googleControl = document.createElement('div'); │ │ │ │ + googleControl.style.width = '100%'; │ │ │ │ + googleControl.style.height = '100%'; │ │ │ │ + mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl); │ │ │ │ │ │ │ │ - return obj; │ │ │ │ + // cache elements for use by any other google layers added to │ │ │ │ + // this same map │ │ │ │ + cache = { │ │ │ │ + googleControl: googleControl, │ │ │ │ + mapObject: mapObject, │ │ │ │ + count: 1 │ │ │ │ + }; │ │ │ │ + OpenLayers.Layer.Google.cache[this.map.id] = cache; │ │ │ │ + } │ │ │ │ + this.mapObject = mapObject; │ │ │ │ + this.setGMapVisibility(this.visibility); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: calculateBounds │ │ │ │ - * Create a new Bounds based on the lon/lat │ │ │ │ + /** │ │ │ │ + * APIMethod: onMapResize │ │ │ │ */ │ │ │ │ - calculateBounds: function() { │ │ │ │ - this.bounds = new OpenLayers.Bounds(this.x, this.y, │ │ │ │ - this.x, this.y); │ │ │ │ + onMapResize: function() { │ │ │ │ + if (this.visibility) { │ │ │ │ + google.maps.event.trigger(this.mapObject, "resize"); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: distanceTo │ │ │ │ - * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ - * │ │ │ │ + * Method: setGMapVisibility │ │ │ │ + * Display the GMap container and associated elements. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ - * options - {Object} Optional properties for configuring the distance │ │ │ │ - * calculation. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * details - {Boolean} Return details from the distance calculation. │ │ │ │ - * Default is false. │ │ │ │ - * edge - {Boolean} Calculate the distance from this geometry to the │ │ │ │ - * nearest edge of the target geometry. Default is true. If true, │ │ │ │ - * calling distanceTo from a geometry that is wholly contained within │ │ │ │ - * the target will result in a non-zero distance. If false, whenever │ │ │ │ - * geometries intersect, calling distanceTo will return 0. If false, │ │ │ │ - * details cannot be returned. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Number | Object} The distance between this geometry and the target. │ │ │ │ - * If details is true, the return will be an object with distance, │ │ │ │ - * x0, y0, x1, and x2 properties. The x0 and y0 properties represent │ │ │ │ - * the coordinates of the closest point on this geometry. The x1 and y1 │ │ │ │ - * properties represent the coordinates of the closest point on the │ │ │ │ - * target geometry. │ │ │ │ + * visible - {Boolean} Display the GMap elements. │ │ │ │ */ │ │ │ │ - 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 │ │ │ │ - }; │ │ │ │ + setGMapVisibility: function(visible) { │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + var map = this.map; │ │ │ │ + if (cache) { │ │ │ │ + var type = this.type; │ │ │ │ + var layers = map.layers; │ │ │ │ + var layer; │ │ │ │ + for (var i = layers.length - 1; i >= 0; --i) { │ │ │ │ + layer = layers[i]; │ │ │ │ + if (layer instanceof OpenLayers.Layer.Google && │ │ │ │ + layer.visibility === true && layer.inRange === true) { │ │ │ │ + type = layer.type; │ │ │ │ + visible = true; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var container = this.mapObject.getDiv(); │ │ │ │ + if (visible === true) { │ │ │ │ + if (container.parentNode !== map.div) { │ │ │ │ + if (!cache.rendered) { │ │ │ │ + var me = this; │ │ │ │ + google.maps.event.addListenerOnce(this.mapObject, 'tilesloaded', function() { │ │ │ │ + cache.rendered = true; │ │ │ │ + me.setGMapVisibility(me.getVisibility()); │ │ │ │ + me.moveTo(me.map.getCenter()); │ │ │ │ + }); │ │ │ │ + } else { │ │ │ │ + map.div.appendChild(container); │ │ │ │ + cache.googleControl.appendChild(map.viewPortDiv); │ │ │ │ + google.maps.event.trigger(this.mapObject, 'resize'); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.mapObject.setMapTypeId(type); │ │ │ │ + } else if (cache.googleControl.hasChildNodes()) { │ │ │ │ + map.div.appendChild(map.viewPortDiv); │ │ │ │ + map.div.removeChild(container); │ │ │ │ } │ │ │ │ } │ │ │ │ - return result; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: equals │ │ │ │ - * Determine whether another geometry is equivalent to this one. Geometries │ │ │ │ - * are considered equivalent if all components have the same coordinates. │ │ │ │ + /** │ │ │ │ + * Method: getMapContainer │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * geom - {<OpenLayers.Geometry.Point>} The geometry to test. │ │ │ │ - * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The supplied geometry is equivalent to this geometry. │ │ │ │ + * {DOMElement} the GMap container's div │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + getMapContainer: function() { │ │ │ │ + return this.mapObject.getDiv(); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: toShortString │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} Shortened String representation of Point object. │ │ │ │ - * (ex. <i>"5, 42"</i>) │ │ │ │ - */ │ │ │ │ - toShortString: function() { │ │ │ │ - return (this.x + ", " + this.y); │ │ │ │ - }, │ │ │ │ + // │ │ │ │ + // TRANSLATION: MapObject Bounds <-> OpenLayers.Bounds │ │ │ │ + // │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ - * │ │ │ │ + * APIMethod: getMapObjectBoundsFromOLBounds │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * x - {Float} Distance to move geometry in positive x direction. │ │ │ │ - * y - {Float} Distance to move geometry in positive y direction. │ │ │ │ + * olBounds - {<OpenLayers.Bounds>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} A MapObject Bounds, translated from olBounds │ │ │ │ + * Returns null if null value is passed in │ │ │ │ */ │ │ │ │ - move: function(x, y) { │ │ │ │ - this.x = this.x + x; │ │ │ │ - this.y = this.y + y; │ │ │ │ - this.clearBounds(); │ │ │ │ + getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ + var moBounds = null; │ │ │ │ + if (olBounds != null) { │ │ │ │ + var sw = this.sphericalMercator ? │ │ │ │ + this.inverseMercator(olBounds.bottom, olBounds.left) : │ │ │ │ + new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ + var ne = this.sphericalMercator ? │ │ │ │ + this.inverseMercator(olBounds.top, olBounds.right) : │ │ │ │ + new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ + moBounds = new google.maps.LatLngBounds( │ │ │ │ + new google.maps.LatLng(sw.lat, sw.lon), │ │ │ │ + new google.maps.LatLng(ne.lat, ne.lon) │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return moBounds; │ │ │ │ }, │ │ │ │ │ │ │ │ + │ │ │ │ + /************************************ │ │ │ │ + * * │ │ │ │ + * MapObject Interface Controls * │ │ │ │ + * * │ │ │ │ + ************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // LonLat - Pixel Translation │ │ │ │ + │ │ │ │ /** │ │ │ │ - * APIMethod: rotate │ │ │ │ - * Rotate a point around another. │ │ │ │ - * │ │ │ │ + * APIMethod: getMapObjectLonLatFromMapObjectPixel │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * angle - {Float} Rotation angle in degrees (measured counterclockwise │ │ │ │ - * from the positive x-axis) │ │ │ │ - * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation │ │ │ │ + * moPixel - {Object} MapObject Pixel format │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} MapObject LonLat translated from MapObject Pixel │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ + getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ + var size = this.map.getSize(); │ │ │ │ + var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ + var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ + var res = this.map.getResolution(); │ │ │ │ + │ │ │ │ + var delta_x = moPixel.x - (size.w / 2); │ │ │ │ + var delta_y = moPixel.y - (size.h / 2); │ │ │ │ + │ │ │ │ + var lonlat = new OpenLayers.LonLat( │ │ │ │ + lon + delta_x * res, │ │ │ │ + lat - delta_y * res │ │ │ │ + ); │ │ │ │ + │ │ │ │ + if (this.wrapDateLine) { │ │ │ │ + lonlat = lonlat.wrapDateLine(this.maxExtent); │ │ │ │ + } │ │ │ │ + return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getCentroid │ │ │ │ - * │ │ │ │ + * APIMethod: getMapObjectPixelFromMapObjectLonLat │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * moLonLat - {Object} MapObject LonLat format │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Geometry.Point>} The centroid of the collection │ │ │ │ + * {Object} MapObject Pixel transtlated from MapObject LonLat │ │ │ │ */ │ │ │ │ - getCentroid: function() { │ │ │ │ - return new OpenLayers.Geometry.Point(this.x, this.y); │ │ │ │ + getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ + var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ + var res = this.map.getResolution(); │ │ │ │ + var extent = this.map.getExtent(); │ │ │ │ + return this.getMapObjectPixelFromXY((1 / res * (lon - extent.left)), │ │ │ │ + (1 / res * (extent.top - lat))); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: resize │ │ │ │ - * Resize a point relative to some origin. For points, this has the effect │ │ │ │ - * of scaling a vector (from the origin to the point). This method is │ │ │ │ - * more useful on geometry collection subclasses. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * scale - {Float} Ratio of the new distance from the origin to the old │ │ │ │ - * distance from the origin. A scale of 2 doubles the │ │ │ │ - * distance between the point and origin. │ │ │ │ - * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing │ │ │ │ - * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: setMapObjectCenter │ │ │ │ + * Set the mapObject to the specified center and zoom │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} - The current geometry. │ │ │ │ + * Parameters: │ │ │ │ + * center - {Object} MapObject LonLat format │ │ │ │ + * zoom - {int} MapObject zoom format │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + setMapObjectCenter: function(center, zoom) { │ │ │ │ + if (this.animationEnabled === false && zoom != this.mapObject.zoom) { │ │ │ │ + var mapContainer = this.getMapContainer(); │ │ │ │ + google.maps.event.addListenerOnce( │ │ │ │ + this.mapObject, │ │ │ │ + "idle", │ │ │ │ + function() { │ │ │ │ + mapContainer.style.visibility = ""; │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + mapContainer.style.visibility = "hidden"; │ │ │ │ + } │ │ │ │ + this.mapObject.setOptions({ │ │ │ │ + center: center, │ │ │ │ + zoom: zoom │ │ │ │ + }); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: intersects │ │ │ │ - * Determine if the input geometry intersects this one. │ │ │ │ - * │ │ │ │ + │ │ │ │ + // Bounds │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: getMapObjectZoomFromMapObjectBounds │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} Any type of geometry. │ │ │ │ - * │ │ │ │ + * moBounds - {Object} MapObject Bounds format │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Boolean} The input geometry intersects this one. │ │ │ │ + * {Object} MapObject Zoom for specified MapObject Bounds │ │ │ │ */ │ │ │ │ - intersects: function(geometry) { │ │ │ │ - var intersect = false; │ │ │ │ - if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - intersect = this.equals(geometry); │ │ │ │ - } else { │ │ │ │ - intersect = geometry.intersects(this); │ │ │ │ - } │ │ │ │ - return intersect; │ │ │ │ + getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ + return this.mapObject.getBoundsZoomLevel(moBounds); │ │ │ │ }, │ │ │ │ │ │ │ │ + /************************************ │ │ │ │ + * * │ │ │ │ + * MapObject Primitives * │ │ │ │ + * * │ │ │ │ + ************************************/ │ │ │ │ + │ │ │ │ + │ │ │ │ + // LonLat │ │ │ │ + │ │ │ │ /** │ │ │ │ - * APIMethod: transform │ │ │ │ - * Translate the x,y properties of the point from source to dest. │ │ │ │ + * APIMethod: getMapObjectLonLatFromLonLat │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * source - {<OpenLayers.Projection>} │ │ │ │ - * dest - {<OpenLayers.Projection>} │ │ │ │ + * lon - {Float} │ │ │ │ + * lat - {Float} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} │ │ │ │ + * {Object} MapObject LonLat built from lon and lat params │ │ │ │ */ │ │ │ │ - transform: function(source, dest) { │ │ │ │ - if ((source && dest)) { │ │ │ │ - OpenLayers.Projection.transform( │ │ │ │ - this, source, dest); │ │ │ │ - this.bounds = null; │ │ │ │ + getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ + var gLatLng; │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + var lonlat = this.inverseMercator(lon, lat); │ │ │ │ + gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon); │ │ │ │ + } else { │ │ │ │ + gLatLng = new google.maps.LatLng(lat, lon); │ │ │ │ } │ │ │ │ - return this; │ │ │ │ + return gLatLng; │ │ │ │ }, │ │ │ │ │ │ │ │ + // Pixel │ │ │ │ + │ │ │ │ /** │ │ │ │ - * APIMethod: getVertices │ │ │ │ - * Return a list of all points in this geometry. │ │ │ │ - * │ │ │ │ + * APIMethod: getMapObjectPixelFromXY │ │ │ │ + * │ │ │ │ * 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. │ │ │ │ - * │ │ │ │ + * x - {Integer} │ │ │ │ + * y - {Integer} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {Array} A list of all vertices in the geometry. │ │ │ │ + * {Object} MapObject Pixel from x and y parameters │ │ │ │ */ │ │ │ │ - getVertices: function(nodes) { │ │ │ │ - return [this]; │ │ │ │ - }, │ │ │ │ + getMapObjectPixelFromXY: function(x, y) { │ │ │ │ + return new google.maps.Point(x, y); │ │ │ │ + } │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.Point" │ │ │ │ -}); │ │ │ │ +}; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Geometry/Collection.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/Geometry.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Geometry.Collection │ │ │ │ - * A Collection is exactly what it sounds like: A collection of different │ │ │ │ - * Geometries. These are stored in the local parameter <components> (which │ │ │ │ - * can be passed as a parameter to the constructor). │ │ │ │ - * │ │ │ │ - * As new geometries are added to the collection, they are NOT cloned. │ │ │ │ - * When removing geometries, they need to be specified by reference (ie you │ │ │ │ - * have to pass in the *exact* geometry to be removed). │ │ │ │ - * │ │ │ │ - * The <getArea> and <getLength> functions here merely iterate through │ │ │ │ - * the components, summing their respective areas and lengths. │ │ │ │ - * │ │ │ │ - * Create a new instance with the <OpenLayers.Geometry.Collection> constructor. │ │ │ │ + * Class: OpenLayers.Popup │ │ │ │ + * A popup is a small div that can opened and closed on the map. │ │ │ │ + * Typically opened in response to clicking on a marker. │ │ │ │ + * See <OpenLayers.Marker>. Popup's don't require their own │ │ │ │ + * layer and are added the the map using the <OpenLayers.Map.addPopup> │ │ │ │ + * method. │ │ │ │ * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Geometry> │ │ │ │ + * 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.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, { │ │ │ │ +OpenLayers.Popup = OpenLayers.Class({ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: components │ │ │ │ - * {Array(<OpenLayers.Geometry>)} The component parts of this geometry │ │ │ │ + /** │ │ │ │ + * Property: events │ │ │ │ + * {<OpenLayers.Events>} custom event manager │ │ │ │ */ │ │ │ │ - components: null, │ │ │ │ + events: 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. │ │ │ │ + /** Property: id │ │ │ │ + * {String} the unique identifier assigned to this popup. │ │ │ │ */ │ │ │ │ - componentTypes: null, │ │ │ │ + id: "", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Geometry.Collection │ │ │ │ - * Creates a Geometry Collection -- a list of geoms. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * components - {Array(<OpenLayers.Geometry>)} Optional array of geometries │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Property: lonlat │ │ │ │ + * {<OpenLayers.LonLat>} the position of this popup on the map │ │ │ │ */ │ │ │ │ - initialize: function(components) { │ │ │ │ - OpenLayers.Geometry.prototype.initialize.apply(this, arguments); │ │ │ │ - this.components = []; │ │ │ │ - if (components != null) { │ │ │ │ - this.addComponents(components); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + lonlat: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: div │ │ │ │ + * {DOMElement} the div that contains this popup. │ │ │ │ + */ │ │ │ │ + div: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: contentSize │ │ │ │ + * {<OpenLayers.Size>} the width and height of the content. │ │ │ │ + */ │ │ │ │ + contentSize: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: size │ │ │ │ + * {<OpenLayers.Size>} the width and height of the popup. │ │ │ │ + */ │ │ │ │ + size: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: contentHTML │ │ │ │ + * {String} An HTML string for this popup to display. │ │ │ │ + */ │ │ │ │ + contentHTML: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: backgroundColor │ │ │ │ + * {String} the background color used by the popup. │ │ │ │ + */ │ │ │ │ + backgroundColor: "", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: opacity │ │ │ │ + * {float} the opacity of this popup (between 0.0 and 1.0) │ │ │ │ + */ │ │ │ │ + opacity: "", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: border │ │ │ │ + * {String} the border size of the popup. (eg 2px) │ │ │ │ + */ │ │ │ │ + border: "", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: contentDiv │ │ │ │ + * {DOMElement} a reference to the element that holds the content of │ │ │ │ + * the div. │ │ │ │ + */ │ │ │ │ + contentDiv: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: groupDiv │ │ │ │ + * {DOMElement} First and only child of 'div'. The group Div contains the │ │ │ │ + * 'contentDiv' and the 'closeDiv'. │ │ │ │ + */ │ │ │ │ + groupDiv: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: closeDiv │ │ │ │ + * {DOMElement} the optional closer image │ │ │ │ + */ │ │ │ │ + closeDiv: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: autoSize │ │ │ │ + * {Boolean} Resize the popup to auto-fit the contents. │ │ │ │ + * Default is false. │ │ │ │ + */ │ │ │ │ + autoSize: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Destroy this geometry. │ │ │ │ + * APIProperty: minSize │ │ │ │ + * {<OpenLayers.Size>} Minimum size allowed for the popup's contents. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.components.length = 0; │ │ │ │ - this.components = null; │ │ │ │ - OpenLayers.Geometry.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + minSize: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Clone this geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry.Collection>} An exact clone of this collection │ │ │ │ + * APIProperty: maxSize │ │ │ │ + * {<OpenLayers.Size>} Maximum size allowed for the popup's contents. │ │ │ │ */ │ │ │ │ - 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()); │ │ │ │ - } │ │ │ │ + maxSize: null, │ │ │ │ │ │ │ │ - // catch any randomly tagged-on properties │ │ │ │ - OpenLayers.Util.applyDefaults(geometry, this); │ │ │ │ + /** │ │ │ │ + * Property: displayClass │ │ │ │ + * {String} The CSS class of the popup. │ │ │ │ + */ │ │ │ │ + displayClass: "olPopup", │ │ │ │ │ │ │ │ - return geometry; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: contentDisplayClass │ │ │ │ + * {String} The CSS class of the popup content div. │ │ │ │ + */ │ │ │ │ + contentDisplayClass: "olPopupContent", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: getComponentsString │ │ │ │ - * Get a string representing the components for this collection │ │ │ │ + /** │ │ │ │ + * Property: padding │ │ │ │ + * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal │ │ │ │ + * padding of the content div inside the popup. This was originally │ │ │ │ + * confused with the css padding as specified in style.css's │ │ │ │ + * 'olPopupContent' class. We would like to get rid of this altogether, │ │ │ │ + * except that it does come in handy for the framed and anchoredbubble │ │ │ │ + * popups, who need to maintain yet another barrier between their │ │ │ │ + * content and the outer border of the popup itself. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {String} A string representation of the components of this geometry │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - getComponentsString: function() { │ │ │ │ - var strings = []; │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ - strings.push(this.components[i].toShortString()); │ │ │ │ - } │ │ │ │ - return strings.join(","); │ │ │ │ - }, │ │ │ │ + padding: 0, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: calculateBounds │ │ │ │ - * Recalculate the bounds by iterating through the components and │ │ │ │ - * calling calling extendBounds() on each item. │ │ │ │ + /** │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + disableFirefoxOverflowHack: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: addComponents │ │ │ │ - * Add components to this geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * components - {Array(<OpenLayers.Geometry>)} An array of geometries to add │ │ │ │ + * Method: fixPadding │ │ │ │ + * To be removed in 3.0, this function merely helps us to deal with the │ │ │ │ + * case where the user may have set an integer value for padding, │ │ │ │ + * instead of an <OpenLayers.Bounds> object. │ │ │ │ */ │ │ │ │ - addComponents: function(components) { │ │ │ │ - if (!(OpenLayers.Util.isArray(components))) { │ │ │ │ - components = [components]; │ │ │ │ - } │ │ │ │ - for (var i = 0, len = components.length; i < len; i++) { │ │ │ │ - this.addComponent(components[i]); │ │ │ │ + fixPadding: function() { │ │ │ │ + if (typeof this.padding == "number") { │ │ │ │ + this.padding = new OpenLayers.Bounds( │ │ │ │ + this.padding, this.padding, this.padding, this.padding │ │ │ │ + ); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: addComponent │ │ │ │ - * Add a new component (geometry) to the collection. If this.componentTypes │ │ │ │ - * is set, then the component class name must be in the componentTypes array. │ │ │ │ - * │ │ │ │ - * The bounds cache is reset. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * component - {<OpenLayers.Geometry>} A geometry to add │ │ │ │ - * index - {int} Optional index into the array to insert the component │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The component geometry was successfully added │ │ │ │ + * APIProperty: panMapIfOutOfView │ │ │ │ + * {Boolean} When drawn, pan map such that the entire popup is visible in │ │ │ │ + * the current viewport (if necessary). │ │ │ │ + * Default is false. │ │ │ │ */ │ │ │ │ - addComponent: function(component, index) { │ │ │ │ - var added = false; │ │ │ │ - if (component) { │ │ │ │ - if (this.componentTypes == null || │ │ │ │ - (OpenLayers.Util.indexOf(this.componentTypes, │ │ │ │ - component.CLASS_NAME) > -1)) { │ │ │ │ + panMapIfOutOfView: false, │ │ │ │ │ │ │ │ - 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: keepInMap │ │ │ │ + * {Boolean} If panMapIfOutOfView is false, and this property is true, │ │ │ │ + * contrain the popup such that it always fits in the available map │ │ │ │ + * space. By default, this is not set on the base class. If you are │ │ │ │ + * creating popups that are near map edges and not allowing pannning, │ │ │ │ + * and especially if you have a popup which has a │ │ │ │ + * fixedRelativePosition, setting this to false may be a smart thing to │ │ │ │ + * do. Subclasses may want to override this setting. │ │ │ │ + * │ │ │ │ + * Default is false. │ │ │ │ + */ │ │ │ │ + keepInMap: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: removeComponents │ │ │ │ - * Remove components from this geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * components - {Array(<OpenLayers.Geometry>)} The components to be removed │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} A component was removed. │ │ │ │ + * APIProperty: closeOnMove │ │ │ │ + * {Boolean} When map pans, close the popup. │ │ │ │ + * Default is false. │ │ │ │ */ │ │ │ │ - removeComponents: function(components) { │ │ │ │ - var removed = false; │ │ │ │ + closeOnMove: false, │ │ │ │ │ │ │ │ - if (!(OpenLayers.Util.isArray(components))) { │ │ │ │ - components = [components]; │ │ │ │ + /** │ │ │ │ + * Property: map │ │ │ │ + * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map │ │ │ │ + */ │ │ │ │ + map: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Popup │ │ │ │ + * Create a popup. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * id - {String} a unqiue identifier for this popup. If null is passed │ │ │ │ + * an identifier will be automatically generated. │ │ │ │ + * lonlat - {<OpenLayers.LonLat>} The position on the map the popup will │ │ │ │ + * be shown. │ │ │ │ + * contentSize - {<OpenLayers.Size>} The size of the content. │ │ │ │ + * contentHTML - {String} An HTML string to display inside the │ │ │ │ + * popup. │ │ │ │ + * closeBox - {Boolean} Whether to display a close box inside │ │ │ │ + * the popup. │ │ │ │ + * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ + */ │ │ │ │ + initialize: function(id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback) { │ │ │ │ + if (id == null) { │ │ │ │ + id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ } │ │ │ │ - for (var i = components.length - 1; i >= 0; --i) { │ │ │ │ - removed = this.removeComponent(components[i]) || removed; │ │ │ │ + │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - return removed; │ │ │ │ - }, │ │ │ │ + this.backgroundColor = OpenLayers.Popup.COLOR; │ │ │ │ + this.opacity = OpenLayers.Popup.OPACITY; │ │ │ │ + this.border = OpenLayers.Popup.BORDER; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: removeComponent │ │ │ │ - * Remove a component from this geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * component - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The component was removed. │ │ │ │ - */ │ │ │ │ - removeComponent: function(component) { │ │ │ │ + this.div = OpenLayers.Util.createDiv(this.id, null, null, │ │ │ │ + null, null, null, "hidden"); │ │ │ │ + this.div.className = this.displayClass; │ │ │ │ │ │ │ │ - OpenLayers.Util.removeItem(this.components, component); │ │ │ │ + var groupDivId = this.id + "_GroupDiv"; │ │ │ │ + this.groupDiv = OpenLayers.Util.createDiv(groupDivId, null, null, │ │ │ │ + null, "relative", null, │ │ │ │ + "hidden"); │ │ │ │ │ │ │ │ - // clearBounds() so that it gets recalculated on the next call │ │ │ │ - // to this.getBounds(); │ │ │ │ - this.clearBounds(); │ │ │ │ - return true; │ │ │ │ - }, │ │ │ │ + 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); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getLength │ │ │ │ - * Calculate the length of this geometry │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Float} The length of the geometry │ │ │ │ - */ │ │ │ │ - getLength: function() { │ │ │ │ - var length = 0.0; │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ - length += this.components[i].getLength(); │ │ │ │ + if (closeBox) { │ │ │ │ + this.addCloseBox(closeBoxCallback); │ │ │ │ } │ │ │ │ - return length; │ │ │ │ + │ │ │ │ + this.registerEvents(); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getArea │ │ │ │ - * Calculate the area of this geometry. Note how this function is overridden │ │ │ │ - * in <OpenLayers.Geometry.Polygon>. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Float} The area of the collection by summing its parts │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * nullify references to prevent circular references and memory leaks │ │ │ │ */ │ │ │ │ - getArea: function() { │ │ │ │ - var area = 0.0; │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ - area += this.components[i].getArea(); │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - return area; │ │ │ │ + │ │ │ │ + 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; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getGeodesicArea │ │ │ │ - * Calculate the approximate area of the polygon were it projected onto │ │ │ │ - * the earth. │ │ │ │ + * Method: draw │ │ │ │ + * Constructs the elements that make up the popup. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * projection - {<OpenLayers.Projection>} The spatial reference system │ │ │ │ - * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ - * assumed. │ │ │ │ + * px - {<OpenLayers.Pixel>} the position the popup in pixels. │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * {DOMElement} Reference to a div that contains the drawn popup │ │ │ │ */ │ │ │ │ - getGeodesicArea: function(projection) { │ │ │ │ - var area = 0.0; │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ - area += this.components[i].getGeodesicArea(projection); │ │ │ │ + draw: function(px) { │ │ │ │ + if (px == null) { │ │ │ │ + if ((this.lonlat != null) && (this.map != null)) { │ │ │ │ + px = this.map.getLayerPxFromLonLat(this.lonlat); │ │ │ │ + } │ │ │ │ } │ │ │ │ - return area; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getCentroid │ │ │ │ - * │ │ │ │ - * Compute the centroid for this geometry collection. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * weighted - {Boolean} Perform the getCentroid computation recursively, │ │ │ │ - * returning an area weighted average of all geometries in this collection. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry.Point>} The centroid of the collection │ │ │ │ - */ │ │ │ │ - getCentroid: function(weighted) { │ │ │ │ - if (!weighted) { │ │ │ │ - return this.components.length && this.components[0].getCentroid(); │ │ │ │ - } │ │ │ │ - var len = this.components.length; │ │ │ │ - if (!len) { │ │ │ │ - return false; │ │ │ │ + // 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); │ │ │ │ } │ │ │ │ │ │ │ │ - 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); │ │ │ │ + //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; │ │ │ │ + } │ │ │ │ + }); │ │ │ │ } │ │ │ │ - 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; │ │ │ │ + │ │ │ │ + this.moveTo(px); │ │ │ │ + if (!this.autoSize && !this.size) { │ │ │ │ + this.setSize(this.contentSize); │ │ │ │ } │ │ │ │ + this.setBackgroundColor(); │ │ │ │ + this.setOpacity(); │ │ │ │ + this.setBorder(); │ │ │ │ + this.setContentHTML(); │ │ │ │ │ │ │ │ - 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; │ │ │ │ + if (this.panMapIfOutOfView) { │ │ │ │ + this.panIntoView(); │ │ │ │ } │ │ │ │ │ │ │ │ - return new OpenLayers.Geometry.Point(xSum / areaSum, ySum / areaSum); │ │ │ │ + return this.div; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: updatePosition │ │ │ │ + * if the popup has a lonlat and its map members set, │ │ │ │ + * then have it move itself to its proper position │ │ │ │ + */ │ │ │ │ + updatePosition: function() { │ │ │ │ + if ((this.lonlat) && (this.map)) { │ │ │ │ + var px = this.map.getLayerPxFromLonLat(this.lonlat); │ │ │ │ + if (px) { │ │ │ │ + this.moveTo(px); │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getGeodesicLength │ │ │ │ - * Calculate the approximate length of the geometry were it projected onto │ │ │ │ - * the earth. │ │ │ │ - * │ │ │ │ - * projection - {<OpenLayers.Projection>} The spatial reference system │ │ │ │ - * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ - * assumed. │ │ │ │ + * Method: moveTo │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Float} The appoximate geodesic length of the geometry in meters. │ │ │ │ + * Parameters: │ │ │ │ + * px - {<OpenLayers.Pixel>} the top and left position of the popup div. │ │ │ │ */ │ │ │ │ - getGeodesicLength: function(projection) { │ │ │ │ - var length = 0.0; │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ - length += this.components[i].getGeodesicLength(projection); │ │ │ │ + moveTo: function(px) { │ │ │ │ + if ((px != null) && (this.div != null)) { │ │ │ │ + this.div.style.left = px.x + "px"; │ │ │ │ + this.div.style.top = px.y + "px"; │ │ │ │ } │ │ │ │ - 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. │ │ │ │ + * Method: visible │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * x - {Float} Distance to move geometry in positive x direction. │ │ │ │ - * y - {Float} Distance to move geometry in positive y direction. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Boolean indicating whether or not the popup is visible │ │ │ │ */ │ │ │ │ - move: function(x, y) { │ │ │ │ - for (var i = 0, len = this.components.length; i < len; i++) { │ │ │ │ - this.components[i].move(x, y); │ │ │ │ + visible: function() { │ │ │ │ + return OpenLayers.Element.visible(this.div); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: toggle │ │ │ │ + * Toggles visibility of the popup. │ │ │ │ + */ │ │ │ │ + toggle: function() { │ │ │ │ + if (this.visible()) { │ │ │ │ + this.hide(); │ │ │ │ + } else { │ │ │ │ + this.show(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: rotate │ │ │ │ - * Rotate a geometry around some origin │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * angle - {Float} Rotation angle in degrees (measured counterclockwise │ │ │ │ - * from the positive x-axis) │ │ │ │ - * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation │ │ │ │ + * Method: show │ │ │ │ + * Makes the popup visible. │ │ │ │ */ │ │ │ │ - rotate: function(angle, origin) { │ │ │ │ - for (var i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ - this.components[i].rotate(angle, origin); │ │ │ │ + show: function() { │ │ │ │ + this.div.style.display = ''; │ │ │ │ + │ │ │ │ + if (this.panMapIfOutOfView) { │ │ │ │ + this.panIntoView(); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: resize │ │ │ │ - * Resize a geometry relative to some origin. Use this method to apply │ │ │ │ - * a uniform scaling to a geometry. │ │ │ │ + * Method: hide │ │ │ │ + * Makes the popup invisible. │ │ │ │ + */ │ │ │ │ + hide: function() { │ │ │ │ + this.div.style.display = 'none'; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setSize │ │ │ │ + * Used to adjust the size of the popup. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * scale - {Float} Factor by which to scale the geometry. A scale of 2 │ │ │ │ - * doubles the size of the geometry in each dimension │ │ │ │ - * (lines, for example, will be twice as long, and polygons │ │ │ │ - * will have four times the area). │ │ │ │ - * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing │ │ │ │ - * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} - The current geometry. │ │ │ │ + * contentSize - {<OpenLayers.Size>} the new size for the popup's │ │ │ │ + * contents div (in pixels). │ │ │ │ */ │ │ │ │ - resize: function(scale, origin, ratio) { │ │ │ │ - for (var i = 0; i < this.components.length; ++i) { │ │ │ │ - this.components[i].resize(scale, origin, ratio); │ │ │ │ + setSize: function(contentSize) { │ │ │ │ + this.size = contentSize.clone(); │ │ │ │ + │ │ │ │ + // if our contentDiv has a css 'padding' set on it by a stylesheet, we │ │ │ │ + // must add that to the desired "size". │ │ │ │ + var contentDivPadding = this.getContentDivPadding(); │ │ │ │ + var wPadding = contentDivPadding.left + contentDivPadding.right; │ │ │ │ + var hPadding = contentDivPadding.top + contentDivPadding.bottom; │ │ │ │ + │ │ │ │ + // take into account the popup's 'padding' property │ │ │ │ + this.fixPadding(); │ │ │ │ + wPadding += this.padding.left + this.padding.right; │ │ │ │ + hPadding += this.padding.top + this.padding.bottom; │ │ │ │ + │ │ │ │ + // make extra space for the close div │ │ │ │ + if (this.closeDiv) { │ │ │ │ + var closeDivWidth = parseInt(this.closeDiv.style.width); │ │ │ │ + wPadding += closeDivWidth + contentDivPadding.right; │ │ │ │ + } │ │ │ │ + │ │ │ │ + //increase size of the main popup div to take into account the │ │ │ │ + // users's desired padding and close div. │ │ │ │ + this.size.w += wPadding; │ │ │ │ + this.size.h += hPadding; │ │ │ │ + │ │ │ │ + //now if our browser is IE, we need to actually make the contents │ │ │ │ + // div itself bigger to take its own padding into effect. this makes │ │ │ │ + // me want to shoot someone, but so it goes. │ │ │ │ + if (OpenLayers.BROWSER_NAME == "msie") { │ │ │ │ + this.contentSize.w += │ │ │ │ + contentDivPadding.left + contentDivPadding.right; │ │ │ │ + this.contentSize.h += │ │ │ │ + contentDivPadding.bottom + contentDivPadding.top; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.div != null) { │ │ │ │ + this.div.style.width = this.size.w + "px"; │ │ │ │ + this.div.style.height = this.size.h + "px"; │ │ │ │ + } │ │ │ │ + if (this.contentDiv != null) { │ │ │ │ + this.contentDiv.style.width = contentSize.w + "px"; │ │ │ │ + this.contentDiv.style.height = contentSize.h + "px"; │ │ │ │ } │ │ │ │ - return this; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: distanceTo │ │ │ │ - * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ - * options - {Object} Optional properties for configuring the distance │ │ │ │ - * calculation. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * details - {Boolean} Return details from the distance calculation. │ │ │ │ - * Default is false. │ │ │ │ - * edge - {Boolean} Calculate the distance from this geometry to the │ │ │ │ - * nearest edge of the target geometry. Default is true. If true, │ │ │ │ - * calling distanceTo from a geometry that is wholly contained within │ │ │ │ - * the target will result in a non-zero distance. If false, whenever │ │ │ │ - * geometries intersect, calling distanceTo will return 0. If false, │ │ │ │ - * details cannot be returned. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Number | Object} The distance between this geometry and the target. │ │ │ │ - * If details is true, the return will be an object with distance, │ │ │ │ - * x0, y0, x1, and y1 properties. The x0 and y0 properties represent │ │ │ │ - * the coordinates of the closest point on this geometry. The x1 and y1 │ │ │ │ - * properties represent the coordinates of the closest point on the │ │ │ │ - * target geometry. │ │ │ │ + * 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 │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + updateSize: function() { │ │ │ │ + │ │ │ │ + // determine actual render dimensions of the contents by putting its │ │ │ │ + // contents into a fake contentDiv (for the CSS) and then measuring it │ │ │ │ + var preparedHTML = "<div class='" + this.contentDisplayClass + "'>" + │ │ │ │ + this.contentDiv.innerHTML + │ │ │ │ + "</div>"; │ │ │ │ + │ │ │ │ + var containerElement = (this.map) ? this.map.div : document.body; │ │ │ │ + var realSize = OpenLayers.Util.getRenderedDimensions( │ │ │ │ + preparedHTML, null, { │ │ │ │ + displayClass: this.displayClass, │ │ │ │ + containerElement: containerElement │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + │ │ │ │ + // is the "real" size of the div is safe to display in our map? │ │ │ │ + var safeSize = this.getSafeContentSize(realSize); │ │ │ │ + │ │ │ │ + var newSize = null; │ │ │ │ + if (safeSize.equals(realSize)) { │ │ │ │ + //real size of content is small enough to fit on the map, │ │ │ │ + // so we use real size. │ │ │ │ + newSize = realSize; │ │ │ │ + │ │ │ │ + } else { │ │ │ │ + │ │ │ │ + // make a new 'size' object with the clipped dimensions │ │ │ │ + // set or null if not clipped. │ │ │ │ + var fixedSize = { │ │ │ │ + w: (safeSize.w < realSize.w) ? safeSize.w : null, │ │ │ │ + h: (safeSize.h < realSize.h) ? safeSize.h : null │ │ │ │ + }; │ │ │ │ + │ │ │ │ + if (fixedSize.w && fixedSize.h) { │ │ │ │ + //content is too big in both directions, so we will use │ │ │ │ + // max popup size (safeSize), knowing well that it will │ │ │ │ + // overflow both ways. │ │ │ │ + newSize = safeSize; │ │ │ │ + } else { │ │ │ │ + //content is clipped in only one direction, so we need to │ │ │ │ + // run getRenderedDimensions() again with a fixed dimension │ │ │ │ + var clippedSize = OpenLayers.Util.getRenderedDimensions( │ │ │ │ + preparedHTML, fixedSize, { │ │ │ │ + displayClass: this.contentDisplayClass, │ │ │ │ + containerElement: containerElement │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + │ │ │ │ + //if the clipped size is still the same as the safeSize, │ │ │ │ + // that means that our content must be fixed in the │ │ │ │ + // offending direction. If overflow is 'auto', this means │ │ │ │ + // we are going to have a scrollbar for sure, so we must │ │ │ │ + // adjust for that. │ │ │ │ + // │ │ │ │ + var currentOverflow = OpenLayers.Element.getStyle( │ │ │ │ + this.contentDiv, "overflow" │ │ │ │ + ); │ │ │ │ + if ((currentOverflow != "hidden") && │ │ │ │ + (clippedSize.equals(safeSize))) { │ │ │ │ + var scrollBar = OpenLayers.Util.getScrollbarWidth(); │ │ │ │ + if (fixedSize.w) { │ │ │ │ + clippedSize.h += scrollBar; │ │ │ │ + } else { │ │ │ │ + clippedSize.w += scrollBar; │ │ │ │ + } │ │ │ │ } │ │ │ │ + │ │ │ │ + newSize = this.getSafeContentSize(clippedSize); │ │ │ │ } │ │ │ │ } │ │ │ │ - return best; │ │ │ │ + this.setSize(newSize); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: equals │ │ │ │ - * Determine whether another geometry is equivalent to this one. Geometries │ │ │ │ - * are considered equivalent if all components have the same coordinates. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} The geometry to test. │ │ │ │ + /** │ │ │ │ + * Method: setBackgroundColor │ │ │ │ + * Sets the background color of the popup. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The supplied geometry is equivalent to this geometry. │ │ │ │ + * Parameters: │ │ │ │ + * color - {String} the background color. eg "#FFBBBB" │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + setBackgroundColor: function(color) { │ │ │ │ + if (color != undefined) { │ │ │ │ + this.backgroundColor = color; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.div != null) { │ │ │ │ + this.div.style.backgroundColor = this.backgroundColor; │ │ │ │ } │ │ │ │ - return equivalent; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: transform │ │ │ │ - * Reproject the components geometry from source to dest. │ │ │ │ + * Method: setOpacity │ │ │ │ + * Sets the opacity of the popup. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * source - {<OpenLayers.Projection>} │ │ │ │ - * dest - {<OpenLayers.Projection>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} │ │ │ │ + * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + 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 + ')'; │ │ │ │ } │ │ │ │ - return this; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: intersects │ │ │ │ - * Determine if the input geometry intersects this one. │ │ │ │ + * Method: setBorder │ │ │ │ + * Sets the border style of the popup. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} Any type of geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The input geometry intersects this one. │ │ │ │ + * border - {String} The border style value. eg 2px │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ + setBorder: function(border) { │ │ │ │ + if (border != undefined) { │ │ │ │ + this.border = border; │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.div != null) { │ │ │ │ + this.div.style.border = this.border; │ │ │ │ } │ │ │ │ - return intersect; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getVertices │ │ │ │ - * Return a list of all points in this geometry. │ │ │ │ + * Method: setContentHTML │ │ │ │ + * Allows the user to set the HTML content of the popup. │ │ │ │ * │ │ │ │ * 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. │ │ │ │ + * contentHTML - {String} HTML for the div. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - }, │ │ │ │ + setContentHTML: function(contentHTML) { │ │ │ │ │ │ │ │ + if (contentHTML != null) { │ │ │ │ + this.contentHTML = contentHTML; │ │ │ │ + } │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.Collection" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Geometry/MultiPoint.js │ │ │ │ - ====================================================================== */ │ │ │ │ + if ((this.contentDiv != null) && │ │ │ │ + (this.contentHTML != null) && │ │ │ │ + (this.contentHTML != this.contentDiv.innerHTML)) { │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of 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.contentDiv.innerHTML = this.contentHTML; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Geometry/Collection.js │ │ │ │ - * @requires OpenLayers/Geometry/Point.js │ │ │ │ - */ │ │ │ │ + if (this.autoSize) { │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Geometry.MultiPoint │ │ │ │ - * MultiPoint is a collection of Points. Create a new instance with the │ │ │ │ - * <OpenLayers.Geometry.MultiPoint> constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Geometry.Collection> │ │ │ │ - * - <OpenLayers.Geometry> │ │ │ │ - */ │ │ │ │ -OpenLayers.Geometry.MultiPoint = OpenLayers.Class( │ │ │ │ - OpenLayers.Geometry.Collection, { │ │ │ │ + //if popup has images, listen for when they finish │ │ │ │ + // loading and resize accordingly │ │ │ │ + this.registerImageListeners(); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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"], │ │ │ │ + //auto size the popup to its current contents │ │ │ │ + this.updateSize(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Geometry.MultiPoint │ │ │ │ - * Create a new MultiPoint Geometry │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * components - {Array(<OpenLayers.Geometry.Point>)} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry.MultiPoint>} │ │ │ │ - */ │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: addPoint │ │ │ │ - * Wrapper for <OpenLayers.Geometry.Collection.addComponent> │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} Point to be added │ │ │ │ - * index - {Integer} Optional index │ │ │ │ - */ │ │ │ │ - addPoint: function(point, index) { │ │ │ │ - this.addComponent(point, index); │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: registerImageListeners │ │ │ │ + * Called when an image contained by the popup loaded. this function │ │ │ │ + * updates the popup size, then unregisters the image load listener. │ │ │ │ + */ │ │ │ │ + registerImageListeners: function() { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: removePoint │ │ │ │ - * Wrapper for <OpenLayers.Geometry.Collection.removeComponent> │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} Point to be removed │ │ │ │ - */ │ │ │ │ - removePoint: function(point) { │ │ │ │ - this.removeComponent(point); │ │ │ │ - }, │ │ │ │ + // 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(); │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.MultiPoint" │ │ │ │ - }); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Geometry/Curve.js │ │ │ │ - ====================================================================== */ │ │ │ │ + if (this.popup.visible() && this.popup.panMapIfOutOfView) { │ │ │ │ + this.popup.panIntoView(); │ │ │ │ + } │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + OpenLayers.Event.stopObserving( │ │ │ │ + this.img, "load", this.img._onImgLoad │ │ │ │ + ); │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Geometry/MultiPoint.js │ │ │ │ - */ │ │ │ │ + }; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Geometry.Curve │ │ │ │ - * A Curve is a MultiPoint, whose points are assumed to be connected. To │ │ │ │ - * this end, we provide a "getLength()" function, which iterates through │ │ │ │ - * the points, summing the distances between them. │ │ │ │ - * │ │ │ │ - * Inherits: │ │ │ │ - * - <OpenLayers.Geometry.MultiPoint> │ │ │ │ - */ │ │ │ │ -OpenLayers.Geometry.Curve = OpenLayers.Class(OpenLayers.Geometry.MultiPoint, { │ │ │ │ + //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) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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"], │ │ │ │ + var context = { │ │ │ │ + 'popup': this, │ │ │ │ + 'img': img │ │ │ │ + }; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Geometry.Curve │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {Array(<OpenLayers.Geometry.Point>)} │ │ │ │ - */ │ │ │ │ + //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); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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]); │ │ │ │ + OpenLayers.Event.observe(img, 'load', img._onImgLoad); │ │ │ │ } │ │ │ │ } │ │ │ │ - return length; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getGeodesicLength │ │ │ │ - * Calculate the approximate length of the geometry were it projected onto │ │ │ │ - * the earth. │ │ │ │ - * │ │ │ │ - * projection - {<OpenLayers.Projection>} The spatial reference system │ │ │ │ - * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ - * assumed. │ │ │ │ + * APIMethod: getSafeContentSize │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * size - {<OpenLayers.Size>} Desired size to make the popup. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Float} The appoximate geodesic length of the geometry in meters. │ │ │ │ + * {<OpenLayers.Size>} A size to make the popup which is neither smaller │ │ │ │ + * than the specified minimum size, nor bigger than the maximum │ │ │ │ + * size (which is calculated relative to the size of the viewport). │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - 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 │ │ │ │ - }); │ │ │ │ - } │ │ │ │ + │ │ │ │ + // 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)); │ │ │ │ } │ │ │ │ - // convert to m │ │ │ │ - return length * 1000; │ │ │ │ - }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.Curve" │ │ │ │ -}); │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Geometry/LineString.js │ │ │ │ - ====================================================================== */ │ │ │ │ + // 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)); │ │ │ │ + } │ │ │ │ │ │ │ │ -/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ - * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ - * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ - * full text of the license. */ │ │ │ │ + //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) { │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Geometry/Curve.js │ │ │ │ - */ │ │ │ │ + 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; │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Geometry.LineString │ │ │ │ - * A LineString is a Curve which, once two points have been added to it, can │ │ │ │ - * never be less than two points long. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Geometry.Curve> │ │ │ │ - */ │ │ │ │ -OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, { │ │ │ │ + var maxY = this.map.size.h - │ │ │ │ + this.map.paddingForPopups.top - │ │ │ │ + this.map.paddingForPopups.bottom - │ │ │ │ + hPadding - extraY; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Geometry.LineString │ │ │ │ - * Create a new LineString geometry │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * points - {Array(<OpenLayers.Geometry.Point>)} An array of points used to │ │ │ │ - * generate the linestring │ │ │ │ - * │ │ │ │ - */ │ │ │ │ + var maxX = this.map.size.w - │ │ │ │ + this.map.paddingForPopups.left - │ │ │ │ + this.map.paddingForPopups.right - │ │ │ │ + wPadding - extraX; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: removeComponent │ │ │ │ - * Only allows removal of a point if there are three or more points in │ │ │ │ - * the linestring. (otherwise the result would be just a single point) │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} The point to be removed │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The component was removed. │ │ │ │ - */ │ │ │ │ - removeComponent: function(point) { │ │ │ │ - var removed = this.components && (this.components.length > 2); │ │ │ │ - if (removed) { │ │ │ │ - OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, │ │ │ │ - arguments); │ │ │ │ + safeContentSize.w = Math.min(safeContentSize.w, maxX); │ │ │ │ + safeContentSize.h = Math.min(safeContentSize.h, maxY); │ │ │ │ } │ │ │ │ - return removed; │ │ │ │ + │ │ │ │ + return safeContentSize; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: 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. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ + * 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} The input geometry intersects this geometry. │ │ │ │ + * {<OpenLayers.Bounds>} │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ + 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); │ │ │ │ } │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + │ │ │ │ + //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 = ""; │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - intersect = geometry.intersects(this); │ │ │ │ } │ │ │ │ - return intersect; │ │ │ │ + return contentDivPadding; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getSortedSegments │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} An array of segment objects. Segment objects have properties │ │ │ │ - * x1, y1, x2, and y2. The start point is represented by x1 and y1. │ │ │ │ - * The end point is represented by x2 and y2. Start and end are │ │ │ │ - * ordered so that x1 < x2. │ │ │ │ + * Method: addCloseBox │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * callback - {Function} The callback to be called when the close button │ │ │ │ + * is clicked. │ │ │ │ */ │ │ │ │ - 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 │ │ │ │ - }; │ │ │ │ + addCloseBox: function(callback) { │ │ │ │ + │ │ │ │ + this.closeDiv = OpenLayers.Util.createDiv( │ │ │ │ + this.id + "_close", null, { │ │ │ │ + w: 17, │ │ │ │ + h: 17 │ │ │ │ } │ │ │ │ - } │ │ │ │ - // more efficient to define this somewhere static │ │ │ │ - function byX1(seg1, seg2) { │ │ │ │ - return seg1.x1 - seg2.x1; │ │ │ │ - } │ │ │ │ - return segments.sort(byX1); │ │ │ │ + ); │ │ │ │ + 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: splitWithSegment │ │ │ │ - * Split this geometry with the given segment. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * seg - {Object} An object with x1, y1, x2, and y2 properties referencing │ │ │ │ - * segment endpoint coordinates. │ │ │ │ - * options - {Object} Properties of this object will be used to determine │ │ │ │ - * how the split is conducted. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ - * true. If false, a vertex on the source segment must be within the │ │ │ │ - * tolerance distance of the intersection to be considered a split. │ │ │ │ - * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ - * within the tolerance distance of one of the source segment's │ │ │ │ - * endpoints will be assumed to occur at the endpoint. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object with *lines* and *points* properties. If the given │ │ │ │ - * segment intersects this linestring, the lines array will reference │ │ │ │ - * geometries that result from the split. The points array will contain │ │ │ │ - * all intersection points. Intersection points are sorted along the │ │ │ │ - * segment (in order from x1,y1 to x2,y2). │ │ │ │ + * Method: panIntoView │ │ │ │ + * Pans the map such that the popup is totaly viewable (if necessary) │ │ │ │ */ │ │ │ │ - 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)); │ │ │ │ + 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; │ │ │ │ } │ │ │ │ - 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); │ │ │ │ - }) │ │ │ │ - }; │ │ │ │ + │ │ │ │ + //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; │ │ │ │ } │ │ │ │ - return result; │ │ │ │ + │ │ │ │ + var dx = origTL.x - newTL.x; │ │ │ │ + var dy = origTL.y - newTL.y; │ │ │ │ + │ │ │ │ + this.map.pan(dx, dy); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: split │ │ │ │ - * Use this geometry (the source) to attempt to split a target geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * target - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ - * options - {Object} Properties of this object will be used to determine │ │ │ │ - * how the split is conducted. │ │ │ │ + /** │ │ │ │ + * Method: registerEvents │ │ │ │ + * Registers events on the popup. │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * Do this in a separate function so that subclasses can │ │ │ │ + * choose to override it if they wish to deal differently │ │ │ │ + * with mouse events │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * 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. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ + registerEvents: function() { │ │ │ │ + this.events = new OpenLayers.Events(this, this.div, null, true); │ │ │ │ + │ │ │ │ + function onTouchstart(evt) { │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ } │ │ │ │ - return results; │ │ │ │ + this.events.on({ │ │ │ │ + "mousedown": this.onmousedown, │ │ │ │ + "mousemove": this.onmousemove, │ │ │ │ + "mouseup": this.onmouseup, │ │ │ │ + "click": this.onclick, │ │ │ │ + "mouseout": this.onmouseout, │ │ │ │ + "dblclick": this.ondblclick, │ │ │ │ + "touchstart": onTouchstart, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: splitWith │ │ │ │ - * Split this geometry (the target) with the given geometry (the source). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} A geometry used to split this │ │ │ │ - * geometry (the source). │ │ │ │ - * options - {Object} Properties of this object will be used to determine │ │ │ │ - * how the split is conducted. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * mutual - {Boolean} Split the source geometry in addition to the target │ │ │ │ - * geometry. Default is false. │ │ │ │ - * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ - * true. If false, a vertex on the source must be within the tolerance │ │ │ │ - * distance of the intersection to be considered a split. │ │ │ │ - * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ - * within the tolerance distance of an existing vertex on the source │ │ │ │ - * will be assumed to occur at the vertex. │ │ │ │ + /** │ │ │ │ + * 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) │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - splitWith: function(geometry, options) { │ │ │ │ - return geometry.split(this, options); │ │ │ │ - │ │ │ │ + onmousedown: function(evt) { │ │ │ │ + this.mousedown = true; │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getVertices │ │ │ │ - * Return a list of all points in this geometry. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * 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: │ │ │ │ - * 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. │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ + onmousemove: function(evt) { │ │ │ │ + if (this.mousedown) { │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ } │ │ │ │ - return vertices; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: distanceTo │ │ │ │ - * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * 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: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ - * options - {Object} Optional properties for configuring the distance │ │ │ │ - * calculation. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * details - {Boolean} Return details from the distance calculation. │ │ │ │ - * Default is false. │ │ │ │ - * edge - {Boolean} Calculate the distance from this geometry to the │ │ │ │ - * nearest edge of the target geometry. Default is true. If true, │ │ │ │ - * calling distanceTo from a geometry that is wholly contained within │ │ │ │ - * the target will result in a non-zero distance. If false, whenever │ │ │ │ - * geometries intersect, calling distanceTo will return 0. If false, │ │ │ │ - * details cannot be returned. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Number | Object} The distance between this geometry and the target. │ │ │ │ - * If details is true, the return will be an object with distance, │ │ │ │ - * x0, y0, x1, and x2 properties. The x0 and y0 properties represent │ │ │ │ - * the coordinates of the closest point on this geometry. The x1 and y1 │ │ │ │ - * properties represent the coordinates of the closest point on the │ │ │ │ - * target geometry. │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - 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 │ │ │ │ - }; │ │ │ │ - } │ │ │ │ + onmouseup: function(evt) { │ │ │ │ + if (this.mousedown) { │ │ │ │ + this.mousedown = false; │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ } │ │ │ │ - return best; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: simplify │ │ │ │ - * This function will return a simplified LineString. │ │ │ │ - * Simplification is based on the Douglas-Peucker algorithm. │ │ │ │ - * │ │ │ │ - * │ │ │ │ + * Method: onclick │ │ │ │ + * Ignore clicks, but allowing default browser handling │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * tolerance - {number} threshhold for simplification in map units │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {OpenLayers.Geometry.LineString} the simplified LineString │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - simplify: function(tolerance) { │ │ │ │ - if (this && this !== null) { │ │ │ │ - var points = this.getVertices(); │ │ │ │ - if (points.length < 3) { │ │ │ │ - return this; │ │ │ │ - } │ │ │ │ - │ │ │ │ - var compareNumbers = function(a, b) { │ │ │ │ - return (a - b); │ │ │ │ - }; │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Private function doing the Douglas-Peucker reduction │ │ │ │ - */ │ │ │ │ - var douglasPeuckerReduction = function(points, firstPoint, lastPoint, tolerance) { │ │ │ │ - var maxDistance = 0; │ │ │ │ - var indexFarthest = 0; │ │ │ │ - │ │ │ │ - for (var index = firstPoint, distance; index < lastPoint; index++) { │ │ │ │ - distance = perpendicularDistance(points[firstPoint], points[lastPoint], points[index]); │ │ │ │ - if (distance > maxDistance) { │ │ │ │ - maxDistance = distance; │ │ │ │ - indexFarthest = index; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (maxDistance > tolerance && indexFarthest != firstPoint) { │ │ │ │ - //Add the largest point that exceeds the tolerance │ │ │ │ - pointIndexsToKeep.push(indexFarthest); │ │ │ │ - douglasPeuckerReduction(points, firstPoint, indexFarthest, tolerance); │ │ │ │ - douglasPeuckerReduction(points, indexFarthest, lastPoint, tolerance); │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Private function calculating the perpendicular distance │ │ │ │ - * TODO: check whether OpenLayers.Geometry.LineString::distanceTo() is faster or slower │ │ │ │ - */ │ │ │ │ - var perpendicularDistance = function(point1, point2, point) { │ │ │ │ - //Area = |(1/2)(x1y2 + x2y3 + x3y1 - x2y1 - x3y2 - x1y3)| *Area of triangle │ │ │ │ - //Base = v((x1-x2)²+(x1-x2)²) *Base of Triangle* │ │ │ │ - //Area = .5*Base*H *Solve for height │ │ │ │ - //Height = Area/.5/Base │ │ │ │ - │ │ │ │ - var area = Math.abs(0.5 * (point1.x * point2.y + point2.x * point.y + point.x * point1.y - point2.x * point1.y - point.x * point2.y - point1.x * point.y)); │ │ │ │ - var bottom = Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2)); │ │ │ │ - var height = area / bottom * 2; │ │ │ │ - │ │ │ │ - return height; │ │ │ │ - }; │ │ │ │ - │ │ │ │ - var firstPoint = 0; │ │ │ │ - var lastPoint = points.length - 1; │ │ │ │ - var pointIndexsToKeep = []; │ │ │ │ - │ │ │ │ - //Add the first and last index to the keepers │ │ │ │ - pointIndexsToKeep.push(firstPoint); │ │ │ │ - pointIndexsToKeep.push(lastPoint); │ │ │ │ - │ │ │ │ - //The first and the last point cannot be the same │ │ │ │ - while (points[firstPoint].equals(points[lastPoint])) { │ │ │ │ - lastPoint--; │ │ │ │ - //Addition: the first point not equal to first point in the LineString is kept as well │ │ │ │ - pointIndexsToKeep.push(lastPoint); │ │ │ │ - } │ │ │ │ + onclick: function(evt) { │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ + }, │ │ │ │ │ │ │ │ - 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); │ │ │ │ + /** │ │ │ │ + * 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; │ │ │ │ + }, │ │ │ │ │ │ │ │ - } else { │ │ │ │ - return this; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: ondblclick │ │ │ │ + * Ignore double-clicks, but allowing default browser handling │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + */ │ │ │ │ + ondblclick: function(evt) { │ │ │ │ + OpenLayers.Event.stop(evt, true); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.LineString" │ │ │ │ + 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/Geometry/MultiLineString.js │ │ │ │ + OpenLayers/Popup/Anchored.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Geometry/Collection.js │ │ │ │ - * @requires OpenLayers/Geometry/LineString.js │ │ │ │ + * @requires OpenLayers/Popup.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Geometry.MultiLineString │ │ │ │ - * A MultiLineString is a geometry with multiple <OpenLayers.Geometry.LineString> │ │ │ │ - * components. │ │ │ │ + * Class: OpenLayers.Popup.Anchored │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Geometry.Collection> │ │ │ │ - * - <OpenLayers.Geometry> │ │ │ │ + * - <OpenLayers.Popup> │ │ │ │ */ │ │ │ │ -OpenLayers.Geometry.MultiLineString = OpenLayers.Class( │ │ │ │ - OpenLayers.Geometry.Collection, { │ │ │ │ +OpenLayers.Popup.Anchored = │ │ │ │ + OpenLayers.Class(OpenLayers.Popup, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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: relativePosition │ │ │ │ + * {String} Relative position of the popup ("br", "tr", "tl" or "bl"). │ │ │ │ */ │ │ │ │ - componentTypes: ["OpenLayers.Geometry.LineString"], │ │ │ │ + relativePosition: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Geometry.MultiLineString │ │ │ │ - * Constructor for a MultiLineString Geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * components - {Array(<OpenLayers.Geometry.LineString>)} │ │ │ │ - * │ │ │ │ + * APIProperty: keepInMap │ │ │ │ + * {Boolean} If panMapIfOutOfView is false, and this property is true, │ │ │ │ + * contrain the popup such that it always fits in the available map │ │ │ │ + * space. By default, this is set. If you are creating popups that are │ │ │ │ + * near map edges and not allowing pannning, and especially if you have │ │ │ │ + * a popup which has a fixedRelativePosition, setting this to false may │ │ │ │ + * be a smart thing to do. │ │ │ │ + * │ │ │ │ + * For anchored popups, default is true, since subclasses will │ │ │ │ + * usually want this functionality. │ │ │ │ */ │ │ │ │ + keepInMap: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: split │ │ │ │ - * Use this geometry (the source) to attempt to split a target geometry. │ │ │ │ + * Property: anchor │ │ │ │ + * {Object} Object to which we'll anchor the popup. Must expose a │ │ │ │ + * 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>). │ │ │ │ + */ │ │ │ │ + anchor: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Popup.Anchored │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ - * options - {Object} Properties of this object will be used to determine │ │ │ │ - * how the split is conducted. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * mutual - {Boolean} Split the source geometry in addition to the target │ │ │ │ - * geometry. Default is false. │ │ │ │ - * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ - * true. If false, a vertex on the source must be within the tolerance │ │ │ │ - * distance of the intersection to be considered a split. │ │ │ │ - * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ - * within the tolerance distance of an existing vertex on the source │ │ │ │ - * will be assumed to occur at the vertex. │ │ │ │ + * id - {String} │ │ │ │ + * lonlat - {<OpenLayers.LonLat>} │ │ │ │ + * contentSize - {<OpenLayers.Size>} │ │ │ │ + * contentHTML - {String} │ │ │ │ + * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size> │ │ │ │ + * and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>). │ │ │ │ + * closeBox - {Boolean} │ │ │ │ + * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ + */ │ │ │ │ + initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, │ │ │ │ + closeBoxCallback) { │ │ │ │ + var newArguments = [ │ │ │ │ + id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback │ │ │ │ + ]; │ │ │ │ + OpenLayers.Popup.prototype.initialize.apply(this, newArguments); │ │ │ │ + │ │ │ │ + this.anchor = (anchor != null) ? anchor : │ │ │ │ + { │ │ │ │ + size: new OpenLayers.Size(0, 0), │ │ │ │ + offset: new OpenLayers.Pixel(0, 0) │ │ │ │ + }; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.anchor = null; │ │ │ │ + this.relativePosition = null; │ │ │ │ + │ │ │ │ + OpenLayers.Popup.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: show │ │ │ │ + * Overridden from Popup since user might hide popup and then show() it │ │ │ │ + * in a new location (meaning we might want to update the relative │ │ │ │ + * position on the show) │ │ │ │ + */ │ │ │ │ + show: function() { │ │ │ │ + this.updatePosition(); │ │ │ │ + OpenLayers.Popup.prototype.show.apply(this, arguments); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveTo │ │ │ │ + * Since the popup is moving to a new px, it might need also to be moved │ │ │ │ + * relative to where the marker is. We first calculate the new │ │ │ │ + * relativePosition, and then we calculate the new px where we will │ │ │ │ + * put the popup, based on the new relative position. │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * If the relativePosition has changed, we must also call │ │ │ │ + * updateRelativePosition() to make any visual changes to the popup │ │ │ │ + * which are associated with putting it in a new relativePosition. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ */ │ │ │ │ - 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 = []; │ │ │ │ + moveTo: function(px) { │ │ │ │ + var oldRelativePosition = this.relativePosition; │ │ │ │ + this.relativePosition = this.calculateRelativePosition(px); │ │ │ │ + │ │ │ │ + OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px)); │ │ │ │ + │ │ │ │ + //if this move has caused the popup to change its relative position, │ │ │ │ + // we need to make the appropriate cosmetic changes. │ │ │ │ + if (this.relativePosition != oldRelativePosition) { │ │ │ │ + this.updateRelativePosition(); │ │ │ │ } │ │ │ │ - if (sourceSplit || targetSplit) { │ │ │ │ - if (mutual) { │ │ │ │ - results = [sourceParts, targetParts]; │ │ │ │ - } else { │ │ │ │ - results = targetParts; │ │ │ │ - } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: setSize │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * contentSize - {<OpenLayers.Size>} the new size for the popup's │ │ │ │ + * contents div (in pixels). │ │ │ │ + */ │ │ │ │ + setSize: function(contentSize) { │ │ │ │ + OpenLayers.Popup.prototype.setSize.apply(this, arguments); │ │ │ │ + │ │ │ │ + if ((this.lonlat) && (this.map)) { │ │ │ │ + var px = this.map.getLayerPxFromLonLat(this.lonlat); │ │ │ │ + this.moveTo(px); │ │ │ │ } │ │ │ │ - return results; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: calculateRelativePosition │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {String} The relative position ("br" "tr" "tl" "bl") at which the popup │ │ │ │ + * should be placed. │ │ │ │ + */ │ │ │ │ + calculateRelativePosition: function(px) { │ │ │ │ + var lonlat = this.map.getLonLatFromLayerPx(px); │ │ │ │ + │ │ │ │ + var extent = this.map.getExtent(); │ │ │ │ + var quadrant = extent.determineQuadrant(lonlat); │ │ │ │ + │ │ │ │ + return OpenLayers.Bounds.oppositeQuadrant(quadrant); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: splitWith │ │ │ │ - * Split this geometry (the target) with the given geometry (the source). │ │ │ │ - * │ │ │ │ + * Method: updateRelativePosition │ │ │ │ + * The popup has been moved to a new relative location, so we may want to │ │ │ │ + * make some cosmetic adjustments to it. │ │ │ │ + * │ │ │ │ + * Note that in the classic Anchored popup, there is nothing to do │ │ │ │ + * here, since the popup looks exactly the same in all four positions. │ │ │ │ + * Subclasses such as Framed, however, will want to do something │ │ │ │ + * special here. │ │ │ │ + */ │ │ │ │ + updateRelativePosition: function() { │ │ │ │ + //to be overridden by subclasses │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: calculateNewPx │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} A geometry used to split this │ │ │ │ - * geometry (the source). │ │ │ │ - * options - {Object} Properties of this object will be used to determine │ │ │ │ - * how the split is conducted. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * mutual - {Boolean} Split the source geometry in addition to the target │ │ │ │ - * geometry. Default is false. │ │ │ │ - * edge - {Boolean} Allow splitting when only edges intersect. Default is │ │ │ │ - * true. If false, a vertex on the source must be within the tolerance │ │ │ │ - * distance of the intersection to be considered a split. │ │ │ │ - * tolerance - {Number} If a non-null value is provided, intersections │ │ │ │ - * within the tolerance distance of an existing vertex on the source │ │ │ │ - * will be assumed to occur at the vertex. │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ * │ │ │ │ * 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. │ │ │ │ + * {<OpenLayers.Pixel>} The the new px position of the popup on the screen │ │ │ │ + * relative to the passed-in px. │ │ │ │ */ │ │ │ │ - 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() │ │ │ │ - ]) │ │ │ │ - ]; │ │ │ │ - } │ │ │ │ + calculateNewPx: function(px) { │ │ │ │ + var newPx = px.offset(this.anchor.offset); │ │ │ │ │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } 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; │ │ │ │ + //use contentSize if size is not already set │ │ │ │ + var size = this.size || this.contentSize; │ │ │ │ + │ │ │ │ + var top = (this.relativePosition.charAt(0) == 't'); │ │ │ │ + newPx.y += (top) ? -size.h : this.anchor.size.h; │ │ │ │ + │ │ │ │ + var left = (this.relativePosition.charAt(1) == 'l'); │ │ │ │ + newPx.x += (left) ? -size.w : this.anchor.size.w; │ │ │ │ + │ │ │ │ + return newPx; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.MultiLineString" │ │ │ │ + CLASS_NAME: "OpenLayers.Popup.Anchored" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Geometry/LinearRing.js │ │ │ │ + OpenLayers/Popup/Framed.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Geometry/LineString.js │ │ │ │ + * @requires OpenLayers/Popup/Anchored.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Geometry.LinearRing │ │ │ │ - * │ │ │ │ - * A Linear Ring is a special LineString which is closed. It closes itself │ │ │ │ - * automatically on every addPoint/removePoint by adding a copy of the first │ │ │ │ - * point as the last point. │ │ │ │ - * │ │ │ │ - * Also, as it is the first in the line family to close itself, a getArea() │ │ │ │ - * function is defined to calculate the enclosed area of the linearRing │ │ │ │ + * Class: OpenLayers.Popup.Framed │ │ │ │ * │ │ │ │ - * Inherits: │ │ │ │ - * - <OpenLayers.Geometry.LineString> │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Popup.Anchored> │ │ │ │ */ │ │ │ │ -OpenLayers.Geometry.LinearRing = OpenLayers.Class( │ │ │ │ - OpenLayers.Geometry.LineString, { │ │ │ │ +OpenLayers.Popup.Framed = │ │ │ │ + OpenLayers.Class(OpenLayers.Popup.Anchored, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: imageSrc │ │ │ │ + * {String} location of the image to be used as the popup frame │ │ │ │ */ │ │ │ │ - componentTypes: ["OpenLayers.Geometry.Point"], │ │ │ │ + imageSrc: 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. │ │ │ │ + * Property: imageSize │ │ │ │ + * {<OpenLayers.Size>} Size (measured in pixels) of the image located │ │ │ │ + * by the 'imageSrc' property. │ │ │ │ + */ │ │ │ │ + imageSize: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isAlphaImage │ │ │ │ + * {Boolean} The image has some alpha and thus needs to use the alpha │ │ │ │ + * image hack. Note that setting this to true will have no noticeable │ │ │ │ + * effect in FF or IE7 browsers, but will all but crush the ie6 │ │ │ │ + * browser. │ │ │ │ + * Default is false. │ │ │ │ + */ │ │ │ │ + isAlphaImage: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: positionBlocks │ │ │ │ + * {Object} Hash of different position blocks (Object/Hashs). Each block │ │ │ │ + * will be keyed by a two-character 'relativePosition' │ │ │ │ + * code string (ie "tl", "tr", "bl", "br"). Block properties are │ │ │ │ + * 'offset', 'padding' (self-explanatory), and finally the 'blocks' │ │ │ │ + * parameter, which is an array of the block objects. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * points - {Array(<OpenLayers.Geometry.Point>)} points │ │ │ │ + * Each block object must have 'size', 'anchor', and 'position' │ │ │ │ + * properties. │ │ │ │ + * │ │ │ │ + * Note that positionBlocks should never be modified at runtime. │ │ │ │ */ │ │ │ │ + positionBlocks: 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 - {<OpenLayers.Geometry.Point>} │ │ │ │ - * index - {Integer} Index into the array to insert the component │ │ │ │ + * Property: blocks │ │ │ │ + * {Array[Object]} Array of objects, each of which is one "block" of the │ │ │ │ + * popup. Each block has a 'div' and an 'image' property, both of │ │ │ │ + * which are DOMElements, and the latter of which is appended to the │ │ │ │ + * former. These are reused as the popup goes changing positions for │ │ │ │ + * great economy and elegance. │ │ │ │ + */ │ │ │ │ + blocks: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: fixedRelativePosition │ │ │ │ + * {Boolean} We want the framed popup to work dynamically placed relative │ │ │ │ + * to its anchor but also in just one fixed position. A well designed │ │ │ │ + * framed popup will have the pixels and logic to display itself in │ │ │ │ + * any of the four relative positions, but (understandably), this will │ │ │ │ + * not be the case for all of them. By setting this property to 'true', │ │ │ │ + * framed popup will not recalculate for the best placement each time │ │ │ │ + * it's open, but will always open the same way. │ │ │ │ + * Note that if this is set to true, it is generally advisable to also │ │ │ │ + * set the 'panIntoView' property to true so that the popup can be │ │ │ │ + * scrolled into view (since it will often be offscreen on open) │ │ │ │ + * Default is false. │ │ │ │ + */ │ │ │ │ + fixedRelativePosition: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Popup.Framed │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Was the Point successfully added? │ │ │ │ + * Parameters: │ │ │ │ + * id - {String} │ │ │ │ + * lonlat - {<OpenLayers.LonLat>} │ │ │ │ + * contentSize - {<OpenLayers.Size>} │ │ │ │ + * contentHTML - {String} │ │ │ │ + * anchor - {Object} Object to which we'll anchor the popup. Must expose │ │ │ │ + * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) │ │ │ │ + * (Note that this is generally an <OpenLayers.Icon>). │ │ │ │ + * closeBox - {Boolean} │ │ │ │ + * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ */ │ │ │ │ - addComponent: function(point, index) { │ │ │ │ - var added = false; │ │ │ │ + initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, │ │ │ │ + closeBoxCallback) { │ │ │ │ │ │ │ │ - //remove last point │ │ │ │ - var lastPoint = this.components.pop(); │ │ │ │ + OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments); │ │ │ │ │ │ │ │ - // 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); │ │ │ │ + if (this.fixedRelativePosition) { │ │ │ │ + //based on our decided relativePostion, set the current padding │ │ │ │ + // this keeps us from getting into trouble │ │ │ │ + this.updateRelativePosition(); │ │ │ │ + │ │ │ │ + //make calculateRelativePosition always return the specified │ │ │ │ + // fixed position. │ │ │ │ + this.calculateRelativePosition = function(px) { │ │ │ │ + return this.relativePosition; │ │ │ │ + }; │ │ │ │ } │ │ │ │ │ │ │ │ - //append copy of first point │ │ │ │ - var firstPoint = this.components[0]; │ │ │ │ - OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, │ │ │ │ - [firstPoint]); │ │ │ │ + this.contentDiv.style.position = "absolute"; │ │ │ │ + this.contentDiv.style.zIndex = 1; │ │ │ │ │ │ │ │ - return added; │ │ │ │ + if (closeBox) { │ │ │ │ + this.closeDiv.style.zIndex = 1; │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.groupDiv.style.position = "absolute"; │ │ │ │ + this.groupDiv.style.top = "0px"; │ │ │ │ + this.groupDiv.style.left = "0px"; │ │ │ │ + this.groupDiv.style.height = "100%"; │ │ │ │ + this.groupDiv.style.width = "100%"; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: removeComponent │ │ │ │ - * Removes a point from geometry components. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The component was removed. │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ */ │ │ │ │ - removeComponent: function(point) { │ │ │ │ - var removed = this.components && (this.components.length > 3); │ │ │ │ - if (removed) { │ │ │ │ - //remove last point │ │ │ │ - this.components.pop(); │ │ │ │ + destroy: function() { │ │ │ │ + this.imageSrc = null; │ │ │ │ + this.imageSize = null; │ │ │ │ + this.isAlphaImage = 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]); │ │ │ │ + this.fixedRelativePosition = false; │ │ │ │ + this.positionBlocks = null; │ │ │ │ + │ │ │ │ + //remove our blocks │ │ │ │ + for (var i = 0; i < this.blocks.length; i++) { │ │ │ │ + var block = this.blocks[i]; │ │ │ │ + │ │ │ │ + if (block.image) { │ │ │ │ + block.div.removeChild(block.image); │ │ │ │ + } │ │ │ │ + block.image = null; │ │ │ │ + │ │ │ │ + if (block.div) { │ │ │ │ + this.groupDiv.removeChild(block.div); │ │ │ │ + } │ │ │ │ + block.div = null; │ │ │ │ } │ │ │ │ - return removed; │ │ │ │ + this.blocks = null; │ │ │ │ + │ │ │ │ + OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * APIMethod: setBackgroundColor │ │ │ │ */ │ │ │ │ - move: function(x, y) { │ │ │ │ - for (var i = 0, len = this.components.length; i < len - 1; i++) { │ │ │ │ - this.components[i].move(x, y); │ │ │ │ - } │ │ │ │ + setBackgroundColor: function(color) { │ │ │ │ + //does nothing since the framed popup's entire scheme is based on a │ │ │ │ + // an image -- changing the background color makes no sense. │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: rotate │ │ │ │ - * Rotate a geometry around some origin │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * angle - {Float} Rotation angle in degrees (measured counterclockwise │ │ │ │ - * from the positive x-axis) │ │ │ │ - * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation │ │ │ │ + * APIMethod: setBorder │ │ │ │ */ │ │ │ │ - rotate: function(angle, origin) { │ │ │ │ - for (var i = 0, len = this.components.length; i < len - 1; ++i) { │ │ │ │ - this.components[i].rotate(angle, origin); │ │ │ │ - } │ │ │ │ + setBorder: function() { │ │ │ │ + //does nothing since the framed popup's entire scheme is based on a │ │ │ │ + // an image -- changing the popup's border makes no sense. │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: resize │ │ │ │ - * Resize a geometry relative to some origin. Use this method to apply │ │ │ │ - * a uniform scaling to a geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * scale - {Float} Factor by which to scale the geometry. A scale of 2 │ │ │ │ - * doubles the size of the geometry in each dimension │ │ │ │ - * (lines, for example, will be twice as long, and polygons │ │ │ │ - * will have four times the area). │ │ │ │ - * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing │ │ │ │ - * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1. │ │ │ │ + * Method: setOpacity │ │ │ │ + * Sets the opacity of the popup. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} - The current geometry. │ │ │ │ + * Parameters: │ │ │ │ + * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ + //does nothing since we suppose that we'll never apply an opacity │ │ │ │ + // to a framed popup │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: transform │ │ │ │ - * Reproject the components geometry from source to dest. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * source - {<OpenLayers.Projection>} │ │ │ │ - * dest - {<OpenLayers.Projection>} │ │ │ │ + * APIMethod: setSize │ │ │ │ + * Overridden here, because we need to update the blocks whenever the size │ │ │ │ + * of the popup has changed. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} │ │ │ │ + * Parameters: │ │ │ │ + * contentSize - {<OpenLayers.Size>} the new size for the popup's │ │ │ │ + * contents div (in pixels). │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - }, │ │ │ │ + setSize: function(contentSize) { │ │ │ │ + OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: getCentroid │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry.Point>} The centroid of the collection │ │ │ │ - */ │ │ │ │ - getCentroid: function() { │ │ │ │ - if (this.components) { │ │ │ │ - var len = this.components.length; │ │ │ │ - if (len > 0 && len <= 2) { │ │ │ │ - return this.components[0].clone(); │ │ │ │ - } else if (len > 2) { │ │ │ │ - var sumX = 0.0; │ │ │ │ - var sumY = 0.0; │ │ │ │ - var x0 = this.components[0].x; │ │ │ │ - var y0 = this.components[0].y; │ │ │ │ - var area = -1 * this.getArea(); │ │ │ │ - if (area != 0) { │ │ │ │ - for (var i = 0; i < len - 1; i++) { │ │ │ │ - var b = this.components[i]; │ │ │ │ - var c = this.components[i + 1]; │ │ │ │ - sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0)); │ │ │ │ - sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0)); │ │ │ │ - } │ │ │ │ - var x = x0 + sumX / (6 * area); │ │ │ │ - var y = y0 + sumY / (6 * area); │ │ │ │ - } else { │ │ │ │ - for (var i = 0; i < len - 1; i++) { │ │ │ │ - sumX += this.components[i].x; │ │ │ │ - sumY += this.components[i].y; │ │ │ │ - } │ │ │ │ - var x = sumX / (len - 1); │ │ │ │ - var y = sumY / (len - 1); │ │ │ │ - } │ │ │ │ - return new OpenLayers.Geometry.Point(x, y); │ │ │ │ - } else { │ │ │ │ - return null; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + this.updateBlocks(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: updateRelativePosition │ │ │ │ + * When the relative position changes, we need to set the new padding │ │ │ │ + * BBOX on the popup, reposition the close div, and update the blocks. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + updateRelativePosition: function() { │ │ │ │ + │ │ │ │ + //update the padding │ │ │ │ + this.padding = this.positionBlocks[this.relativePosition].padding; │ │ │ │ + │ │ │ │ + //update the position of our close box to new padding │ │ │ │ + if (this.closeDiv) { │ │ │ │ + // use the content div's css padding to determine if we should │ │ │ │ + // padd the close div │ │ │ │ + var contentDivPadding = this.getContentDivPadding(); │ │ │ │ + │ │ │ │ + this.closeDiv.style.right = contentDivPadding.right + │ │ │ │ + this.padding.right + "px"; │ │ │ │ + this.closeDiv.style.top = contentDivPadding.top + │ │ │ │ + this.padding.top + "px"; │ │ │ │ } │ │ │ │ - return area; │ │ │ │ + │ │ │ │ + this.updateBlocks(); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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. │ │ │ │ - * │ │ │ │ + /** │ │ │ │ + * Method: calculateNewPx │ │ │ │ + * Besides the standard offset as determined by the Anchored class, our │ │ │ │ + * Framed popups have a special 'offset' property for each of their │ │ │ │ + * positions, which is used to offset the popup relative to its anchor. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * projection - {<OpenLayers.Projection>} The spatial reference system │ │ │ │ - * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ - * assumed. │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * {<OpenLayers.Pixel>} The the new px position of the popup on the screen │ │ │ │ + * relative to the passed-in px. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + calculateNewPx: function(px) { │ │ │ │ + var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply( │ │ │ │ + this, arguments │ │ │ │ + ); │ │ │ │ + │ │ │ │ + newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset); │ │ │ │ + │ │ │ │ + return newPx; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: containsPoint │ │ │ │ - * Test if a point is inside a linear ring. For the case where a point │ │ │ │ - * is coincident with a linear ring edge, returns 1. Otherwise, │ │ │ │ - * returns boolean. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean | Number} The point is inside the linear ring. Returns 1 if │ │ │ │ - * the point is coincident with an edge. Returns boolean otherwise. │ │ │ │ + * Method: createBlocks │ │ │ │ */ │ │ │ │ - containsPoint: function(point) { │ │ │ │ - var approx = OpenLayers.Number.limitSigDigs; │ │ │ │ - var digs = 14; │ │ │ │ - var px = approx(point.x, digs); │ │ │ │ - var py = approx(point.y, digs); │ │ │ │ + createBlocks: function() { │ │ │ │ + this.blocks = []; │ │ │ │ │ │ │ │ - function getX(y, x1, y1, x2, y2) { │ │ │ │ - return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2; │ │ │ │ + //since all positions contain the same number of blocks, we can │ │ │ │ + // just pick the first position and use its blocks array to create │ │ │ │ + // our blocks array │ │ │ │ + var firstPosition = null; │ │ │ │ + for (var key in this.positionBlocks) { │ │ │ │ + firstPosition = key; │ │ │ │ + break; │ │ │ │ } │ │ │ │ - var numSeg = this.components.length - 1; │ │ │ │ - var start, end, x1, y1, x2, y2, cx, cy; │ │ │ │ - var crosses = 0; │ │ │ │ - for (var i = 0; i < numSeg; ++i) { │ │ │ │ - start = this.components[i]; │ │ │ │ - x1 = approx(start.x, digs); │ │ │ │ - y1 = approx(start.y, digs); │ │ │ │ - end = this.components[i + 1]; │ │ │ │ - x2 = approx(end.x, digs); │ │ │ │ - y2 = approx(end.y, digs); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * The following conditions enforce five edge-crossing rules: │ │ │ │ - * 1. points coincident with edges are considered contained; │ │ │ │ - * 2. an upward edge includes its starting endpoint, and │ │ │ │ - * excludes its final endpoint; │ │ │ │ - * 3. a downward edge excludes its starting endpoint, and │ │ │ │ - * includes its final endpoint; │ │ │ │ - * 4. horizontal edges are excluded; and │ │ │ │ - * 5. the edge-ray intersection point must be strictly right │ │ │ │ - * of the point P. │ │ │ │ - */ │ │ │ │ - if (y1 == y2) { │ │ │ │ - // horizontal edge │ │ │ │ - if (py == y1) { │ │ │ │ - // point on horizontal line │ │ │ │ - if (x1 <= x2 && (px >= x1 && px <= x2) || // right or vert │ │ │ │ - x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert │ │ │ │ - // point on edge │ │ │ │ - crosses = -1; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - // ignore other horizontal edges │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ - cx = approx(getX(py, x1, y1, x2, y2), digs); │ │ │ │ - if (cx == px) { │ │ │ │ - // point on line │ │ │ │ - if (y1 < y2 && (py >= y1 && py <= y2) || // upward │ │ │ │ - y1 > y2 && (py <= y1 && py >= y2)) { // downward │ │ │ │ - // point on edge │ │ │ │ - crosses = -1; │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (cx <= px) { │ │ │ │ - // no crossing to the right │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ - if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) { │ │ │ │ - // no crossing │ │ │ │ - continue; │ │ │ │ - } │ │ │ │ - if (y1 < y2 && (py >= y1 && py < y2) || // upward │ │ │ │ - y1 > y2 && (py < y1 && py >= y2)) { // downward │ │ │ │ - ++crosses; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var contained = (crosses == -1) ? │ │ │ │ - // on edge │ │ │ │ - 1 : │ │ │ │ - // even (out) or odd (in) │ │ │ │ - !!(crosses & 1); │ │ │ │ + var position = this.positionBlocks[firstPosition]; │ │ │ │ + for (var i = 0; i < position.blocks.length; i++) { │ │ │ │ │ │ │ │ - return contained; │ │ │ │ - }, │ │ │ │ + var block = {}; │ │ │ │ + this.blocks.push(block); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: intersects │ │ │ │ - * Determine if the input geometry intersects this one. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} Any type of geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The input geometry intersects this one. │ │ │ │ - */ │ │ │ │ - intersects: function(geometry) { │ │ │ │ - var intersect = false; │ │ │ │ - if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - intersect = this.containsPoint(geometry); │ │ │ │ - } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") { │ │ │ │ - intersect = geometry.intersects(this); │ │ │ │ - } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { │ │ │ │ - intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply( │ │ │ │ - this, [geometry] │ │ │ │ + var divId = this.id + '_FrameDecorationDiv_' + i; │ │ │ │ + block.div = OpenLayers.Util.createDiv(divId, │ │ │ │ + null, null, null, "absolute", null, "hidden", null │ │ │ │ ); │ │ │ │ - } 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; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + │ │ │ │ + var imgId = this.id + '_FrameDecorationImg_' + i; │ │ │ │ + var imageCreator = │ │ │ │ + (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv : │ │ │ │ + OpenLayers.Util.createImage; │ │ │ │ + │ │ │ │ + block.image = imageCreator(imgId, │ │ │ │ + null, this.imageSize, this.imageSrc, │ │ │ │ + "absolute", null, null, null │ │ │ │ + ); │ │ │ │ + │ │ │ │ + block.div.appendChild(block.image); │ │ │ │ + this.groupDiv.appendChild(block.div); │ │ │ │ } │ │ │ │ - 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. │ │ │ │ + * Method: updateBlocks │ │ │ │ + * Internal method, called on initialize and when the popup's relative │ │ │ │ + * position has changed. This function takes care of re-positioning │ │ │ │ + * the popup's blocks in their appropropriate places. │ │ │ │ */ │ │ │ │ - getVertices: function(nodes) { │ │ │ │ - return (nodes === true) ? [] : this.components.slice(0, this.components.length - 1); │ │ │ │ + updateBlocks: function() { │ │ │ │ + if (!this.blocks) { │ │ │ │ + this.createBlocks(); │ │ │ │ + } │ │ │ │ + │ │ │ │ + if (this.size && this.relativePosition) { │ │ │ │ + var position = this.positionBlocks[this.relativePosition]; │ │ │ │ + for (var i = 0; i < position.blocks.length; i++) { │ │ │ │ + │ │ │ │ + var positionBlock = position.blocks[i]; │ │ │ │ + var block = this.blocks[i]; │ │ │ │ + │ │ │ │ + // adjust sizes │ │ │ │ + var l = positionBlock.anchor.left; │ │ │ │ + var b = positionBlock.anchor.bottom; │ │ │ │ + var r = positionBlock.anchor.right; │ │ │ │ + var t = positionBlock.anchor.top; │ │ │ │ + │ │ │ │ + //note that we use the isNaN() test here because if the │ │ │ │ + // size object is initialized with a "auto" parameter, the │ │ │ │ + // size constructor calls parseFloat() on the string, │ │ │ │ + // which will turn it into NaN │ │ │ │ + // │ │ │ │ + var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l) : │ │ │ │ + positionBlock.size.w; │ │ │ │ + │ │ │ │ + var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t) : │ │ │ │ + positionBlock.size.h; │ │ │ │ + │ │ │ │ + block.div.style.width = (w < 0 ? 0 : w) + 'px'; │ │ │ │ + block.div.style.height = (h < 0 ? 0 : h) + 'px'; │ │ │ │ + │ │ │ │ + block.div.style.left = (l != null) ? l + 'px' : ''; │ │ │ │ + block.div.style.bottom = (b != null) ? b + 'px' : ''; │ │ │ │ + block.div.style.right = (r != null) ? r + 'px' : ''; │ │ │ │ + block.div.style.top = (t != null) ? t + 'px' : ''; │ │ │ │ + │ │ │ │ + block.image.style.left = positionBlock.position.x + 'px'; │ │ │ │ + block.image.style.top = positionBlock.position.y + 'px'; │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.contentDiv.style.left = this.padding.left + "px"; │ │ │ │ + this.contentDiv.style.top = this.padding.top + "px"; │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.LinearRing" │ │ │ │ + CLASS_NAME: "OpenLayers.Popup.Framed" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Geometry/Polygon.js │ │ │ │ + OpenLayers/Popup/FramedCloud.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Geometry/Collection.js │ │ │ │ - * @requires OpenLayers/Geometry/LinearRing.js │ │ │ │ + * @requires OpenLayers/Popup/Framed.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Bounds.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Pixel.js │ │ │ │ + * @requires OpenLayers/BaseTypes/Size.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Geometry.Polygon │ │ │ │ - * Polygon is a collection of Geometry.LinearRings. │ │ │ │ + * Class: OpenLayers.Popup.FramedCloud │ │ │ │ * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Geometry.Collection> │ │ │ │ - * - <OpenLayers.Geometry> │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Popup.Framed> │ │ │ │ */ │ │ │ │ -OpenLayers.Geometry.Polygon = OpenLayers.Class( │ │ │ │ - OpenLayers.Geometry.Collection, { │ │ │ │ +OpenLayers.Popup.FramedCloud = │ │ │ │ + OpenLayers.Class(OpenLayers.Popup.Framed, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: contentDisplayClass │ │ │ │ + * {String} The CSS class of the popup content div. │ │ │ │ + */ │ │ │ │ + contentDisplayClass: "olFramedCloudPopupContent", │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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: autoSize │ │ │ │ + * {Boolean} Framed Cloud is autosizing by default. │ │ │ │ */ │ │ │ │ - componentTypes: ["OpenLayers.Geometry.LinearRing"], │ │ │ │ + autoSize: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Geometry.Polygon │ │ │ │ - * Constructor for a Polygon geometry. │ │ │ │ - * The first ring (this.component[0])is the outer bounds of the polygon and │ │ │ │ - * all subsequent rings (this.component[1-n]) are internal holes. │ │ │ │ - * │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * components - {Array(<OpenLayers.Geometry.LinearRing>)} │ │ │ │ + * APIProperty: panMapIfOutOfView │ │ │ │ + * {Boolean} Framed Cloud does pan into view by default. │ │ │ │ + */ │ │ │ │ + panMapIfOutOfView: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: imageSize │ │ │ │ + * {<OpenLayers.Size>} │ │ │ │ + */ │ │ │ │ + imageSize: new OpenLayers.Size(1276, 736), │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: isAlphaImage │ │ │ │ + * {Boolean} The FramedCloud does not use an alpha image (in honor of the │ │ │ │ + * good ie6 folk out there) │ │ │ │ */ │ │ │ │ + isAlphaImage: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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 │ │ │ │ + * APIProperty: fixedRelativePosition │ │ │ │ + * {Boolean} The Framed Cloud popup works in just one fixed position. │ │ │ │ */ │ │ │ │ - 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()); │ │ │ │ - } │ │ │ │ + fixedRelativePosition: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: positionBlocks │ │ │ │ + * {Object} Hash of differen position blocks, keyed by relativePosition │ │ │ │ + * two-character code string (ie "tl", "tr", "bl", "br") │ │ │ │ + */ │ │ │ │ + positionBlocks: { │ │ │ │ + "tl": { │ │ │ │ + 'offset': new OpenLayers.Pixel(44, 0), │ │ │ │ + 'padding': new OpenLayers.Bounds(8, 40, 8, 9), │ │ │ │ + 'blocks': [{ // top-left │ │ │ │ + size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 51, 22, 0), │ │ │ │ + position: new OpenLayers.Pixel(0, 0) │ │ │ │ + }, { //top-right │ │ │ │ + size: new OpenLayers.Size(22, 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 50, 0, 0), │ │ │ │ + position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ + }, { //bottom-left │ │ │ │ + size: new OpenLayers.Size('auto', 19), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 32, 22, null), │ │ │ │ + position: new OpenLayers.Pixel(0, -631) │ │ │ │ + }, { //bottom-right │ │ │ │ + size: new OpenLayers.Size(22, 18), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 32, 0, null), │ │ │ │ + position: new OpenLayers.Pixel(-1238, -632) │ │ │ │ + }, { // stem │ │ │ │ + size: new OpenLayers.Size(81, 35), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 0, 0, null), │ │ │ │ + position: new OpenLayers.Pixel(0, -688) │ │ │ │ + }] │ │ │ │ + }, │ │ │ │ + "tr": { │ │ │ │ + 'offset': new OpenLayers.Pixel(-45, 0), │ │ │ │ + 'padding': new OpenLayers.Bounds(8, 40, 8, 9), │ │ │ │ + 'blocks': [{ // top-left │ │ │ │ + size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 51, 22, 0), │ │ │ │ + position: new OpenLayers.Pixel(0, 0) │ │ │ │ + }, { //top-right │ │ │ │ + size: new OpenLayers.Size(22, 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 50, 0, 0), │ │ │ │ + position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ + }, { //bottom-left │ │ │ │ + size: new OpenLayers.Size('auto', 19), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 32, 22, null), │ │ │ │ + position: new OpenLayers.Pixel(0, -631) │ │ │ │ + }, { //bottom-right │ │ │ │ + size: new OpenLayers.Size(22, 19), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 32, 0, null), │ │ │ │ + position: new OpenLayers.Pixel(-1238, -631) │ │ │ │ + }, { // stem │ │ │ │ + size: new OpenLayers.Size(81, 35), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 0, null, null), │ │ │ │ + position: new OpenLayers.Pixel(-215, -687) │ │ │ │ + }] │ │ │ │ + }, │ │ │ │ + "bl": { │ │ │ │ + 'offset': new OpenLayers.Pixel(45, 0), │ │ │ │ + 'padding': new OpenLayers.Bounds(8, 9, 8, 40), │ │ │ │ + 'blocks': [{ // top-left │ │ │ │ + size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 21, 22, 32), │ │ │ │ + position: new OpenLayers.Pixel(0, 0) │ │ │ │ + }, { //top-right │ │ │ │ + size: new OpenLayers.Size(22, 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 21, 0, 32), │ │ │ │ + position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ + }, { //bottom-left │ │ │ │ + size: new OpenLayers.Size('auto', 21), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 0, 22, null), │ │ │ │ + position: new OpenLayers.Pixel(0, -629) │ │ │ │ + }, { //bottom-right │ │ │ │ + size: new OpenLayers.Size(22, 21), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 0, 0, null), │ │ │ │ + position: new OpenLayers.Pixel(-1238, -629) │ │ │ │ + }, { // stem │ │ │ │ + size: new OpenLayers.Size(81, 33), │ │ │ │ + anchor: new OpenLayers.Bounds(null, null, 0, 0), │ │ │ │ + position: new OpenLayers.Pixel(-101, -674) │ │ │ │ + }] │ │ │ │ + }, │ │ │ │ + "br": { │ │ │ │ + 'offset': new OpenLayers.Pixel(-44, 0), │ │ │ │ + 'padding': new OpenLayers.Bounds(8, 9, 8, 40), │ │ │ │ + 'blocks': [{ // top-left │ │ │ │ + size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 21, 22, 32), │ │ │ │ + position: new OpenLayers.Pixel(0, 0) │ │ │ │ + }, { //top-right │ │ │ │ + size: new OpenLayers.Size(22, 'auto'), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 21, 0, 32), │ │ │ │ + position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ + }, { //bottom-left │ │ │ │ + size: new OpenLayers.Size('auto', 21), │ │ │ │ + anchor: new OpenLayers.Bounds(0, 0, 22, null), │ │ │ │ + position: new OpenLayers.Pixel(0, -629) │ │ │ │ + }, { //bottom-right │ │ │ │ + size: new OpenLayers.Size(22, 21), │ │ │ │ + anchor: new OpenLayers.Bounds(null, 0, 0, null), │ │ │ │ + position: new OpenLayers.Pixel(-1238, -629) │ │ │ │ + }, { // stem │ │ │ │ + size: new OpenLayers.Size(81, 33), │ │ │ │ + anchor: new OpenLayers.Bounds(0, null, null, 0), │ │ │ │ + position: new OpenLayers.Pixel(-311, -674) │ │ │ │ + }] │ │ │ │ } │ │ │ │ - return area; │ │ │ │ }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * APIProperty: minSize │ │ │ │ + * {<OpenLayers.Size>} │ │ │ │ + */ │ │ │ │ + minSize: new OpenLayers.Size(105, 10), │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: maxSize │ │ │ │ + * {<OpenLayers.Size>} │ │ │ │ + */ │ │ │ │ + maxSize: new OpenLayers.Size(1200, 660), │ │ │ │ + │ │ │ │ /** │ │ │ │ - * APIMethod: getGeodesicArea │ │ │ │ - * Calculate the approximate area of the polygon were it projected onto │ │ │ │ - * the earth. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * projection - {<OpenLayers.Projection>} The spatial reference system │ │ │ │ - * for the geometry coordinates. If not provided, Geographic/WGS84 is │ │ │ │ - * assumed. │ │ │ │ + * Constructor: OpenLayers.Popup.FramedCloud │ │ │ │ * │ │ │ │ - * 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. │ │ │ │ + * Parameters: │ │ │ │ + * id - {String} │ │ │ │ + * lonlat - {<OpenLayers.LonLat>} │ │ │ │ + * contentSize - {<OpenLayers.Size>} │ │ │ │ + * contentHTML - {String} │ │ │ │ + * anchor - {Object} Object to which we'll anchor the popup. Must expose │ │ │ │ + * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) │ │ │ │ + * (Note that this is generally an <OpenLayers.Icon>). │ │ │ │ + * closeBox - {Boolean} │ │ │ │ + * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ */ │ │ │ │ - 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)); │ │ │ │ + initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, │ │ │ │ + closeBoxCallback) { │ │ │ │ + │ │ │ │ + this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png'); │ │ │ │ + OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments); │ │ │ │ + this.contentDiv.className = this.contentDisplayClass; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Popup.FramedCloud" │ │ │ │ + }); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control │ │ │ │ + * Controls affect the display or behavior of the map. They allow everything │ │ │ │ + * from panning and zooming to displaying a scale indicator. Controls by │ │ │ │ + * default are added to the map they are contained within however it is │ │ │ │ + * possible to add a control to an external div by passing the div in the │ │ │ │ + * options parameter. │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * The following example shows how to add many of the common controls │ │ │ │ + * to a map. │ │ │ │ + * │ │ │ │ + * > var map = new OpenLayers.Map('map', { controls: [] }); │ │ │ │ + * > │ │ │ │ + * > map.addControl(new OpenLayers.Control.PanZoomBar()); │ │ │ │ + * > map.addControl(new OpenLayers.Control.LayerSwitcher({'ascending':false})); │ │ │ │ + * > map.addControl(new OpenLayers.Control.Permalink()); │ │ │ │ + * > map.addControl(new OpenLayers.Control.Permalink('permalink')); │ │ │ │ + * > map.addControl(new OpenLayers.Control.MousePosition()); │ │ │ │ + * > map.addControl(new OpenLayers.Control.OverviewMap()); │ │ │ │ + * > map.addControl(new OpenLayers.Control.KeyboardDefaults()); │ │ │ │ + * │ │ │ │ + * The next code fragment is a quick example of how to intercept │ │ │ │ + * shift-mouse click to display the extent of the bounding box │ │ │ │ + * dragged out by the user. Usually controls are not created │ │ │ │ + * in exactly this manner. See the source for a more complete │ │ │ │ + * example: │ │ │ │ + * │ │ │ │ + * > var control = new OpenLayers.Control(); │ │ │ │ + * > OpenLayers.Util.extend(control, { │ │ │ │ + * > draw: function () { │ │ │ │ + * > // this Handler.Box will intercept the shift-mousedown │ │ │ │ + * > // before Control.MouseDefault gets to see it │ │ │ │ + * > this.box = new OpenLayers.Handler.Box( control, │ │ │ │ + * > {"done": this.notice}, │ │ │ │ + * > {keyMask: OpenLayers.Handler.MOD_SHIFT}); │ │ │ │ + * > this.box.activate(); │ │ │ │ + * > }, │ │ │ │ + * > │ │ │ │ + * > notice: function (bounds) { │ │ │ │ + * > OpenLayers.Console.userError(bounds); │ │ │ │ + * > } │ │ │ │ + * > }); │ │ │ │ + * > map.addControl(control); │ │ │ │ + * │ │ │ │ + */ │ │ │ │ +OpenLayers.Control = OpenLayers.Class({ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: id │ │ │ │ + * {String} │ │ │ │ + */ │ │ │ │ + id: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: map │ │ │ │ + * {<OpenLayers.Map>} this gets set in the addControl() function in │ │ │ │ + * OpenLayers.Map │ │ │ │ + */ │ │ │ │ + map: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: div │ │ │ │ + * {DOMElement} The element that contains the control, if not present the │ │ │ │ + * control is placed inside the map. │ │ │ │ + */ │ │ │ │ + div: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: type │ │ │ │ + * {Number} Controls can have a 'type'. The type determines the type of │ │ │ │ + * interactions which are possible with them when they are placed in an │ │ │ │ + * <OpenLayers.Control.Panel>. │ │ │ │ + */ │ │ │ │ + type: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: allowSelection │ │ │ │ + * {Boolean} By default, controls do not allow selection, because │ │ │ │ + * it may interfere with map dragging. If this is true, OpenLayers │ │ │ │ + * will not prevent selection of the control. │ │ │ │ + * Default is false. │ │ │ │ + */ │ │ │ │ + allowSelection: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: displayClass │ │ │ │ + * {string} This property is used for CSS related to the drawing of the │ │ │ │ + * Control. │ │ │ │ + */ │ │ │ │ + displayClass: "", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: title │ │ │ │ + * {string} This property is used for showing a tooltip over the │ │ │ │ + * Control. │ │ │ │ + */ │ │ │ │ + title: "", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * false. │ │ │ │ + */ │ │ │ │ + autoActivate: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: active │ │ │ │ + * {Boolean} The control is active (read-only). Use <activate> and │ │ │ │ + * <deactivate> to change control state. │ │ │ │ + */ │ │ │ │ + active: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: handlerOptions │ │ │ │ + * {Object} Used to set non-default properties on the control's handler │ │ │ │ + */ │ │ │ │ + handlerOptions: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: handler │ │ │ │ + * {<OpenLayers.Handler>} null │ │ │ │ + */ │ │ │ │ + handler: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: eventListeners │ │ │ │ + * {Object} If set as an option at construction, the eventListeners │ │ │ │ + * object will be registered with <OpenLayers.Events.on>. Object │ │ │ │ + * structure must be a listeners object as shown in the example for │ │ │ │ + * the events.on method. │ │ │ │ + */ │ │ │ │ + eventListeners: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Listeners will be called with a reference to an event object. The │ │ │ │ + * properties of this event depends on exactly what happened. │ │ │ │ + * │ │ │ │ + * All event objects have at least the following properties: │ │ │ │ + * object - {Object} A reference to control.events.object (a reference │ │ │ │ + * to the control). │ │ │ │ + * element - {DOMElement} A reference to control.events.element (which │ │ │ │ + * will be null unless documented otherwise). │ │ │ │ + * │ │ │ │ + * Supported map event types: │ │ │ │ + * activate - Triggered when activated. │ │ │ │ + * deactivate - Triggered when deactivated. │ │ │ │ + */ │ │ │ │ + events: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control │ │ │ │ + * Create an OpenLayers Control. The options passed as a parameter │ │ │ │ + * directly extend the control. For example passing the following: │ │ │ │ + * │ │ │ │ + * > var control = new OpenLayers.Control({div: myDiv}); │ │ │ │ + * │ │ │ │ + * Overrides the default div attribute value of null. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + // We do this before the extend so that instances can override │ │ │ │ + // className in options. │ │ │ │ + this.displayClass = │ │ │ │ + this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, ""); │ │ │ │ + │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + │ │ │ │ + this.events = new OpenLayers.Events(this); │ │ │ │ + if (this.eventListeners instanceof Object) { │ │ │ │ + this.events.on(this.eventListeners); │ │ │ │ + } │ │ │ │ + if (this.id == null) { │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * The destroy method is used to perform any clean up before the control │ │ │ │ + * is dereferenced. Typically this is where event listeners are removed │ │ │ │ + * to prevent memory leaks. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + if (this.events) { │ │ │ │ + if (this.eventListeners) { │ │ │ │ + this.events.un(this.eventListeners); │ │ │ │ + } │ │ │ │ + this.events.destroy(); │ │ │ │ + this.events = null; │ │ │ │ + } │ │ │ │ + this.eventListeners = null; │ │ │ │ + │ │ │ │ + // eliminate circular references │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.destroy(); │ │ │ │ + this.handler = null; │ │ │ │ + } │ │ │ │ + if (this.handlers) { │ │ │ │ + for (var key in this.handlers) { │ │ │ │ + if (this.handlers.hasOwnProperty(key) && │ │ │ │ + typeof this.handlers[key].destroy == "function") { │ │ │ │ + this.handlers[key].destroy(); │ │ │ │ } │ │ │ │ } │ │ │ │ - return area; │ │ │ │ - }, │ │ │ │ + this.handlers = null; │ │ │ │ + } │ │ │ │ + if (this.map) { │ │ │ │ + this.map.removeControl(this); │ │ │ │ + this.map = null; │ │ │ │ + } │ │ │ │ + this.div = null; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the control. This is done through an accessor │ │ │ │ + * so that subclasses can override this and take special action once │ │ │ │ + * they have their map variable set. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + this.map = map; │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.setMap(map); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * The draw method is called when the control is ready to be displayed │ │ │ │ + * on the page. If a div has not been created one is created. Controls │ │ │ │ + * with a visual component will almost always want to override this method │ │ │ │ + * to customize the look of control. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * px - {<OpenLayers.Pixel>} The top-left pixel position of the control │ │ │ │ + * or null. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A reference to the DIV DOMElement containing the control │ │ │ │ + */ │ │ │ │ + draw: function(px) { │ │ │ │ + if (this.div == null) { │ │ │ │ + this.div = OpenLayers.Util.createDiv(this.id); │ │ │ │ + this.div.className = this.displayClass; │ │ │ │ + if (!this.allowSelection) { │ │ │ │ + this.div.className += " olControlNoSelect"; │ │ │ │ + this.div.setAttribute("unselectable", "on", 0); │ │ │ │ + this.div.onselectstart = OpenLayers.Function.False; │ │ │ │ + } │ │ │ │ + if (this.title != "") { │ │ │ │ + this.div.title = this.title; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (px != null) { │ │ │ │ + this.position = px.clone(); │ │ │ │ + } │ │ │ │ + this.moveTo(this.position); │ │ │ │ + return this.div; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: moveTo │ │ │ │ + * Sets the left and top style attributes to the passed in pixel │ │ │ │ + * coordinates. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * px - {<OpenLayers.Pixel>} │ │ │ │ + */ │ │ │ │ + moveTo: function(px) { │ │ │ │ + if ((px != null) && (this.div != null)) { │ │ │ │ + this.div.style.left = px.x + "px"; │ │ │ │ + this.div.style.top = px.y + "px"; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: activate │ │ │ │ + * Explicitly activates a control and it's associated │ │ │ │ + * handler if one has been set. Controls can be │ │ │ │ + * deactivated by calling the deactivate() method. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} True if the control was successfully activated or │ │ │ │ + * false if the control was already active. │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.activate(); │ │ │ │ + } │ │ │ │ + this.active = true; │ │ │ │ + if (this.map) { │ │ │ │ + OpenLayers.Element.addClass( │ │ │ │ + this.map.viewPortDiv, │ │ │ │ + this.displayClass.replace(/ /g, "") + "Active" │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("activate"); │ │ │ │ + return true; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivates a control and it's associated handler if any. The exact │ │ │ │ + * effect of this depends on the control itself. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} True if the control was effectively deactivated or false │ │ │ │ + * if the control was already inactive. │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.deactivate(); │ │ │ │ + } │ │ │ │ + this.active = false; │ │ │ │ + if (this.map) { │ │ │ │ + OpenLayers.Element.removeClass( │ │ │ │ + this.map.viewPortDiv, │ │ │ │ + this.displayClass.replace(/ /g, "") + "Active" │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("deactivate"); │ │ │ │ + return true; │ │ │ │ + } │ │ │ │ + return false; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Control" │ │ │ │ +}); │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Control.TYPE_BUTTON │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.TYPE_BUTTON = 1; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Control.TYPE_TOGGLE │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.TYPE_TOGGLE = 2; │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Control.TYPE_TOOL │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.TYPE_TOOL = 3; │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Handler.js │ │ │ │ + ====================================================================== */ │ │ │ │ + │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Events.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Handler │ │ │ │ + * Base class to construct a higher-level handler for event sequences. All │ │ │ │ + * handlers have activate and deactivate methods. In addition, they have │ │ │ │ + * methods named like browser events. When a handler is activated, any │ │ │ │ + * additional methods named like a browser event is registered as a │ │ │ │ + * listener for the corresponding event. When a handler is deactivated, │ │ │ │ + * those same methods are unregistered as event listeners. │ │ │ │ + * │ │ │ │ + * Handlers also typically have a callbacks object with keys named like │ │ │ │ + * the abstracted events or event sequences that they are in charge of │ │ │ │ + * handling. The controls that wrap handlers define the methods that │ │ │ │ + * correspond to these abstract events - so instead of listening for │ │ │ │ + * individual browser events, they only listen for the abstract events │ │ │ │ + * defined by the handler. │ │ │ │ + * │ │ │ │ + * Handlers are created by controls, which ultimately have the responsibility │ │ │ │ + * of making changes to the the state of the application. Handlers │ │ │ │ + * themselves may make temporary changes, but in general are expected to │ │ │ │ + * return the application in the same state that they found it. │ │ │ │ + */ │ │ │ │ +OpenLayers.Handler = OpenLayers.Class({ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: id │ │ │ │ + * {String} │ │ │ │ + */ │ │ │ │ + id: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: control │ │ │ │ + * {<OpenLayers.Control>}. The control that initialized this handler. The │ │ │ │ + * control is assumed to have a valid map property - that map is used │ │ │ │ + * in the handler's own setMap method. │ │ │ │ + */ │ │ │ │ + control: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: map │ │ │ │ + * {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + map: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: keyMask │ │ │ │ + * {Integer} Use bitwise operators and one or more of the OpenLayers.Handler │ │ │ │ + * constants to construct a keyMask. The keyMask is used by │ │ │ │ + * <checkModifiers>. If the keyMask matches the combination of keys │ │ │ │ + * down on an event, checkModifiers returns true. │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * (code) │ │ │ │ + * // handler only responds if the Shift key is down │ │ │ │ + * handler.keyMask = OpenLayers.Handler.MOD_SHIFT; │ │ │ │ + * │ │ │ │ + * // handler only responds if Ctrl-Shift is down │ │ │ │ + * handler.keyMask = OpenLayers.Handler.MOD_SHIFT | │ │ │ │ + * OpenLayers.Handler.MOD_CTRL; │ │ │ │ + * (end) │ │ │ │ + */ │ │ │ │ + keyMask: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: active │ │ │ │ + * {Boolean} │ │ │ │ + */ │ │ │ │ + active: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: evt │ │ │ │ + * {Event} This property references the last event handled by the handler. │ │ │ │ + * Note that this property is not part of the stable API. Use of the │ │ │ │ + * evt property should be restricted to controls in the library │ │ │ │ + * or other applications that are willing to update with changes to │ │ │ │ + * the OpenLayers code. │ │ │ │ + */ │ │ │ │ + evt: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: touch │ │ │ │ + * {Boolean} Indicates the support of touch events. When touch events are │ │ │ │ + * started touch will be true and all mouse related listeners will do │ │ │ │ + * nothing. │ │ │ │ + */ │ │ │ │ + touch: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Handler │ │ │ │ + * Construct a handler. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * control - {<OpenLayers.Control>} The control that initialized this │ │ │ │ + * handler. The control is assumed to have a valid map property; that │ │ │ │ + * map is used in the handler's own setMap method. If a map property │ │ │ │ + * is present in the options argument it will be used instead. │ │ │ │ + * callbacks - {Object} An object whose properties correspond to abstracted │ │ │ │ + * events or sequences of browser events. The values for these │ │ │ │ + * properties are functions defined by the control that get called by │ │ │ │ + * the handler. │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * the handler. │ │ │ │ + */ │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.control = control; │ │ │ │ + this.callbacks = callbacks; │ │ │ │ + │ │ │ │ + var map = this.map || control.map; │ │ │ │ + if (map) { │ │ │ │ + this.setMap(map); │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_"); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + this.map = map; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: checkModifiers │ │ │ │ + * Check the keyMask on the handler. If no <keyMask> is set, this always │ │ │ │ + * returns true. If a <keyMask> is set and it matches the combination │ │ │ │ + * of keys down on an event, this returns true. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The keyMask matches the keys down on an event. │ │ │ │ + */ │ │ │ │ + checkModifiers: function(evt) { │ │ │ │ + if (this.keyMask == null) { │ │ │ │ + return true; │ │ │ │ + } │ │ │ │ + /* calculate the keyboard modifier mask for this event */ │ │ │ │ + var keyModifiers = │ │ │ │ + (evt.shiftKey ? OpenLayers.Handler.MOD_SHIFT : 0) | │ │ │ │ + (evt.ctrlKey ? OpenLayers.Handler.MOD_CTRL : 0) | │ │ │ │ + (evt.altKey ? OpenLayers.Handler.MOD_ALT : 0) | │ │ │ │ + (evt.metaKey ? OpenLayers.Handler.MOD_META : 0); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: containsPoint │ │ │ │ - * Test if a point is inside a polygon. Points on a polygon edge are │ │ │ │ - * considered inside. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean | Number} The point is inside the polygon. Returns 1 if the │ │ │ │ - * point is on an edge. Returns boolean otherwise. │ │ │ │ - */ │ │ │ │ - containsPoint: function(point) { │ │ │ │ - var numRings = this.components.length; │ │ │ │ - var contained = false; │ │ │ │ - if (numRings > 0) { │ │ │ │ - // check exterior ring - 1 means on edge, boolean otherwise │ │ │ │ - contained = this.components[0].containsPoint(point); │ │ │ │ - if (contained !== 1) { │ │ │ │ - if (contained && numRings > 1) { │ │ │ │ - // check interior rings │ │ │ │ - var hole; │ │ │ │ - for (var i = 1; i < numRings; ++i) { │ │ │ │ - hole = this.components[i].containsPoint(point); │ │ │ │ - if (hole) { │ │ │ │ - if (hole === 1) { │ │ │ │ - // on edge │ │ │ │ - contained = 1; │ │ │ │ - } else { │ │ │ │ - // in hole │ │ │ │ - contained = false; │ │ │ │ - } │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /* if it differs from the handler object's key mask, │ │ │ │ + bail out of the event handler */ │ │ │ │ + return (keyModifiers == this.keyMask); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIMethod: activate │ │ │ │ + * Turn on the handler. Returns false if the handler was already active. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The handler was activated. │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + // register for event handlers defined on this class. │ │ │ │ + var events = OpenLayers.Events.prototype.BROWSER_EVENTS; │ │ │ │ + for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ + if (this[events[i]]) { │ │ │ │ + this.register(events[i], this[events[i]]); │ │ │ │ } │ │ │ │ - return contained; │ │ │ │ - }, │ │ │ │ + } │ │ │ │ + this.active = true; │ │ │ │ + return true; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: intersects │ │ │ │ - * Determine if the input geometry intersects this one. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} Any type of geometry. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The input geometry intersects this one. │ │ │ │ - */ │ │ │ │ - intersects: function(geometry) { │ │ │ │ - var intersect = false; │ │ │ │ - var i, len; │ │ │ │ - if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - intersect = this.containsPoint(geometry); │ │ │ │ - } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" || │ │ │ │ - geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { │ │ │ │ - // check if rings/linestrings intersect │ │ │ │ - for (i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ - intersect = geometry.intersects(this.components[i]); │ │ │ │ - if (intersect) { │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (!intersect) { │ │ │ │ - // check if this poly contains points of the ring/linestring │ │ │ │ - for (i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ - intersect = this.containsPoint(geometry.components[i]); │ │ │ │ - if (intersect) { │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - for (i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ - intersect = this.intersects(geometry.components[i]); │ │ │ │ - if (intersect) { │ │ │ │ - break; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Turn off the handler. Returns false if the handler was already inactive. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The handler was deactivated. │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + if (!this.active) { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + // unregister event handlers defined on this class. │ │ │ │ + var events = OpenLayers.Events.prototype.BROWSER_EVENTS; │ │ │ │ + for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ + if (this[events[i]]) { │ │ │ │ + this.unregister(events[i], this[events[i]]); │ │ │ │ } │ │ │ │ - // 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; │ │ │ │ - } │ │ │ │ + } │ │ │ │ + this.touch = false; │ │ │ │ + this.active = false; │ │ │ │ + return true; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: startTouch │ │ │ │ + * Start touch events, this method must be called by subclasses in │ │ │ │ + * "touchstart" method. When touch events are started <touch> will be │ │ │ │ + * true and all mouse related listeners will do nothing. │ │ │ │ + */ │ │ │ │ + startTouch: function() { │ │ │ │ + if (!this.touch) { │ │ │ │ + this.touch = true; │ │ │ │ + var events = [ │ │ │ │ + "mousedown", "mouseup", "mousemove", "click", "dblclick", │ │ │ │ + "mouseout" │ │ │ │ + ]; │ │ │ │ + for (var i = 0, len = events.length; i < len; i++) { │ │ │ │ + if (this[events[i]]) { │ │ │ │ + this.unregister(events[i], this[events[i]]); │ │ │ │ } │ │ │ │ } │ │ │ │ - return intersect; │ │ │ │ - }, │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: distanceTo │ │ │ │ - * Calculate the closest distance between two geometries (on the x-y plane). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} The target geometry. │ │ │ │ - * options - {Object} Optional properties for configuring the distance │ │ │ │ - * calculation. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * details - {Boolean} Return details from the distance calculation. │ │ │ │ - * Default is false. │ │ │ │ - * edge - {Boolean} Calculate the distance from this geometry to the │ │ │ │ - * nearest edge of the target geometry. Default is true. If true, │ │ │ │ - * calling distanceTo from a geometry that is wholly contained within │ │ │ │ - * the target will result in a non-zero distance. If false, whenever │ │ │ │ - * geometries intersect, calling distanceTo will return 0. If false, │ │ │ │ - * details cannot be returned. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Number | Object} The distance between this geometry and the target. │ │ │ │ - * If details is true, the return will be an object with distance, │ │ │ │ - * x0, y0, x1, and y1 properties. The x0 and y0 properties represent │ │ │ │ - * the coordinates of the closest point on this geometry. The x1 and y1 │ │ │ │ - * properties represent the coordinates of the closest point on the │ │ │ │ - * target geometry. │ │ │ │ - */ │ │ │ │ - distanceTo: function(geometry, options) { │ │ │ │ - var edge = !(options && options.edge === false); │ │ │ │ - var result; │ │ │ │ - // this is the case where we might not be looking for distance to edge │ │ │ │ - if (!edge && this.intersects(geometry)) { │ │ │ │ - result = 0; │ │ │ │ - } else { │ │ │ │ - result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply( │ │ │ │ - this, [geometry, options] │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - return result; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: callback │ │ │ │ + * Trigger the control's named callback with the given arguments │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} The key for the callback that is one of the properties │ │ │ │ + * of the handler's callbacks object. │ │ │ │ + * args - {Array(*)} An array of arguments (any type) with which to call │ │ │ │ + * the callback (defined by the control). │ │ │ │ + */ │ │ │ │ + callback: function(name, args) { │ │ │ │ + if (name && this.callbacks[name]) { │ │ │ │ + this.callbacks[name].apply(this.control, args); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.Polygon" │ │ │ │ - }); │ │ │ │ + /** │ │ │ │ + * Method: register │ │ │ │ + * register an event on the map │ │ │ │ + */ │ │ │ │ + register: function(name, method) { │ │ │ │ + // TODO: deal with registerPriority in 3.0 │ │ │ │ + this.map.events.registerPriority(name, this, method); │ │ │ │ + this.map.events.registerPriority(name, this, this.setEvent); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: unregister │ │ │ │ + * unregister an event from the map │ │ │ │ + */ │ │ │ │ + unregister: function(name, method) { │ │ │ │ + this.map.events.unregister(name, this, method); │ │ │ │ + this.map.events.unregister(name, this, this.setEvent); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setEvent │ │ │ │ + * With each registered browser event, the handler sets its own evt │ │ │ │ + * property. This property can be accessed by controls if needed │ │ │ │ + * to get more information about the event that the handler is │ │ │ │ + * processing. │ │ │ │ + * │ │ │ │ + * This allows modifier keys on the event to be checked (alt, shift, ctrl, │ │ │ │ + * and meta cannot be checked with the keyboard handler). For a │ │ │ │ + * control to determine which modifier keys are associated with the │ │ │ │ + * event that a handler is currently processing, it should access │ │ │ │ + * (code)handler.evt.altKey || handler.evt.shiftKey || │ │ │ │ + * handler.evt.ctrlKey || handler.evt.metaKey(end). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} The browser event. │ │ │ │ + */ │ │ │ │ + setEvent: function(evt) { │ │ │ │ + this.evt = evt; │ │ │ │ + return true; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * Deconstruct the handler. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + // unregister event listeners │ │ │ │ + this.deactivate(); │ │ │ │ + // eliminate circular references │ │ │ │ + this.control = this.map = null; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Handler" │ │ │ │ +}); │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: createRegularPolygon │ │ │ │ - * Create a regular polygon around a radius. Useful for creating circles │ │ │ │ - * and the like. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * origin - {<OpenLayers.Geometry.Point>} center of polygon. │ │ │ │ - * radius - {Float} distance to vertex, in map units. │ │ │ │ - * sides - {Integer} Number of sides. 20 approximates a circle. │ │ │ │ - * rotation - {Float} original angle of rotation, in degrees. │ │ │ │ + * Constant: OpenLayers.Handler.MOD_NONE │ │ │ │ + * If set as the <keyMask>, <checkModifiers> returns false if any key is down. │ │ │ │ */ │ │ │ │ -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 │ │ │ │ - ====================================================================== */ │ │ │ │ +OpenLayers.Handler.MOD_NONE = 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. */ │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Handler.MOD_SHIFT │ │ │ │ + * If set as the <keyMask>, <checkModifiers> returns false if Shift is down. │ │ │ │ + */ │ │ │ │ +OpenLayers.Handler.MOD_SHIFT = 1; │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Geometry/Collection.js │ │ │ │ - * @requires OpenLayers/Geometry/Polygon.js │ │ │ │ + * Constant: OpenLayers.Handler.MOD_CTRL │ │ │ │ + * If set as the <keyMask>, <checkModifiers> returns false if Ctrl is down. │ │ │ │ */ │ │ │ │ +OpenLayers.Handler.MOD_CTRL = 2; │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Geometry.MultiPolygon │ │ │ │ - * MultiPolygon is a geometry with multiple <OpenLayers.Geometry.Polygon> │ │ │ │ - * components. Create a new instance with the <OpenLayers.Geometry.MultiPolygon> │ │ │ │ - * constructor. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Geometry.Collection> │ │ │ │ + * Constant: OpenLayers.Handler.MOD_ALT │ │ │ │ + * If set as the <keyMask>, <checkModifiers> returns false if Alt is down. │ │ │ │ */ │ │ │ │ -OpenLayers.Geometry.MultiPolygon = OpenLayers.Class( │ │ │ │ - OpenLayers.Geometry.Collection, { │ │ │ │ +OpenLayers.Handler.MOD_ALT = 4; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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"], │ │ │ │ +/** │ │ │ │ + * Constant: OpenLayers.Handler.MOD_META │ │ │ │ + * If set as the <keyMask>, <checkModifiers> returns false if Cmd is down. │ │ │ │ + */ │ │ │ │ +OpenLayers.Handler.MOD_META = 8; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Geometry.MultiPolygon │ │ │ │ - * Create a new MultiPolygon geometry │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * components - {Array(<OpenLayers.Geometry.Polygon>)} An array of polygons │ │ │ │ - * used to generate the MultiPolygon │ │ │ │ - * │ │ │ │ - */ │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.MultiPolygon" │ │ │ │ - }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Format/GeoJSON.js │ │ │ │ + OpenLayers/Handler/Feature.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/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/Handler.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Format.GeoJSON │ │ │ │ - * Read and write GeoJSON. Create a new parser with the │ │ │ │ - * <OpenLayers.Format.GeoJSON> constructor. │ │ │ │ + * Class: OpenLayers.Handler.Feature │ │ │ │ + * Handler to respond to mouse events related to a drawn feature. Callbacks │ │ │ │ + * with the following keys will be notified of the following events │ │ │ │ + * associated with features: click, clickout, over, out, and dblclick. │ │ │ │ + * │ │ │ │ + * This handler stops event propagation for mousedown and mouseup if those │ │ │ │ + * browser events target features that can be selected. │ │ │ │ * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Format.JSON> │ │ │ │ + * - <OpenLayers.Handler> │ │ │ │ */ │ │ │ │ -OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, { │ │ │ │ +OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: ignoreExtraDims │ │ │ │ - * {Boolean} Ignore dimensions higher than 2 when reading geometry │ │ │ │ - * coordinates. │ │ │ │ + * Property: EVENTMAP │ │ │ │ + * {Object} A object mapping the browser events to objects with callback │ │ │ │ + * keys for in and out. │ │ │ │ */ │ │ │ │ - ignoreExtraDims: false, │ │ │ │ + EVENTMAP: { │ │ │ │ + 'click': { │ │ │ │ + 'in': 'click', │ │ │ │ + 'out': 'clickout' │ │ │ │ + }, │ │ │ │ + 'mousemove': { │ │ │ │ + 'in': 'over', │ │ │ │ + 'out': 'out' │ │ │ │ + }, │ │ │ │ + 'dblclick': { │ │ │ │ + 'in': 'dblclick', │ │ │ │ + 'out': null │ │ │ │ + }, │ │ │ │ + 'mousedown': { │ │ │ │ + 'in': null, │ │ │ │ + 'out': null │ │ │ │ + }, │ │ │ │ + 'mouseup': { │ │ │ │ + 'in': null, │ │ │ │ + 'out': null │ │ │ │ + }, │ │ │ │ + 'touchstart': { │ │ │ │ + 'in': 'click', │ │ │ │ + 'out': 'clickout' │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Format.GeoJSON │ │ │ │ - * Create a new parser for GeoJSON. │ │ │ │ + * Property: feature │ │ │ │ + * {<OpenLayers.Feature.Vector>} The last feature that was hovered. │ │ │ │ + */ │ │ │ │ + feature: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: lastFeature │ │ │ │ + * {<OpenLayers.Feature.Vector>} The last feature that was handled. │ │ │ │ + */ │ │ │ │ + lastFeature: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: down │ │ │ │ + * {<OpenLayers.Pixel>} The location of the last mousedown. │ │ │ │ + */ │ │ │ │ + down: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: up │ │ │ │ + * {<OpenLayers.Pixel>} The location of the last mouseup. │ │ │ │ + */ │ │ │ │ + up: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: clickTolerance │ │ │ │ + * {Number} The number of pixels the mouse can move between mousedown │ │ │ │ + * and mouseup for the event to still be considered a click. │ │ │ │ + * Dragging the map should not trigger the click and clickout callbacks │ │ │ │ + * unless the map is moved by less than this tolerance. Defaults to 4. │ │ │ │ + */ │ │ │ │ + clickTolerance: 4, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: geometryTypes │ │ │ │ + * To restrict dragging to a limited set of geometry types, send a list │ │ │ │ + * of strings corresponding to the geometry class names. │ │ │ │ + * │ │ │ │ + * @type Array(String) │ │ │ │ + */ │ │ │ │ + geometryTypes: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: stopClick │ │ │ │ + * {Boolean} If stopClick is set to true, handled clicks do not │ │ │ │ + * propagate to other click listeners. Otherwise, handled clicks │ │ │ │ + * do propagate. Unhandled clicks always propagate, whatever the │ │ │ │ + * value of stopClick. Defaults to true. │ │ │ │ + */ │ │ │ │ + stopClick: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: stopDown │ │ │ │ + * {Boolean} If stopDown is set to true, handled mousedowns do not │ │ │ │ + * propagate to other mousedown listeners. Otherwise, handled │ │ │ │ + * mousedowns do propagate. Unhandled mousedowns always propagate, │ │ │ │ + * whatever the value of stopDown. Defaults to true. │ │ │ │ + */ │ │ │ │ + stopDown: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: stopUp │ │ │ │ + * {Boolean} If stopUp is set to true, handled mouseups do not │ │ │ │ + * propagate to other mouseup listeners. Otherwise, handled mouseups │ │ │ │ + * do propagate. Unhandled mouseups always propagate, whatever the │ │ │ │ + * value of stopUp. Defaults to false. │ │ │ │ + */ │ │ │ │ + stopUp: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Handler.Feature │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object whose properties will be set on │ │ │ │ - * this instance. │ │ │ │ + * control - {<OpenLayers.Control>} │ │ │ │ + * layer - {<OpenLayers.Layer.Vector>} │ │ │ │ + * callbacks - {Object} An object with a 'over' property whos value is │ │ │ │ + * a function to be called when the mouse is over a feature. The │ │ │ │ + * callback should expect to recieve a single argument, the feature. │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ + initialize: function(control, layer, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]); │ │ │ │ + this.layer = layer; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Deserialize a GeoJSON string. │ │ │ │ + * Method: touchstart │ │ │ │ + * Handle touchstart events │ │ │ │ * │ │ │ │ * 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. │ │ │ │ + * evt - {Event} │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Object} The return depends on the value of the type argument. If type │ │ │ │ - * is "FeatureCollection" (the default), the return will be an array │ │ │ │ - * of <OpenLayers.Feature.Vector>. If type is "Geometry", the input json │ │ │ │ - * must represent a single geometry, and the return will be an │ │ │ │ - * <OpenLayers.Geometry>. If type is "Feature", the input json must │ │ │ │ - * represent a single feature, and the return will be an │ │ │ │ - * <OpenLayers.Feature.Vector>. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + touchstart: function(evt) { │ │ │ │ + this.startTouch(); │ │ │ │ + return OpenLayers.Event.isMultiTouch(evt) ? │ │ │ │ + true : this.mousedown(evt); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: isValidType │ │ │ │ - * Check if a GeoJSON object is a valid representative of the given type. │ │ │ │ + * Method: touchmove │ │ │ │ + * Handle touchmove events. We just prevent the browser default behavior, │ │ │ │ + * for Android Webkit not to select text when moving the finger after │ │ │ │ + * selecting a feature. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The object is valid GeoJSON object of the given type. │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + touchmove: function(evt) { │ │ │ │ + OpenLayers.Event.preventDefault(evt); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: mousedown │ │ │ │ + * Handle mouse down. Stop propagation if a feature is targeted by this │ │ │ │ + * event (stops map dragging during feature selection). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + */ │ │ │ │ + mousedown: function(evt) { │ │ │ │ + // Feature selection is only done with a left click. Other handlers may stop the │ │ │ │ + // propagation of left-click mousedown events but not right-click mousedown events. │ │ │ │ + // This mismatch causes problems when comparing the location of the down and up │ │ │ │ + // events in the click function so it is important ignore right-clicks. │ │ │ │ + if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) { │ │ │ │ + this.down = evt.xy; │ │ │ │ } │ │ │ │ - return valid; │ │ │ │ + return this.handle(evt) ? !this.stopDown : true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseFeature │ │ │ │ - * Convert a feature object from GeoJSON into an │ │ │ │ - * <OpenLayers.Feature.Vector>. │ │ │ │ + * Method: mouseup │ │ │ │ + * Handle mouse up. Stop propagation if a feature is targeted by this │ │ │ │ + * event. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + */ │ │ │ │ + mouseup: function(evt) { │ │ │ │ + this.up = evt.xy; │ │ │ │ + return this.handle(evt) ? !this.stopUp : true; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: click │ │ │ │ + * Handle click. Call the "click" callback if click on a feature, │ │ │ │ + * or the "clickout" callback if click outside any feature. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} │ │ │ │ + */ │ │ │ │ + click: function(evt) { │ │ │ │ + return this.handle(evt) ? !this.stopClick : true; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: mousemove │ │ │ │ + * Handle mouse moves. Call the "over" callback if moving in to a feature, │ │ │ │ + * or the "out" callback if moving out of a feature. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * obj - {Object} An object created from a GeoJSON object │ │ │ │ + * evt - {Event} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Feature.Vector>} A feature. │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + mousemove: function(evt) { │ │ │ │ + if (!this.callbacks['over'] && !this.callbacks['out']) { │ │ │ │ + return true; │ │ │ │ } │ │ │ │ - return feature; │ │ │ │ + this.handle(evt); │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: parseGeometry │ │ │ │ - * Convert a geometry object from GeoJSON into an <OpenLayers.Geometry>. │ │ │ │ + * Method: dblclick │ │ │ │ + * Handle dblclick. Call the "dblclick" callback if dblclick on a feature. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * obj - {Object} An object created from a GeoJSON object │ │ │ │ + * evt - {Event} │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} A geometry. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + dblclick: function(evt) { │ │ │ │ + return !this.handle(evt); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: parseCoords │ │ │ │ - * Object with properties corresponding to the GeoJSON geometry types. │ │ │ │ - * Property values are functions that do the actual parsing. │ │ │ │ + * Method: geometryTypeMatches │ │ │ │ + * Return true if the geometry type of the passed feature matches │ │ │ │ + * one of the geometry types in the geometryTypes array. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Vector.Feature>} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - parseCoords: { │ │ │ │ - /** │ │ │ │ - * Method: parseCoords.point │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ - * <OpenLayers.Geometry>. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * array - {Object} The coordinates array from the GeoJSON fragment. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} A geometry. │ │ │ │ - */ │ │ │ │ - "point": function(array) { │ │ │ │ - if (this.ignoreExtraDims == false && │ │ │ │ - array.length != 2) { │ │ │ │ - throw "Only 2D points are supported: " + array; │ │ │ │ - } │ │ │ │ - return new OpenLayers.Geometry.Point(array[0], array[1]); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: parseCoords.multipoint │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ - * <OpenLayers.Geometry>. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * array - {Object} The coordinates array from the GeoJSON fragment. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} A geometry. │ │ │ │ - */ │ │ │ │ - "multipoint": function(array) { │ │ │ │ - var points = []; │ │ │ │ - var p = null; │ │ │ │ - for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ - try { │ │ │ │ - p = this.parseCoords["point"].apply(this, [array[i]]); │ │ │ │ - } catch (err) { │ │ │ │ - throw err; │ │ │ │ - } │ │ │ │ - points.push(p); │ │ │ │ - } │ │ │ │ - return new OpenLayers.Geometry.MultiPoint(points); │ │ │ │ - }, │ │ │ │ + geometryTypeMatches: function(feature) { │ │ │ │ + return this.geometryTypes == null || │ │ │ │ + OpenLayers.Util.indexOf(this.geometryTypes, │ │ │ │ + feature.geometry.CLASS_NAME) > -1; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: parseCoords.linestring │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ - * <OpenLayers.Geometry>. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * array - {Object} The coordinates array from the GeoJSON fragment. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} A geometry. │ │ │ │ - */ │ │ │ │ - "linestring": function(array) { │ │ │ │ - var points = []; │ │ │ │ - var p = null; │ │ │ │ - for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ - try { │ │ │ │ - p = this.parseCoords["point"].apply(this, [array[i]]); │ │ │ │ - } catch (err) { │ │ │ │ - throw err; │ │ │ │ - } │ │ │ │ - points.push(p); │ │ │ │ + /** │ │ │ │ + * Method: handle │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The event occurred over a relevant feature. │ │ │ │ + */ │ │ │ │ + handle: function(evt) { │ │ │ │ + if (this.feature && !this.feature.layer) { │ │ │ │ + // feature has been destroyed │ │ │ │ + this.feature = null; │ │ │ │ + } │ │ │ │ + var type = evt.type; │ │ │ │ + var handled = false; │ │ │ │ + var previouslyIn = !!(this.feature); // previously in a feature │ │ │ │ + var click = (type == "click" || type == "dblclick" || type == "touchstart"); │ │ │ │ + this.feature = this.layer.getFeatureFromEvent(evt); │ │ │ │ + if (this.feature && !this.feature.layer) { │ │ │ │ + // feature has been destroyed │ │ │ │ + this.feature = null; │ │ │ │ + } │ │ │ │ + if (this.lastFeature && !this.lastFeature.layer) { │ │ │ │ + // last feature has been destroyed │ │ │ │ + this.lastFeature = null; │ │ │ │ + } │ │ │ │ + if (this.feature) { │ │ │ │ + if (type === "touchstart") { │ │ │ │ + // stop the event to prevent Android Webkit from │ │ │ │ + // "flashing" the map div │ │ │ │ + OpenLayers.Event.preventDefault(evt); │ │ │ │ } │ │ │ │ - return new OpenLayers.Geometry.LineString(points); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: parseCoords.multilinestring │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ - * <OpenLayers.Geometry>. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * array - {Object} The coordinates array from the GeoJSON fragment. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} A geometry. │ │ │ │ - */ │ │ │ │ - "multilinestring": function(array) { │ │ │ │ - var lines = []; │ │ │ │ - var l = null; │ │ │ │ - for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ - try { │ │ │ │ - l = this.parseCoords["linestring"].apply(this, [array[i]]); │ │ │ │ - } catch (err) { │ │ │ │ - throw err; │ │ │ │ + var inNew = (this.feature != this.lastFeature); │ │ │ │ + if (this.geometryTypeMatches(this.feature)) { │ │ │ │ + // in to a feature │ │ │ │ + if (previouslyIn && inNew) { │ │ │ │ + // out of last feature and in to another │ │ │ │ + if (this.lastFeature) { │ │ │ │ + this.triggerCallback(type, 'out', [this.lastFeature]); │ │ │ │ + } │ │ │ │ + this.triggerCallback(type, 'in', [this.feature]); │ │ │ │ + } else if (!previouslyIn || click) { │ │ │ │ + // in feature for the first time │ │ │ │ + this.triggerCallback(type, 'in', [this.feature]); │ │ │ │ } │ │ │ │ - lines.push(l); │ │ │ │ - } │ │ │ │ - return new OpenLayers.Geometry.MultiLineString(lines); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: parseCoords.polygon │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ - * <OpenLayers.Geometry>. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} A geometry. │ │ │ │ - */ │ │ │ │ - "polygon": function(array) { │ │ │ │ - var rings = []; │ │ │ │ - var r, l; │ │ │ │ - for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ - try { │ │ │ │ - l = this.parseCoords["linestring"].apply(this, [array[i]]); │ │ │ │ - } catch (err) { │ │ │ │ - throw err; │ │ │ │ + this.lastFeature = this.feature; │ │ │ │ + handled = true; │ │ │ │ + } else { │ │ │ │ + // not in to a feature │ │ │ │ + if (this.lastFeature && (previouslyIn && inNew || click)) { │ │ │ │ + // out of last feature for the first time │ │ │ │ + this.triggerCallback(type, 'out', [this.lastFeature]); │ │ │ │ } │ │ │ │ - r = new OpenLayers.Geometry.LinearRing(l.components); │ │ │ │ - rings.push(r); │ │ │ │ + // next time the mouse goes in a feature whose geometry type │ │ │ │ + // doesn't match we don't want to call the 'out' callback │ │ │ │ + // again, so let's set this.feature to null so that │ │ │ │ + // previouslyIn will evaluate to false the next time │ │ │ │ + // we enter handle. Yes, a bit hackish... │ │ │ │ + this.feature = null; │ │ │ │ } │ │ │ │ - return new OpenLayers.Geometry.Polygon(rings); │ │ │ │ - }, │ │ │ │ + } else if (this.lastFeature && (previouslyIn || click)) { │ │ │ │ + this.triggerCallback(type, 'out', [this.lastFeature]); │ │ │ │ + } │ │ │ │ + return handled; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: parseCoords.multipolygon │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ - * <OpenLayers.Geometry>. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * array - {Object} The coordinates array from the GeoJSON fragment. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} A geometry. │ │ │ │ - */ │ │ │ │ - "multipolygon": function(array) { │ │ │ │ - var polys = []; │ │ │ │ - var p = null; │ │ │ │ - for (var i = 0, len = array.length; i < len; ++i) { │ │ │ │ - try { │ │ │ │ - p = this.parseCoords["polygon"].apply(this, [array[i]]); │ │ │ │ - } catch (err) { │ │ │ │ - throw err; │ │ │ │ + /** │ │ │ │ + * Method: triggerCallback │ │ │ │ + * Call the callback keyed in the event map with the supplied arguments. │ │ │ │ + * For click and clickout, the <clickTolerance> is checked first. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * type - {String} │ │ │ │ + */ │ │ │ │ + triggerCallback: function(type, mode, args) { │ │ │ │ + var key = this.EVENTMAP[type][mode]; │ │ │ │ + if (key) { │ │ │ │ + if (type == 'click' && this.up && this.down) { │ │ │ │ + // for click/clickout, only trigger callback if tolerance is met │ │ │ │ + var dpx = Math.sqrt( │ │ │ │ + Math.pow(this.up.x - this.down.x, 2) + │ │ │ │ + Math.pow(this.up.y - this.down.y, 2) │ │ │ │ + ); │ │ │ │ + if (dpx <= this.clickTolerance) { │ │ │ │ + this.callback(key, args); │ │ │ │ } │ │ │ │ - polys.push(p); │ │ │ │ - } │ │ │ │ - return new OpenLayers.Geometry.MultiPolygon(polys); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: parseCoords.box │ │ │ │ - * Convert a coordinate array from GeoJSON into an │ │ │ │ - * <OpenLayers.Geometry>. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * array - {Object} The coordinates array from the GeoJSON fragment. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Geometry>} A geometry. │ │ │ │ - */ │ │ │ │ - "box": function(array) { │ │ │ │ - if (array.length != 2) { │ │ │ │ - throw "GeoJSON box coordinates must have 2 elements"; │ │ │ │ + // we're done with this set of events now: clear the cached │ │ │ │ + // positions so we can't trip over them later (this can occur │ │ │ │ + // if one of the up/down events gets eaten before it gets to us │ │ │ │ + // but we still get the click) │ │ │ │ + this.up = this.down = null; │ │ │ │ + } else { │ │ │ │ + this.callback(key, args); │ │ │ │ } │ │ │ │ - return new OpenLayers.Geometry.Polygon([ │ │ │ │ - new OpenLayers.Geometry.LinearRing([ │ │ │ │ - new OpenLayers.Geometry.Point(array[0][0], array[0][1]), │ │ │ │ - new OpenLayers.Geometry.Point(array[1][0], array[0][1]), │ │ │ │ - new OpenLayers.Geometry.Point(array[1][0], array[1][1]), │ │ │ │ - new OpenLayers.Geometry.Point(array[0][0], array[1][1]), │ │ │ │ - new OpenLayers.Geometry.Point(array[0][0], array[0][1]) │ │ │ │ - ]) │ │ │ │ - ]); │ │ │ │ } │ │ │ │ - │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: write │ │ │ │ - * Serialize a feature, geometry, array of features into a GeoJSON string. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * obj - {Object} An <OpenLayers.Feature.Vector>, <OpenLayers.Geometry>, │ │ │ │ - * or an array of features. │ │ │ │ - * pretty - {Boolean} Structure the output with newlines and indentation. │ │ │ │ - * Default is false. │ │ │ │ + * Method: activate │ │ │ │ + * Turn on the handler. Returns false if the handler was already active. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {String} The GeoJSON string representation of the input geometry, │ │ │ │ - * features, or array of features. │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + activate: function() { │ │ │ │ + var activated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.moveLayerToTop(); │ │ │ │ + this.map.events.on({ │ │ │ │ + "removelayer": this.handleMapEvents, │ │ │ │ + "changelayer": this.handleMapEvents, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + activated = true; │ │ │ │ } │ │ │ │ - return OpenLayers.Format.JSON.prototype.write.apply(this, │ │ │ │ - [geojson, pretty]); │ │ │ │ + return activated; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createCRSObject │ │ │ │ - * Create the CRS object for an object. │ │ │ │ + * Method: deactivate │ │ │ │ + * Turn off the handler. Returns false if the handler was already active. │ │ │ │ * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.moveLayerBack(); │ │ │ │ + this.feature = null; │ │ │ │ + this.lastFeature = null; │ │ │ │ + this.down = null; │ │ │ │ + this.up = null; │ │ │ │ + this.map.events.un({ │ │ │ │ + "removelayer": this.handleMapEvents, │ │ │ │ + "changelayer": this.handleMapEvents, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + deactivated = true; │ │ │ │ + } │ │ │ │ + return deactivated; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: handleMapEvents │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * object - {<OpenLayers.Feature.Vector>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object which can be assigned to the crs property │ │ │ │ - * of a GeoJSON object. │ │ │ │ + * evt - {Object} │ │ │ │ */ │ │ │ │ - createCRSObject: function(object) { │ │ │ │ - var proj = object.layer.projection.toString(); │ │ │ │ - var crs = {}; │ │ │ │ - if (proj.match(/epsg:/i)) { │ │ │ │ - var code = parseInt(proj.substring(proj.indexOf(":") + 1)); │ │ │ │ - if (code == 4326) { │ │ │ │ - crs = { │ │ │ │ - "type": "name", │ │ │ │ - "properties": { │ │ │ │ - "name": "urn:ogc:def:crs:OGC:1.3:CRS84" │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - } else { │ │ │ │ - crs = { │ │ │ │ - "type": "name", │ │ │ │ - "properties": { │ │ │ │ - "name": "EPSG:" + code │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - } │ │ │ │ + handleMapEvents: function(evt) { │ │ │ │ + if (evt.type == "removelayer" || evt.property == "order") { │ │ │ │ + this.moveLayerToTop(); │ │ │ │ } │ │ │ │ - return crs; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: extract │ │ │ │ - * Object with properties corresponding to the GeoJSON types. │ │ │ │ - * Property values are functions that do the actual value extraction. │ │ │ │ + * Method: moveLayerToTop │ │ │ │ + * Moves the layer for this handler to the top, so mouse events can reach │ │ │ │ + * it. │ │ │ │ */ │ │ │ │ - extract: { │ │ │ │ - /** │ │ │ │ - * Method: extract.feature │ │ │ │ - * Return a partial GeoJSON object representing a single feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object representing the point. │ │ │ │ - */ │ │ │ │ - 'feature': function(feature) { │ │ │ │ - var geom = this.extract.geometry.apply(this, [feature.geometry]); │ │ │ │ - var json = { │ │ │ │ - "type": "Feature", │ │ │ │ - "properties": feature.attributes, │ │ │ │ - "geometry": geom │ │ │ │ - }; │ │ │ │ - if (feature.fid != null) { │ │ │ │ - json.id = feature.fid; │ │ │ │ - } │ │ │ │ - return json; │ │ │ │ - }, │ │ │ │ + moveLayerToTop: function() { │ │ │ │ + var index = Math.max(this.map.Z_INDEX_BASE['Feature'] - 1, │ │ │ │ + this.layer.getZIndex()) + 1; │ │ │ │ + this.layer.setZIndex(index); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: extract.geometry │ │ │ │ - * Return a GeoJSON object representing a single geometry. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * geometry - {<OpenLayers.Geometry>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Object} An object representing the geometry. │ │ │ │ - */ │ │ │ │ - 'geometry': function(geometry) { │ │ │ │ - if (geometry == null) { │ │ │ │ - return null; │ │ │ │ - } │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - geometry = geometry.clone(); │ │ │ │ - geometry.transform(this.internalProjection, │ │ │ │ - this.externalProjection); │ │ │ │ - } │ │ │ │ - var geometryType = geometry.CLASS_NAME.split('.')[2]; │ │ │ │ - var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]); │ │ │ │ - var json; │ │ │ │ - if (geometryType == "Collection") { │ │ │ │ - json = { │ │ │ │ - "type": "GeometryCollection", │ │ │ │ - "geometries": data │ │ │ │ - }; │ │ │ │ - } else { │ │ │ │ - json = { │ │ │ │ - "type": geometryType, │ │ │ │ - "coordinates": data │ │ │ │ - }; │ │ │ │ - } │ │ │ │ + }, │ │ │ │ │ │ │ │ - return json; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: moveLayerBack │ │ │ │ + * Moves the layer back to the position determined by the map's layers │ │ │ │ + * array. │ │ │ │ + */ │ │ │ │ + moveLayerBack: function() { │ │ │ │ + var index = this.layer.getZIndex() - 1; │ │ │ │ + if (index >= this.map.Z_INDEX_BASE['Feature']) { │ │ │ │ + this.layer.setZIndex(index); │ │ │ │ + } else { │ │ │ │ + this.map.setLayerZIndex(this.layer, │ │ │ │ + this.map.getLayerIndex(this.layer)); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: extract.point │ │ │ │ - * Return an array of coordinates from a point. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * point - {<OpenLayers.Geometry.Point>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} An array of coordinates representing the point. │ │ │ │ - */ │ │ │ │ - 'point': function(point) { │ │ │ │ - return [point.x, point.y]; │ │ │ │ - }, │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Feature" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Layer/Vector/RootContainer.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: extract.multipoint │ │ │ │ - * Return an array of point coordinates from a multipoint. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * multipoint - {<OpenLayers.Geometry.MultiPoint>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} An array of point coordinate arrays representing │ │ │ │ - * the multipoint. │ │ │ │ - */ │ │ │ │ - 'multipoint': function(multipoint) { │ │ │ │ - var array = []; │ │ │ │ - for (var i = 0, len = multipoint.components.length; i < len; ++i) { │ │ │ │ - array.push(this.extract.point.apply(this, [multipoint.components[i]])); │ │ │ │ - } │ │ │ │ - return array; │ │ │ │ - }, │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of 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: extract.linestring │ │ │ │ - * Return an array of coordinate arrays from a linestring. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * linestring - {<OpenLayers.Geometry.LineString>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} An array of coordinate arrays representing │ │ │ │ - * the linestring. │ │ │ │ - */ │ │ │ │ - 'linestring': function(linestring) { │ │ │ │ - var array = []; │ │ │ │ - for (var i = 0, len = linestring.components.length; i < len; ++i) { │ │ │ │ - array.push(this.extract.point.apply(this, [linestring.components[i]])); │ │ │ │ - } │ │ │ │ - return array; │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Layer/Vector.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: extract.multilinestring │ │ │ │ - * Return an array of linestring arrays from a linestring. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * multilinestring - {<OpenLayers.Geometry.MultiLineString>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} An array of linestring arrays representing │ │ │ │ - * the multilinestring. │ │ │ │ - */ │ │ │ │ - 'multilinestring': function(multilinestring) { │ │ │ │ - var array = []; │ │ │ │ - for (var i = 0, len = multilinestring.components.length; i < len; ++i) { │ │ │ │ - array.push(this.extract.linestring.apply(this, [multilinestring.components[i]])); │ │ │ │ - } │ │ │ │ - return array; │ │ │ │ - }, │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Layer.Vector.RootContainer │ │ │ │ + * A special layer type to combine multiple vector layers inside a single │ │ │ │ + * renderer root container. This class is not supposed to be instantiated │ │ │ │ + * from user space, it is a helper class for controls that require event │ │ │ │ + * processing for multiple vector layers. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Layer.Vector> │ │ │ │ + */ │ │ │ │ +OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: extract.polygon │ │ │ │ - * Return an array of linear ring arrays from a polygon. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * polygon - {<OpenLayers.Geometry.Polygon>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} An array of linear ring arrays representing the polygon. │ │ │ │ - */ │ │ │ │ - 'polygon': function(polygon) { │ │ │ │ - var array = []; │ │ │ │ - for (var i = 0, len = polygon.components.length; i < len; ++i) { │ │ │ │ - array.push(this.extract.linestring.apply(this, [polygon.components[i]])); │ │ │ │ - } │ │ │ │ - return array; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: displayInLayerSwitcher │ │ │ │ + * Set to false for this layer type │ │ │ │ + */ │ │ │ │ + displayInLayerSwitcher: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: extract.multipolygon │ │ │ │ - * Return an array of polygon arrays from a multipolygon. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * multipolygon - {<OpenLayers.Geometry.MultiPolygon>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} An array of polygon arrays representing │ │ │ │ - * the multipolygon │ │ │ │ - */ │ │ │ │ - 'multipolygon': function(multipolygon) { │ │ │ │ - var array = []; │ │ │ │ - for (var i = 0, len = multipolygon.components.length; i < len; ++i) { │ │ │ │ - array.push(this.extract.polygon.apply(this, [multipolygon.components[i]])); │ │ │ │ - } │ │ │ │ - return array; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * APIProperty: layers │ │ │ │ + * Layers that are attached to this container. Required config option. │ │ │ │ + */ │ │ │ │ + layers: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: extract.collection │ │ │ │ - * Return an array of geometries from a geometry collection. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * collection - {<OpenLayers.Geometry.Collection>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Array} An array of geometry objects representing the geometry │ │ │ │ - * collection. │ │ │ │ - */ │ │ │ │ - 'collection': function(collection) { │ │ │ │ - var len = collection.components.length; │ │ │ │ - var array = new Array(len); │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ - array[i] = this.extract.geometry.apply( │ │ │ │ - this, [collection.components[i]] │ │ │ │ - ); │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Layer.Vector.RootContainer │ │ │ │ + * Create a new root container for multiple vector layer. This constructor │ │ │ │ + * is not supposed to be used from user space, it is only to be used by │ │ │ │ + * controls that need feature selection across multiple vector layers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * name - {String} A name for the layer │ │ │ │ + * options - {Object} Optional object with non-default properties to set on │ │ │ │ + * the layer. │ │ │ │ + * │ │ │ │ + * Required options properties: │ │ │ │ + * layers - {Array(<OpenLayers.Layer.Vector>)} The layers managed by this │ │ │ │ + * container │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Layer.Vector.RootContainer>} A new vector layer root │ │ │ │ + * container │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: display │ │ │ │ + */ │ │ │ │ + display: function() {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: getFeatureFromEvent │ │ │ │ + * walk through the layers to find the feature returned by the event │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} event object with a feature property │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {<OpenLayers.Feature.Vector>} │ │ │ │ + */ │ │ │ │ + getFeatureFromEvent: function(evt) { │ │ │ │ + var layers = this.layers; │ │ │ │ + var feature; │ │ │ │ + for (var i = 0; i < layers.length; i++) { │ │ │ │ + feature = layers[i].getFeatureFromEvent(evt); │ │ │ │ + if (feature) { │ │ │ │ + return feature; │ │ │ │ } │ │ │ │ - return array; │ │ │ │ } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); │ │ │ │ + this.collectRoots(); │ │ │ │ + map.events.register("changelayer", this, this.handleChangeLayer); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: removeMap │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + removeMap: function(map) { │ │ │ │ + map.events.unregister("changelayer", this, this.handleChangeLayer); │ │ │ │ + this.resetRoots(); │ │ │ │ + OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Method: collectRoots │ │ │ │ + * Collects the root nodes of all layers this control is configured with │ │ │ │ + * and moveswien the nodes to this control's layer │ │ │ │ + */ │ │ │ │ + collectRoots: function() { │ │ │ │ + var layer; │ │ │ │ + // walk through all map layers, because we want to keep the order │ │ │ │ + for (var i = 0; i < this.map.layers.length; ++i) { │ │ │ │ + layer = this.map.layers[i]; │ │ │ │ + if (OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ + layer.renderer.moveRoot(this.renderer); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ + /** │ │ │ │ + * Method: resetRoots │ │ │ │ + * Resets the root nodes back into the layers they belong to. │ │ │ │ + */ │ │ │ │ + resetRoots: function() { │ │ │ │ + var layer; │ │ │ │ + for (var i = 0; i < this.layers.length; ++i) { │ │ │ │ + layer = this.layers[i]; │ │ │ │ + if (this.renderer && layer.renderer.getRenderLayerId() == this.id) { │ │ │ │ + this.renderer.moveRoot(layer.renderer); │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Format.GeoJSON" │ │ │ │ + /** │ │ │ │ + * Method: handleChangeLayer │ │ │ │ + * Event handler for the map's changelayer event. We need to rebuild │ │ │ │ + * this container's layer dom if order of one of its layers changes. │ │ │ │ + * This handler is added with the setMap method, and removed with the │ │ │ │ + * removeMap method. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} │ │ │ │ + */ │ │ │ │ + handleChangeLayer: function(evt) { │ │ │ │ + var layer = evt.layer; │ │ │ │ + if (evt.property == "order" && │ │ │ │ + OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ + this.resetRoots(); │ │ │ │ + this.collectRoots(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Protocol.js │ │ │ │ + OpenLayers/Control/SelectFeature.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ + │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Feature/Vector.js │ │ │ │ + * @requires OpenLayers/Handler/Feature.js │ │ │ │ + * @requires OpenLayers/Layer/Vector/RootContainer.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Protocol │ │ │ │ - * Abstract vector layer protocol class. Not to be instantiated directly. Use │ │ │ │ - * one of the protocol subclasses instead. │ │ │ │ + * Class: OpenLayers.Control.SelectFeature │ │ │ │ + * The SelectFeature control selects vector features from a given layer on │ │ │ │ + * click or hover. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol = OpenLayers.Class({ │ │ │ │ +OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: events │ │ │ │ + * {<OpenLayers.Events>} Events instance for listeners and triggering │ │ │ │ + * control specific events. │ │ │ │ + * │ │ │ │ + * Register a listener for a particular event with the following syntax: │ │ │ │ + * (code) │ │ │ │ + * control.events.register(type, obj, listener); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Supported event types (in addition to those from <OpenLayers.Control.events>): │ │ │ │ + * beforefeaturehighlighted - Triggered before a feature is highlighted │ │ │ │ + * featurehighlighted - Triggered when a feature is highlighted │ │ │ │ + * featureunhighlighted - Triggered when a feature is unhighlighted │ │ │ │ + * boxselectionstart - Triggered before box selection starts │ │ │ │ + * boxselectionend - Triggered after box selection ends │ │ │ │ + */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: format │ │ │ │ - * {<OpenLayers.Format>} The format used by this protocol. │ │ │ │ + * Property: multipleKey │ │ │ │ + * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets │ │ │ │ + * the <multiple> property to true. Default is null. │ │ │ │ */ │ │ │ │ - format: null, │ │ │ │ + multipleKey: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: options │ │ │ │ - * {Object} Any options sent to the constructor. │ │ │ │ + * Property: toggleKey │ │ │ │ + * {String} An event modifier ('altKey' or 'shiftKey') that temporarily sets │ │ │ │ + * the <toggle> property to true. Default is null. │ │ │ │ */ │ │ │ │ - options: null, │ │ │ │ + toggleKey: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: autoDestroy │ │ │ │ - * {Boolean} The creator of the protocol can set autoDestroy to false │ │ │ │ - * to fully control when the protocol is destroyed. Defaults to │ │ │ │ - * true. │ │ │ │ + * APIProperty: multiple │ │ │ │ + * {Boolean} Allow selection of multiple geometries. Default is false. │ │ │ │ */ │ │ │ │ - autoDestroy: true, │ │ │ │ + multiple: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: defaultFilter │ │ │ │ - * {<OpenLayers.Filter>} Optional default filter to read requests │ │ │ │ + * APIProperty: clickout │ │ │ │ + * {Boolean} Unselect features when clicking outside any feature. │ │ │ │ + * Default is true. │ │ │ │ */ │ │ │ │ - defaultFilter: null, │ │ │ │ + clickout: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Protocol │ │ │ │ - * Abstract class for vector protocols. Create instances of a subclass. │ │ │ │ + * APIProperty: toggle │ │ │ │ + * {Boolean} Unselect a selected feature on click. Default is false. Only │ │ │ │ + * has meaning if hover is false. │ │ │ │ + */ │ │ │ │ + toggle: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: hover │ │ │ │ + * {Boolean} Select on mouse over and deselect on mouse out. If true, this │ │ │ │ + * ignores clicks and only listens to mouse moves. │ │ │ │ + */ │ │ │ │ + hover: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: highlightOnly │ │ │ │ + * {Boolean} If true do not actually select features (that is place them in │ │ │ │ + * the layer's selected features array), just highlight them. This property │ │ │ │ + * has no effect if hover is false. Defaults to false. │ │ │ │ + */ │ │ │ │ + highlightOnly: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: box │ │ │ │ + * {Boolean} Allow feature selection by drawing a box. │ │ │ │ + */ │ │ │ │ + box: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: onBeforeSelect │ │ │ │ + * {Function} Optional function to be called before a feature is selected. │ │ │ │ + * The function should expect to be called with a feature. │ │ │ │ + */ │ │ │ │ + onBeforeSelect: function() {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: onSelect │ │ │ │ + * {Function} Optional function to be called when a feature is selected. │ │ │ │ + * The function should expect to be called with a feature. │ │ │ │ + */ │ │ │ │ + onSelect: function() {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: onUnselect │ │ │ │ + * {Function} Optional function to be called when a feature is unselected. │ │ │ │ + * The function should expect to be called with a feature. │ │ │ │ + */ │ │ │ │ + onUnselect: function() {}, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: scope │ │ │ │ + * {Object} The scope to use with the onBeforeSelect, onSelect, onUnselect │ │ │ │ + * callbacks. If null the scope will be this control. │ │ │ │ + */ │ │ │ │ + scope: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: geometryTypes │ │ │ │ + * {Array(String)} To restrict selecting to a limited set of geometry types, │ │ │ │ + * send a list of strings corresponding to the geometry class names. │ │ │ │ + */ │ │ │ │ + geometryTypes: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: layer │ │ │ │ + * {<OpenLayers.Layer.Vector>} The vector layer with a common renderer │ │ │ │ + * root for all layers this control is configured with (if an array of │ │ │ │ + * layers was passed to the constructor), or the vector layer the control │ │ │ │ + * was configured with (if a single layer was passed to the constructor). │ │ │ │ + */ │ │ │ │ + layer: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: layers │ │ │ │ + * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on, │ │ │ │ + * or null if the control was configured with a single layer │ │ │ │ + */ │ │ │ │ + layers: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: callbacks │ │ │ │ + * {Object} The functions that are sent to the handlers.feature for callback │ │ │ │ + */ │ │ │ │ + callbacks: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: selectStyle │ │ │ │ + * {Object} Hash of styles │ │ │ │ + */ │ │ │ │ + selectStyle: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: renderIntent │ │ │ │ + * {String} key used to retrieve the select style from the layer's │ │ │ │ + * style map. │ │ │ │ + */ │ │ │ │ + renderIntent: "select", │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: handlers │ │ │ │ + * {Object} Object with references to multiple <OpenLayers.Handler> │ │ │ │ + * instances. │ │ │ │ + */ │ │ │ │ + handlers: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.SelectFeature │ │ │ │ + * Create a new control for selecting features. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. The │ │ │ │ + * layer(s) this control will select features from. │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.options = options; │ │ │ │ + initialize: function(layers, options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + │ │ │ │ + if (this.scope === null) { │ │ │ │ + this.scope = this; │ │ │ │ + } │ │ │ │ + this.initLayer(layers); │ │ │ │ + var callbacks = { │ │ │ │ + click: this.clickFeature, │ │ │ │ + clickout: this.clickoutFeature │ │ │ │ + }; │ │ │ │ + if (this.hover) { │ │ │ │ + callbacks.over = this.overFeature; │ │ │ │ + callbacks.out = this.outFeature; │ │ │ │ + } │ │ │ │ + │ │ │ │ + this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); │ │ │ │ + this.handlers = { │ │ │ │ + feature: new OpenLayers.Handler.Feature( │ │ │ │ + this, this.layer, this.callbacks, { │ │ │ │ + geometryTypes: this.geometryTypes │ │ │ │ + } │ │ │ │ + ) │ │ │ │ + }; │ │ │ │ + │ │ │ │ + if (this.box) { │ │ │ │ + this.handlers.box = new OpenLayers.Handler.Box( │ │ │ │ + this, { │ │ │ │ + done: this.selectBox │ │ │ │ + }, { │ │ │ │ + boxDivClassName: "olHandlerBoxSelectFeature" │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: mergeWithDefaultFilter │ │ │ │ - * Merge filter passed to the read method with the default one │ │ │ │ + * Method: initLayer │ │ │ │ + * Assign the layer property. If layers is an array, we need to use │ │ │ │ + * a RootContainer. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * filter - {<OpenLayers.Filter>} │ │ │ │ + * layers - {<OpenLayers.Layer.Vector>}, or an array of vector layers. │ │ │ │ */ │ │ │ │ - mergeWithDefaultFilter: function(filter) { │ │ │ │ - var merged; │ │ │ │ - if (filter && this.defaultFilter) { │ │ │ │ - merged = new OpenLayers.Filter.Logical({ │ │ │ │ - type: OpenLayers.Filter.Logical.AND, │ │ │ │ - filters: [this.defaultFilter, filter] │ │ │ │ - }); │ │ │ │ + initLayer: function(layers) { │ │ │ │ + if (OpenLayers.Util.isArray(layers)) { │ │ │ │ + this.layers = layers; │ │ │ │ + this.layer = new OpenLayers.Layer.Vector.RootContainer( │ │ │ │ + this.id + "_container", { │ │ │ │ + layers: layers │ │ │ │ + } │ │ │ │ + ); │ │ │ │ } else { │ │ │ │ - merged = filter || this.defaultFilter || undefined; │ │ │ │ + this.layer = layers; │ │ │ │ } │ │ │ │ - return merged; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Clean up the protocol. │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ destroy: function() { │ │ │ │ - this.options = null; │ │ │ │ - this.format = null; │ │ │ │ + if (this.active && this.layers) { │ │ │ │ + this.map.removeLayer(this.layer); │ │ │ │ + } │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + if (this.layers) { │ │ │ │ + this.layer.destroy(); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Construct a request for reading new features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ - * │ │ │ │ + * Method: activate │ │ │ │ + * Activates the control. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ - * object, the same object will be passed to the callback function passed │ │ │ │ - * if one exists in the options object. │ │ │ │ + * {Boolean} The control was effectively activated. │ │ │ │ */ │ │ │ │ - read: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - options.filter = this.mergeWithDefaultFilter(options.filter); │ │ │ │ + activate: function() { │ │ │ │ + if (!this.active) { │ │ │ │ + if (this.layers) { │ │ │ │ + this.map.addLayer(this.layer); │ │ │ │ + } │ │ │ │ + this.handlers.feature.activate(); │ │ │ │ + if (this.box && this.handlers.box) { │ │ │ │ + this.handlers.box.activate(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return OpenLayers.Control.prototype.activate.apply( │ │ │ │ + this, arguments │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * APIMethod: create │ │ │ │ - * Construct a request for writing newly created features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ - * {<OpenLayers.Feature.Vector>} │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ - * │ │ │ │ + * Method: deactivate │ │ │ │ + * Deactivates the control. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ - * object, the same object will be passed to the callback function passed │ │ │ │ - * if one exists in the options object. │ │ │ │ + * {Boolean} The control was effectively deactivated. │ │ │ │ */ │ │ │ │ - create: function() {}, │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + this.handlers.feature.deactivate(); │ │ │ │ + if (this.handlers.box) { │ │ │ │ + this.handlers.box.deactivate(); │ │ │ │ + } │ │ │ │ + if (this.layers) { │ │ │ │ + this.map.removeLayer(this.layer); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return OpenLayers.Control.prototype.deactivate.apply( │ │ │ │ + this, arguments │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: update │ │ │ │ - * Construct a request updating modified features. │ │ │ │ + * Method: unselectAll │ │ │ │ + * Unselect all selected features. To unselect all except for a single │ │ │ │ + * feature, set the options.except property to the feature. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * features - {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ - * {<OpenLayers.Feature.Vector>} │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ - * object, the same object will be passed to the callback function passed │ │ │ │ - * if one exists in the options object. │ │ │ │ + * options - {Object} Optional configuration object. │ │ │ │ */ │ │ │ │ - update: function() {}, │ │ │ │ + unselectAll: function(options) { │ │ │ │ + // we'll want an option to supress notification here │ │ │ │ + var layers = this.layers || [this.layer], │ │ │ │ + layer, feature, l, numExcept; │ │ │ │ + for (l = 0; l < layers.length; ++l) { │ │ │ │ + layer = layers[l]; │ │ │ │ + numExcept = 0; │ │ │ │ + //layer.selectedFeatures is null when layer is destroyed and │ │ │ │ + //one of it's preremovelayer listener calls setLayer │ │ │ │ + //with another layer on this control │ │ │ │ + if (layer.selectedFeatures != null) { │ │ │ │ + while (layer.selectedFeatures.length > numExcept) { │ │ │ │ + feature = layer.selectedFeatures[numExcept]; │ │ │ │ + if (!options || options.except != feature) { │ │ │ │ + this.unselect(feature); │ │ │ │ + } else { │ │ │ │ + ++numExcept; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: delete │ │ │ │ - * Construct a request deleting a removed feature. │ │ │ │ + * Method: clickFeature │ │ │ │ + * Called on click in a feature │ │ │ │ + * Only responds if this.hover is false. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ + */ │ │ │ │ + clickFeature: function(feature) { │ │ │ │ + if (!this.hover) { │ │ │ │ + var selected = (OpenLayers.Util.indexOf( │ │ │ │ + feature.layer.selectedFeatures, feature) > -1); │ │ │ │ + if (selected) { │ │ │ │ + if (this.toggleSelect()) { │ │ │ │ + this.unselect(feature); │ │ │ │ + } else if (!this.multipleSelect()) { │ │ │ │ + this.unselectAll({ │ │ │ │ + except: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + if (!this.multipleSelect()) { │ │ │ │ + this.unselectAll({ │ │ │ │ + except: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + this.select(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: multipleSelect │ │ │ │ + * Allow for multiple selected features based on <multiple> property and │ │ │ │ + * <multipleKey> event modifier. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ - * object, the same object will be passed to the callback function passed │ │ │ │ - * if one exists in the options object. │ │ │ │ + * {Boolean} Allow for multiple selected features. │ │ │ │ */ │ │ │ │ - "delete": function() {}, │ │ │ │ + multipleSelect: function() { │ │ │ │ + return this.multiple || (this.handlers.feature.evt && │ │ │ │ + this.handlers.feature.evt[this.multipleKey]); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: commit │ │ │ │ - * Go over the features and for each take action │ │ │ │ - * based on the feature state. Possible actions are create, │ │ │ │ - * update and delete. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array({<OpenLayers.Feature.Vector>})} │ │ │ │ - * options - {Object} Object whose possible keys are "create", "update", │ │ │ │ - * "delete", "callback" and "scope", the values referenced by the │ │ │ │ - * first three are objects as passed to the "create", "update", and │ │ │ │ - * "delete" methods, the value referenced by the "callback" key is │ │ │ │ - * a function which is called when the commit operation is complete │ │ │ │ - * using the scope referenced by the "scope" key. │ │ │ │ + * Method: toggleSelect │ │ │ │ + * Event should toggle the selected state of a feature based on <toggle> │ │ │ │ + * property and <toggleKey> event modifier. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array({<OpenLayers.Protocol.Response>})} An array of │ │ │ │ - * <OpenLayers.Protocol.Response> objects. │ │ │ │ + * {Boolean} Toggle the selected state of a feature. │ │ │ │ */ │ │ │ │ - commit: function() {}, │ │ │ │ + toggleSelect: function() { │ │ │ │ + return this.toggle || (this.handlers.feature.evt && │ │ │ │ + this.handlers.feature.evt[this.toggleKey]); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: abort │ │ │ │ - * Abort an ongoing request. │ │ │ │ + * Method: clickoutFeature │ │ │ │ + * Called on click outside a previously clicked (selected) feature. │ │ │ │ + * Only responds if this.hover is false. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} │ │ │ │ + * feature - {<OpenLayers.Vector.Feature>} │ │ │ │ */ │ │ │ │ - abort: function(response) {}, │ │ │ │ + clickoutFeature: function(feature) { │ │ │ │ + if (!this.hover && this.clickout) { │ │ │ │ + this.unselectAll(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: createCallback │ │ │ │ - * Returns a function that applies the given public method with resp and │ │ │ │ - * options arguments. │ │ │ │ + * Method: overFeature │ │ │ │ + * Called on over a feature. │ │ │ │ + * Only responds if this.hover is true. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * method - {Function} The method to be applied by the callback. │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} The protocol response object. │ │ │ │ - * options - {Object} Options sent to the protocol method │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - createCallback: function(method, response, options) { │ │ │ │ - return OpenLayers.Function.bind(function() { │ │ │ │ - method.apply(this, [response, options]); │ │ │ │ - }, this); │ │ │ │ + overFeature: function(feature) { │ │ │ │ + var layer = feature.layer; │ │ │ │ + if (this.hover) { │ │ │ │ + if (this.highlightOnly) { │ │ │ │ + this.highlight(feature); │ │ │ │ + } else if (OpenLayers.Util.indexOf( │ │ │ │ + layer.selectedFeatures, feature) == -1) { │ │ │ │ + this.select(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol" │ │ │ │ -}); │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Protocol.Response │ │ │ │ - * Protocols return Response objects to their users. │ │ │ │ - */ │ │ │ │ -OpenLayers.Protocol.Response = OpenLayers.Class({ │ │ │ │ /** │ │ │ │ - * Property: code │ │ │ │ - * {Number} - OpenLayers.Protocol.Response.SUCCESS or │ │ │ │ - * OpenLayers.Protocol.Response.FAILURE │ │ │ │ + * Method: outFeature │ │ │ │ + * Called on out of a selected feature. │ │ │ │ + * Only responds if this.hover is true. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - code: null, │ │ │ │ + outFeature: function(feature) { │ │ │ │ + if (this.hover) { │ │ │ │ + if (this.highlightOnly) { │ │ │ │ + // we do nothing if we're not the last highlighter of the │ │ │ │ + // feature │ │ │ │ + if (feature._lastHighlighter == this.id) { │ │ │ │ + // if another select control had highlighted the feature before │ │ │ │ + // we did it ourself then we use that control to highlight the │ │ │ │ + // feature as it was before we highlighted it, else we just │ │ │ │ + // unhighlight it │ │ │ │ + if (feature._prevHighlighter && │ │ │ │ + feature._prevHighlighter != this.id) { │ │ │ │ + delete feature._lastHighlighter; │ │ │ │ + var control = this.map.getControl( │ │ │ │ + feature._prevHighlighter); │ │ │ │ + if (control) { │ │ │ │ + control.highlight(feature); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.unhighlight(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.unselect(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: requestType │ │ │ │ - * {String} The type of request this response corresponds to. Either │ │ │ │ - * "create", "read", "update" or "delete". │ │ │ │ + * Method: highlight │ │ │ │ + * Redraw feature with the select style. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - requestType: null, │ │ │ │ + highlight: function(feature) { │ │ │ │ + var layer = feature.layer; │ │ │ │ + var cont = this.events.triggerEvent("beforefeaturehighlighted", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + if (cont !== false) { │ │ │ │ + feature._prevHighlighter = feature._lastHighlighter; │ │ │ │ + feature._lastHighlighter = this.id; │ │ │ │ + var style = this.selectStyle || this.renderIntent; │ │ │ │ + layer.drawFeature(feature, style); │ │ │ │ + this.events.triggerEvent("featurehighlighted", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: last │ │ │ │ - * {Boolean} - true if this is the last response expected in a commit, │ │ │ │ - * false otherwise, defaults to true. │ │ │ │ + * Method: unhighlight │ │ │ │ + * Redraw feature with the "default" style │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - last: true, │ │ │ │ + unhighlight: function(feature) { │ │ │ │ + var layer = feature.layer; │ │ │ │ + // three cases: │ │ │ │ + // 1. there's no other highlighter, in that case _prev is undefined, │ │ │ │ + // and we just need to undef _last │ │ │ │ + // 2. another control highlighted the feature after we did it, in │ │ │ │ + // that case _last references this other control, and we just │ │ │ │ + // need to undef _prev │ │ │ │ + // 3. another control highlighted the feature before we did it, in │ │ │ │ + // that case _prev references this other control, and we need to │ │ │ │ + // set _last to _prev and undef _prev │ │ │ │ + if (feature._prevHighlighter == undefined) { │ │ │ │ + delete feature._lastHighlighter; │ │ │ │ + } else if (feature._prevHighlighter == this.id) { │ │ │ │ + delete feature._prevHighlighter; │ │ │ │ + } else { │ │ │ │ + feature._lastHighlighter = feature._prevHighlighter; │ │ │ │ + delete feature._prevHighlighter; │ │ │ │ + } │ │ │ │ + layer.drawFeature(feature, feature.style || feature.layer.style || │ │ │ │ + "default"); │ │ │ │ + this.events.triggerEvent("featureunhighlighted", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: features │ │ │ │ - * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>} │ │ │ │ - * The features returned in the response by the server. Depending on the │ │ │ │ - * protocol's read payload, either features or data will be populated. │ │ │ │ + * Method: select │ │ │ │ + * Add feature to the layer's selectedFeature array, render the feature as │ │ │ │ + * selected, and call the onSelect function. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - features: null, │ │ │ │ + select: function(feature) { │ │ │ │ + var cont = this.onBeforeSelect.call(this.scope, feature); │ │ │ │ + var layer = feature.layer; │ │ │ │ + if (cont !== false) { │ │ │ │ + cont = layer.events.triggerEvent("beforefeatureselected", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + if (cont !== false) { │ │ │ │ + layer.selectedFeatures.push(feature); │ │ │ │ + this.highlight(feature); │ │ │ │ + // if the feature handler isn't involved in the feature │ │ │ │ + // selection (because the box handler is used or the │ │ │ │ + // feature is selected programatically) we fake the │ │ │ │ + // feature handler to allow unselecting on click │ │ │ │ + if (!this.handlers.feature.lastFeature) { │ │ │ │ + this.handlers.feature.lastFeature = layer.selectedFeatures[0]; │ │ │ │ + } │ │ │ │ + layer.events.triggerEvent("featureselected", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + this.onSelect.call(this.scope, feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: data │ │ │ │ - * {Object} │ │ │ │ - * The data returned in the response by the server. Depending on the │ │ │ │ - * protocol's read payload, either features or data will be populated. │ │ │ │ + * Method: unselect │ │ │ │ + * Remove feature from the layer's selectedFeature array, render the feature as │ │ │ │ + * normal, and call the onUnselect function. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ */ │ │ │ │ - data: null, │ │ │ │ + unselect: function(feature) { │ │ │ │ + var layer = feature.layer; │ │ │ │ + // Store feature style for restoration later │ │ │ │ + this.unhighlight(feature); │ │ │ │ + OpenLayers.Util.removeItem(layer.selectedFeatures, feature); │ │ │ │ + layer.events.triggerEvent("featureunselected", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + this.onUnselect.call(this.scope, feature); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: reqFeatures │ │ │ │ - * {Array({<OpenLayers.Feature.Vector>})} or {<OpenLayers.Feature.Vector>} │ │ │ │ - * The features provided by the user and placed in the request by the │ │ │ │ - * protocol. │ │ │ │ + * Method: selectBox │ │ │ │ + * Callback from the handlers.box set up when <box> selection is true │ │ │ │ + * on. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * position - {<OpenLayers.Bounds> || <OpenLayers.Pixel> } │ │ │ │ */ │ │ │ │ - reqFeatures: null, │ │ │ │ + selectBox: function(position) { │ │ │ │ + if (position instanceof OpenLayers.Bounds) { │ │ │ │ + var minXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.left, │ │ │ │ + y: position.bottom │ │ │ │ + }); │ │ │ │ + var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.right, │ │ │ │ + y: position.top │ │ │ │ + }); │ │ │ │ + var bounds = new OpenLayers.Bounds( │ │ │ │ + minXY.lon, minXY.lat, maxXY.lon, maxXY.lat │ │ │ │ + ); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: priv │ │ │ │ - */ │ │ │ │ - priv: null, │ │ │ │ + // if multiple is false, first deselect currently selected features │ │ │ │ + if (!this.multipleSelect()) { │ │ │ │ + this.unselectAll(); │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: error │ │ │ │ - * {Object} The error object in case a service exception was encountered. │ │ │ │ - */ │ │ │ │ - error: null, │ │ │ │ + // because we're using a box, we consider we want multiple selection │ │ │ │ + var prevMultiple = this.multiple; │ │ │ │ + this.multiple = true; │ │ │ │ + var layers = this.layers || [this.layer]; │ │ │ │ + this.events.triggerEvent("boxselectionstart", { │ │ │ │ + layers: layers │ │ │ │ + }); │ │ │ │ + var layer; │ │ │ │ + for (var l = 0; l < layers.length; ++l) { │ │ │ │ + layer = layers[l]; │ │ │ │ + for (var i = 0, len = layer.features.length; i < len; ++i) { │ │ │ │ + var feature = layer.features[i]; │ │ │ │ + // check if the feature is displayed │ │ │ │ + if (!feature.getVisibility()) { │ │ │ │ + continue; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Protocol.Response │ │ │ │ - * │ │ │ │ + if (this.geometryTypes == null || OpenLayers.Util.indexOf( │ │ │ │ + this.geometryTypes, feature.geometry.CLASS_NAME) > -1) { │ │ │ │ + if (bounds.toGeometry().intersects(feature.geometry)) { │ │ │ │ + if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { │ │ │ │ + this.select(feature); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.multiple = prevMultiple; │ │ │ │ + this.events.triggerEvent("boxselectionend", { │ │ │ │ + layers: layers │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * Set the map property for the control. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ + setMap: function(map) { │ │ │ │ + this.handlers.feature.setMap(map); │ │ │ │ + if (this.box) { │ │ │ │ + this.handlers.box.setMap(map); │ │ │ │ + } │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: success │ │ │ │ + * APIMethod: setLayer │ │ │ │ + * Attach a new layer to the control, overriding any existing layers. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} - true on success, false otherwise │ │ │ │ + * Parameters: │ │ │ │ + * layers - Array of {<OpenLayers.Layer.Vector>} or a single │ │ │ │ + * {<OpenLayers.Layer.Vector>} │ │ │ │ */ │ │ │ │ - success: function() { │ │ │ │ - return this.code > 0; │ │ │ │ + setLayer: function(layers) { │ │ │ │ + var isActive = this.active; │ │ │ │ + this.unselectAll(); │ │ │ │ + this.deactivate(); │ │ │ │ + if (this.layers) { │ │ │ │ + this.layer.destroy(); │ │ │ │ + this.layers = null; │ │ │ │ + } │ │ │ │ + this.initLayer(layers); │ │ │ │ + this.handlers.feature.layer = this.layer; │ │ │ │ + if (isActive) { │ │ │ │ + this.activate(); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.Response" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.SelectFeature" │ │ │ │ }); │ │ │ │ - │ │ │ │ -OpenLayers.Protocol.Response.SUCCESS = 1; │ │ │ │ -OpenLayers.Protocol.Response.FAILURE = 0; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Request.js │ │ │ │ + OpenLayers/Handler/Drag.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Events.js │ │ │ │ - * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ + * @requires OpenLayers/Handler.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * TODO: deprecate me │ │ │ │ - * Use OpenLayers.Request.proxy instead. │ │ │ │ + * Class: OpenLayers.Handler.Drag │ │ │ │ + * The drag handler is used to deal with sequences of browser events related │ │ │ │ + * to dragging. The handler is used by controls that want to know when │ │ │ │ + * a drag sequence begins, when a drag is happening, and when it has │ │ │ │ + * finished. │ │ │ │ + * │ │ │ │ + * Controls that use the drag handler typically construct it with callbacks │ │ │ │ + * for 'down', 'move', and 'done'. Callbacks for these keys are called │ │ │ │ + * when the drag begins, with each move, and when the drag is done. In │ │ │ │ + * addition, controls can have callbacks keyed to 'up' and 'out' if they │ │ │ │ + * care to differentiate between the types of events that correspond with │ │ │ │ + * the end of a drag sequence. If no drag actually occurs (no mouse move) │ │ │ │ + * the 'down' and 'up' callbacks will be called, but not the 'done' │ │ │ │ + * callback. │ │ │ │ + * │ │ │ │ + * Create a new drag handler with the <OpenLayers.Handler.Drag> constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Handler> │ │ │ │ */ │ │ │ │ -OpenLayers.ProxyHost = ""; │ │ │ │ +OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: started │ │ │ │ + * {Boolean} When a mousedown or touchstart event is received, we want to │ │ │ │ + * record it, but not set 'dragging' until the mouse moves after starting. │ │ │ │ + */ │ │ │ │ + started: false, │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Namespace: OpenLayers.Request │ │ │ │ - * The OpenLayers.Request namespace contains convenience methods for working │ │ │ │ - * with XMLHttpRequests. These methods work with a cross-browser │ │ │ │ - * W3C compliant <OpenLayers.Request.XMLHttpRequest> class. │ │ │ │ - */ │ │ │ │ -if (!OpenLayers.Request) { │ │ │ │ /** │ │ │ │ - * This allows for OpenLayers/Request/XMLHttpRequest.js to be included │ │ │ │ - * before or after this script. │ │ │ │ + * Property: stopDown │ │ │ │ + * {Boolean} Stop propagation of mousedown events from getting to listeners │ │ │ │ + * on the same element. Default is true. │ │ │ │ */ │ │ │ │ - OpenLayers.Request = {}; │ │ │ │ -} │ │ │ │ -OpenLayers.Util.extend(OpenLayers.Request, { │ │ │ │ + stopDown: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: dragging │ │ │ │ + * {Boolean} │ │ │ │ + */ │ │ │ │ + dragging: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: last │ │ │ │ + * {<OpenLayers.Pixel>} The last pixel location of the drag. │ │ │ │ + */ │ │ │ │ + last: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: start │ │ │ │ + * {<OpenLayers.Pixel>} The first pixel location of the drag. │ │ │ │ + */ │ │ │ │ + start: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: DEFAULT_CONFIG │ │ │ │ - * {Object} Default configuration for all requests. │ │ │ │ + * Property: lastMoveEvt │ │ │ │ + * {Object} The last mousemove event that occurred. Used to │ │ │ │ + * position the map correctly when our "delay drag" │ │ │ │ + * timeout expired. │ │ │ │ */ │ │ │ │ - DEFAULT_CONFIG: { │ │ │ │ - method: "GET", │ │ │ │ - url: window.location.href, │ │ │ │ - async: true, │ │ │ │ - user: undefined, │ │ │ │ - password: undefined, │ │ │ │ - params: null, │ │ │ │ - proxy: OpenLayers.ProxyHost, │ │ │ │ - headers: {}, │ │ │ │ - data: null, │ │ │ │ - callback: function() {}, │ │ │ │ - success: null, │ │ │ │ - failure: null, │ │ │ │ - scope: null │ │ │ │ - }, │ │ │ │ + lastMoveEvt: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constant: URL_SPLIT_REGEX │ │ │ │ + * Property: oldOnselectstart │ │ │ │ + * {Function} │ │ │ │ */ │ │ │ │ - URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/, │ │ │ │ + oldOnselectstart: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: events │ │ │ │ - * {<OpenLayers.Events>} An events object that handles all │ │ │ │ - * events on the {<OpenLayers.Request>} object. │ │ │ │ - * │ │ │ │ - * All event listeners will receive an event object with three properties: │ │ │ │ - * request - {<OpenLayers.Request.XMLHttpRequest>} The request object. │ │ │ │ - * config - {Object} The config object sent to the specific request method. │ │ │ │ - * requestUrl - {String} The request url. │ │ │ │ + * Property: interval │ │ │ │ + * {Integer} In order to increase performance, an interval (in │ │ │ │ + * milliseconds) can be set to reduce the number of drag events │ │ │ │ + * called. If set, a new drag event will not be set until the │ │ │ │ + * interval has passed. │ │ │ │ + * Defaults to 0, meaning no interval. │ │ │ │ + */ │ │ │ │ + interval: 0, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: timeoutId │ │ │ │ + * {String} The id of the timeout used for the mousedown interval. │ │ │ │ + * This is "private", and should be left alone. │ │ │ │ + */ │ │ │ │ + timeoutId: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: documentDrag │ │ │ │ + * {Boolean} If set to true, the handler will also handle mouse moves when │ │ │ │ + * the cursor has moved out of the map viewport. Default is false. │ │ │ │ + */ │ │ │ │ + documentDrag: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: documentEvents │ │ │ │ + * {Boolean} Are we currently observing document events? │ │ │ │ + */ │ │ │ │ + documentEvents: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Handler.Drag │ │ │ │ + * Returns OpenLayers.Handler.Drag │ │ │ │ * │ │ │ │ - * Supported event types: │ │ │ │ - * complete - Triggered when we have a response from the request, if a │ │ │ │ - * listener returns false, no further response processing will take │ │ │ │ - * place. │ │ │ │ - * success - Triggered when the HTTP response has a success code (200-299). │ │ │ │ - * failure - Triggered when the HTTP response does not have a success code. │ │ │ │ + * Parameters: │ │ │ │ + * control - {<OpenLayers.Control>} The control that is making use of │ │ │ │ + * this handler. If a handler is being used without a control, the │ │ │ │ + * handlers setMap method must be overridden to deal properly with │ │ │ │ + * the map. │ │ │ │ + * callbacks - {Object} An object containing a single function to be │ │ │ │ + * called when the drag operation is finished. The callback should │ │ │ │ + * expect to recieve a single argument, the pixel location of the event. │ │ │ │ + * Callbacks for 'move' and 'done' are supported. You can also speficy │ │ │ │ + * callbacks for 'down', 'up', and 'out' to respond to those events. │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - events: new OpenLayers.Events(this), │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ + │ │ │ │ + if (this.documentDrag === true) { │ │ │ │ + var me = this; │ │ │ │ + this._docMove = function(evt) { │ │ │ │ + me.mousemove({ │ │ │ │ + xy: { │ │ │ │ + x: evt.clientX, │ │ │ │ + y: evt.clientY │ │ │ │ + }, │ │ │ │ + element: document │ │ │ │ + }); │ │ │ │ + }; │ │ │ │ + this._docUp = function(evt) { │ │ │ │ + me.mouseup({ │ │ │ │ + xy: { │ │ │ │ + x: evt.clientX, │ │ │ │ + y: evt.clientY │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + }; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: makeSameOrigin │ │ │ │ - * Using the specified proxy, returns a same origin url of the provided url. │ │ │ │ + * Method: dragstart │ │ │ │ + * This private method is factorized from mousedown and touchstart methods │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * url - {String} An arbitrary url │ │ │ │ - * proxy {String|Function} The proxy to use to make the provided url a │ │ │ │ - * same origin url. │ │ │ │ + * evt - {Event} The event │ │ │ │ * │ │ │ │ - * Returns │ │ │ │ - * {String} the same origin url. If no proxy is provided, the returned url │ │ │ │ - * will be the same as the provided url. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ */ │ │ │ │ - makeSameOrigin: function(url, proxy) { │ │ │ │ - var sameOrigin = url.indexOf("http") !== 0; │ │ │ │ - var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX); │ │ │ │ - if (urlParts) { │ │ │ │ - var location = window.location; │ │ │ │ - sameOrigin = │ │ │ │ - urlParts[1] == location.protocol && │ │ │ │ - urlParts[3] == location.hostname; │ │ │ │ - var uPort = urlParts[4], │ │ │ │ - lPort = location.port; │ │ │ │ - if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") { │ │ │ │ - sameOrigin = sameOrigin && uPort == lPort; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (!sameOrigin) { │ │ │ │ - if (proxy) { │ │ │ │ - if (typeof proxy == "function") { │ │ │ │ - url = proxy(url); │ │ │ │ - } else { │ │ │ │ - url = proxy + encodeURIComponent(url); │ │ │ │ - } │ │ │ │ + dragstart: function(evt) { │ │ │ │ + var propagate = true; │ │ │ │ + this.dragging = false; │ │ │ │ + if (this.checkModifiers(evt) && │ │ │ │ + (OpenLayers.Event.isLeftClick(evt) || │ │ │ │ + OpenLayers.Event.isSingleTouch(evt))) { │ │ │ │ + this.started = true; │ │ │ │ + this.start = evt.xy; │ │ │ │ + this.last = evt.xy; │ │ │ │ + OpenLayers.Element.addClass( │ │ │ │ + this.map.viewPortDiv, "olDragDown" │ │ │ │ + ); │ │ │ │ + this.down(evt); │ │ │ │ + this.callback("down", [evt.xy]); │ │ │ │ + │ │ │ │ + // prevent document dragging │ │ │ │ + OpenLayers.Event.preventDefault(evt); │ │ │ │ + │ │ │ │ + if (!this.oldOnselectstart) { │ │ │ │ + this.oldOnselectstart = document.onselectstart ? │ │ │ │ + document.onselectstart : OpenLayers.Function.True; │ │ │ │ } │ │ │ │ + document.onselectstart = OpenLayers.Function.False; │ │ │ │ + │ │ │ │ + propagate = !this.stopDown; │ │ │ │ + } else { │ │ │ │ + this.started = false; │ │ │ │ + this.start = null; │ │ │ │ + this.last = null; │ │ │ │ } │ │ │ │ - return url; │ │ │ │ + return propagate; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: issue │ │ │ │ - * Create a new XMLHttpRequest object, open it, set any headers, bind │ │ │ │ - * a callback to done state, and send any data. It is recommended that │ │ │ │ - * you use one <GET>, <POST>, <PUT>, <DELETE>, <OPTIONS>, or <HEAD>. │ │ │ │ - * This method is only documented to provide detail on the configuration │ │ │ │ - * options available to all request methods. │ │ │ │ + * Method: dragmove │ │ │ │ + * This private method is factorized from mousemove and touchmove methods │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * config - {Object} Object containing properties for configuring the │ │ │ │ - * request. Allowed configuration properties are described below. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ - * Allowed config properties: │ │ │ │ - * method - {String} One of GET, POST, PUT, DELETE, HEAD, or │ │ │ │ - * OPTIONS. Default is GET. │ │ │ │ - * url - {String} URL for the request. │ │ │ │ - * async - {Boolean} Open an asynchronous request. Default is true. │ │ │ │ - * user - {String} User for relevant authentication scheme. Set │ │ │ │ - * to null to clear current user. │ │ │ │ - * password - {String} Password for relevant authentication scheme. │ │ │ │ - * Set to null to clear current password. │ │ │ │ - * proxy - {String} Optional proxy. Defaults to │ │ │ │ - * <OpenLayers.ProxyHost>. │ │ │ │ - * params - {Object} Any key:value pairs to be appended to the │ │ │ │ - * url as a query string. Assumes url doesn't already include a query │ │ │ │ - * string or hash. Typically, this is only appropriate for <GET> │ │ │ │ - * requests where the query string will be appended to the url. │ │ │ │ - * Parameter values that are arrays will be │ │ │ │ - * concatenated with a comma (note that this goes against form-encoding) │ │ │ │ - * as is done with <OpenLayers.Util.getParameterString>. │ │ │ │ - * headers - {Object} Object with header:value pairs to be set on │ │ │ │ - * the request. │ │ │ │ - * data - {String | Document} Optional data to send with the request. │ │ │ │ - * Typically, this is only used with <POST> and <PUT> requests. │ │ │ │ - * Make sure to provide the appropriate "Content-Type" header for your │ │ │ │ - * data. For <POST> and <PUT> requests, the content type defaults to │ │ │ │ - * "application-xml". If your data is a different content type, or │ │ │ │ - * if you are using a different HTTP method, set the "Content-Type" │ │ │ │ - * header to match your data type. │ │ │ │ - * callback - {Function} Function to call when request is done. │ │ │ │ - * To determine if the request failed, check request.status (200 │ │ │ │ - * indicates success). │ │ │ │ - * success - {Function} Optional function to call if request status is in │ │ │ │ - * the 200s. This will be called in addition to callback above and │ │ │ │ - * would typically only be used as an alternative. │ │ │ │ - * failure - {Function} Optional function to call if request status is not │ │ │ │ - * in the 200s. This will be called in addition to callback above and │ │ │ │ - * would typically only be used as an alternative. │ │ │ │ - * scope - {Object} If callback is a public method on some object, │ │ │ │ - * set the scope to that object. │ │ │ │ + * evt - {Event} The event │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {XMLHttpRequest} Request object. To abort the request before a response │ │ │ │ - * is received, call abort() on the request object. │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ */ │ │ │ │ - issue: function(config) { │ │ │ │ - // apply default config - proxy host may have changed │ │ │ │ - var defaultConfig = OpenLayers.Util.extend( │ │ │ │ - this.DEFAULT_CONFIG, { │ │ │ │ - proxy: OpenLayers.ProxyHost │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - config = config || {}; │ │ │ │ - config.headers = config.headers || {}; │ │ │ │ - config = OpenLayers.Util.applyDefaults(config, defaultConfig); │ │ │ │ - config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers); │ │ │ │ - // Always set the "X-Requested-With" header to signal that this request │ │ │ │ - // was issued through the XHR-object. Since header keys are case │ │ │ │ - // insensitive and we want to allow overriding of the "X-Requested-With" │ │ │ │ - // header through the user we cannot use applyDefaults, but have to │ │ │ │ - // check manually whether we were called with a "X-Requested-With" │ │ │ │ - // header. │ │ │ │ - var customRequestedWithHeader = false, │ │ │ │ - headerKey; │ │ │ │ - for (headerKey in config.headers) { │ │ │ │ - if (config.headers.hasOwnProperty(headerKey)) { │ │ │ │ - if (headerKey.toLowerCase() === 'x-requested-with') { │ │ │ │ - customRequestedWithHeader = true; │ │ │ │ + dragmove: function(evt) { │ │ │ │ + this.lastMoveEvt = evt; │ │ │ │ + if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || │ │ │ │ + evt.xy.y != this.last.y)) { │ │ │ │ + if (this.documentDrag === true && this.documentEvents) { │ │ │ │ + if (evt.element === document) { │ │ │ │ + this.adjustXY(evt); │ │ │ │ + // do setEvent manually because the documentEvents are not │ │ │ │ + // registered with the map │ │ │ │ + this.setEvent(evt); │ │ │ │ + } else { │ │ │ │ + this.removeDocumentEvents(); │ │ │ │ } │ │ │ │ } │ │ │ │ - } │ │ │ │ - if (customRequestedWithHeader === false) { │ │ │ │ - // we did not have a custom "X-Requested-With" header │ │ │ │ - config.headers['X-Requested-With'] = 'XMLHttpRequest'; │ │ │ │ - } │ │ │ │ + if (this.interval > 0) { │ │ │ │ + this.timeoutId = setTimeout( │ │ │ │ + OpenLayers.Function.bind(this.removeTimeout, this), │ │ │ │ + this.interval); │ │ │ │ + } │ │ │ │ + this.dragging = true; │ │ │ │ │ │ │ │ - // create request, open, and set headers │ │ │ │ - var request = new OpenLayers.Request.XMLHttpRequest(); │ │ │ │ - var url = OpenLayers.Util.urlAppend(config.url, │ │ │ │ - OpenLayers.Util.getParameterString(config.params || {})); │ │ │ │ - url = OpenLayers.Request.makeSameOrigin(url, config.proxy); │ │ │ │ - request.open( │ │ │ │ - config.method, url, config.async, config.user, config.password │ │ │ │ - ); │ │ │ │ - for (var header in config.headers) { │ │ │ │ - request.setRequestHeader(header, config.headers[header]); │ │ │ │ + this.move(evt); │ │ │ │ + this.callback("move", [evt.xy]); │ │ │ │ + if (!this.oldOnselectstart) { │ │ │ │ + this.oldOnselectstart = document.onselectstart; │ │ │ │ + document.onselectstart = OpenLayers.Function.False; │ │ │ │ + } │ │ │ │ + this.last = evt.xy; │ │ │ │ } │ │ │ │ + return true; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var events = this.events; │ │ │ │ - │ │ │ │ - // we want to execute runCallbacks with "this" as the │ │ │ │ - // execution scope │ │ │ │ - var self = this; │ │ │ │ - │ │ │ │ - request.onreadystatechange = function() { │ │ │ │ - if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) { │ │ │ │ - var proceed = events.triggerEvent( │ │ │ │ - "complete", { │ │ │ │ - request: request, │ │ │ │ - config: config, │ │ │ │ - requestUrl: url │ │ │ │ - } │ │ │ │ - ); │ │ │ │ - if (proceed !== false) { │ │ │ │ - self.runCallbacks({ │ │ │ │ - request: request, │ │ │ │ - config: config, │ │ │ │ - requestUrl: url │ │ │ │ - }); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: dragend │ │ │ │ + * This private method is factorized from mouseup and touchend methods │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} The event │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ + */ │ │ │ │ + dragend: function(evt) { │ │ │ │ + if (this.started) { │ │ │ │ + if (this.documentDrag === true && this.documentEvents) { │ │ │ │ + this.adjustXY(evt); │ │ │ │ + this.removeDocumentEvents(); │ │ │ │ } │ │ │ │ - }; │ │ │ │ - │ │ │ │ - // send request (optionally with data) and return │ │ │ │ - // call in a timeout for asynchronous requests so the return is │ │ │ │ - // available before readyState == 4 for cached docs │ │ │ │ - if (config.async === false) { │ │ │ │ - request.send(config.data); │ │ │ │ - } else { │ │ │ │ - window.setTimeout(function() { │ │ │ │ - if (request.readyState !== 0) { // W3C: 0-UNSENT │ │ │ │ - request.send(config.data); │ │ │ │ - } │ │ │ │ - }, 0); │ │ │ │ + var dragged = (this.start != this.last); │ │ │ │ + this.started = false; │ │ │ │ + this.dragging = false; │ │ │ │ + OpenLayers.Element.removeClass( │ │ │ │ + this.map.viewPortDiv, "olDragDown" │ │ │ │ + ); │ │ │ │ + this.up(evt); │ │ │ │ + this.callback("up", [evt.xy]); │ │ │ │ + if (dragged) { │ │ │ │ + this.callback("done", [evt.xy]); │ │ │ │ + } │ │ │ │ + document.onselectstart = this.oldOnselectstart; │ │ │ │ } │ │ │ │ - return request; │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: runCallbacks │ │ │ │ - * Calls the complete, success and failure callbacks. Application │ │ │ │ - * can listen to the "complete" event, have the listener │ │ │ │ - * display a confirm window and always return false, and │ │ │ │ - * execute OpenLayers.Request.runCallbacks if the user │ │ │ │ - * hits "yes" in the confirm window. │ │ │ │ + * The four methods below (down, move, up, and out) are used by subclasses │ │ │ │ + * to do their own processing related to these mouse events. │ │ │ │ + */ │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: down │ │ │ │ + * This method is called during the handling of the mouse down event. │ │ │ │ + * Subclasses can do their own processing here. │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} Hash containing request, config and requestUrl keys │ │ │ │ + * evt - {Event} The mouse down event │ │ │ │ */ │ │ │ │ - runCallbacks: function(options) { │ │ │ │ - var request = options.request; │ │ │ │ - var config = options.config; │ │ │ │ + down: function(evt) {}, │ │ │ │ │ │ │ │ - // bind callbacks to readyState 4 (done) │ │ │ │ - var complete = (config.scope) ? │ │ │ │ - OpenLayers.Function.bind(config.callback, config.scope) : │ │ │ │ - config.callback; │ │ │ │ + /** │ │ │ │ + * Method: move │ │ │ │ + * This method is called during the handling of the mouse move event. │ │ │ │ + * Subclasses can do their own processing here. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} The mouse move event │ │ │ │ + * │ │ │ │ + */ │ │ │ │ + move: function(evt) {}, │ │ │ │ │ │ │ │ - // optional success callback │ │ │ │ - var success; │ │ │ │ - if (config.success) { │ │ │ │ - success = (config.scope) ? │ │ │ │ - OpenLayers.Function.bind(config.success, config.scope) : │ │ │ │ - config.success; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: up │ │ │ │ + * This method is called during the handling of the mouse up event. │ │ │ │ + * Subclasses can do their own processing here. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} The mouse up event │ │ │ │ + */ │ │ │ │ + up: function(evt) {}, │ │ │ │ │ │ │ │ - // optional failure callback │ │ │ │ - var failure; │ │ │ │ - if (config.failure) { │ │ │ │ - failure = (config.scope) ? │ │ │ │ - OpenLayers.Function.bind(config.failure, config.scope) : │ │ │ │ - config.failure; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: out │ │ │ │ + * This method is called during the handling of the mouse out event. │ │ │ │ + * Subclasses can do their own processing here. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} The mouse out event │ │ │ │ + */ │ │ │ │ + out: function(evt) {}, │ │ │ │ │ │ │ │ - if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" && │ │ │ │ - request.responseText) { │ │ │ │ - request.status = 200; │ │ │ │ - } │ │ │ │ - complete(request); │ │ │ │ + /** │ │ │ │ + * The methods below are part of the magic of event handling. Because │ │ │ │ + * they are named like browser events, they are registered as listeners │ │ │ │ + * for the events they represent. │ │ │ │ + */ │ │ │ │ │ │ │ │ - if (!request.status || (request.status >= 200 && request.status < 300)) { │ │ │ │ - this.events.triggerEvent("success", options); │ │ │ │ - if (success) { │ │ │ │ - success(request); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (request.status && (request.status < 200 || request.status >= 300)) { │ │ │ │ - this.events.triggerEvent("failure", options); │ │ │ │ - if (failure) { │ │ │ │ - failure(request); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: mousedown │ │ │ │ + * Handle mousedown events │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ + */ │ │ │ │ + mousedown: function(evt) { │ │ │ │ + return this.dragstart(evt); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: GET │ │ │ │ - * Send an HTTP GET request. Additional configuration properties are │ │ │ │ - * documented in the <issue> method, with the method property set │ │ │ │ - * to GET. │ │ │ │ + * Method: touchstart │ │ │ │ + * Handle touchstart events │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * config - {Object} Object with properties for configuring the request. │ │ │ │ - * See the <issue> method for documentation of allowed properties. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ + * evt - {Event} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {XMLHttpRequest} Request object. │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ */ │ │ │ │ - GET: function(config) { │ │ │ │ - config = OpenLayers.Util.extend(config, { │ │ │ │ - method: "GET" │ │ │ │ - }); │ │ │ │ - return OpenLayers.Request.issue(config); │ │ │ │ + touchstart: function(evt) { │ │ │ │ + this.startTouch(); │ │ │ │ + return this.dragstart(evt); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: POST │ │ │ │ - * Send a POST request. Additional configuration properties are │ │ │ │ - * documented in the <issue> method, with the method property set │ │ │ │ - * to POST and "Content-Type" header set to "application/xml". │ │ │ │ + * Method: mousemove │ │ │ │ + * Handle mousemove events │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * config - {Object} Object with properties for configuring the request. │ │ │ │ - * See the <issue> method for documentation of allowed properties. The │ │ │ │ - * default "Content-Type" header will be set to "application-xml" if │ │ │ │ - * none is provided. This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ + * evt - {Event} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {XMLHttpRequest} Request object. │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ */ │ │ │ │ - POST: function(config) { │ │ │ │ - config = OpenLayers.Util.extend(config, { │ │ │ │ - method: "POST" │ │ │ │ - }); │ │ │ │ - // set content type to application/xml if it isn't already set │ │ │ │ - config.headers = config.headers ? config.headers : {}; │ │ │ │ - if (!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) { │ │ │ │ - config.headers["Content-Type"] = "application/xml"; │ │ │ │ - } │ │ │ │ - return OpenLayers.Request.issue(config); │ │ │ │ + mousemove: function(evt) { │ │ │ │ + return this.dragmove(evt); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: PUT │ │ │ │ - * Send an HTTP PUT request. Additional configuration properties are │ │ │ │ - * documented in the <issue> method, with the method property set │ │ │ │ - * to PUT and "Content-Type" header set to "application/xml". │ │ │ │ + * Method: touchmove │ │ │ │ + * Handle touchmove events │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * config - {Object} Object with properties for configuring the request. │ │ │ │ - * See the <issue> method for documentation of allowed properties. The │ │ │ │ - * default "Content-Type" header will be set to "application-xml" if │ │ │ │ - * none is provided. This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ + * evt - {Event} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {XMLHttpRequest} Request object. │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ */ │ │ │ │ - PUT: function(config) { │ │ │ │ - config = OpenLayers.Util.extend(config, { │ │ │ │ - method: "PUT" │ │ │ │ - }); │ │ │ │ - // set content type to application/xml if it isn't already set │ │ │ │ - config.headers = config.headers ? config.headers : {}; │ │ │ │ - if (!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) { │ │ │ │ - config.headers["Content-Type"] = "application/xml"; │ │ │ │ + touchmove: function(evt) { │ │ │ │ + return this.dragmove(evt); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: removeTimeout │ │ │ │ + * Private. Called by mousemove() to remove the drag timeout. │ │ │ │ + */ │ │ │ │ + removeTimeout: function() { │ │ │ │ + this.timeoutId = null; │ │ │ │ + // if timeout expires while we're still dragging (mouseup │ │ │ │ + // hasn't occurred) then call mousemove to move to the │ │ │ │ + // correct position │ │ │ │ + if (this.dragging) { │ │ │ │ + this.mousemove(this.lastMoveEvt); │ │ │ │ } │ │ │ │ - return OpenLayers.Request.issue(config); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: DELETE │ │ │ │ - * Send an HTTP DELETE request. Additional configuration properties are │ │ │ │ - * documented in the <issue> method, with the method property set │ │ │ │ - * to DELETE. │ │ │ │ + * Method: mouseup │ │ │ │ + * Handle mouseup events │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * config - {Object} Object with properties for configuring the request. │ │ │ │ - * See the <issue> method for documentation of allowed properties. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ + * evt - {Event} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {XMLHttpRequest} Request object. │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ */ │ │ │ │ - DELETE: function(config) { │ │ │ │ - config = OpenLayers.Util.extend(config, { │ │ │ │ - method: "DELETE" │ │ │ │ - }); │ │ │ │ - return OpenLayers.Request.issue(config); │ │ │ │ + mouseup: function(evt) { │ │ │ │ + return this.dragend(evt); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: HEAD │ │ │ │ - * Send an HTTP HEAD request. Additional configuration properties are │ │ │ │ - * documented in the <issue> method, with the method property set │ │ │ │ - * to HEAD. │ │ │ │ + * Method: touchend │ │ │ │ + * Handle touchend events │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * config - {Object} Object with properties for configuring the request. │ │ │ │ - * See the <issue> method for documentation of allowed properties. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ + * evt - {Event} │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {XMLHttpRequest} Request object. │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ */ │ │ │ │ - HEAD: function(config) { │ │ │ │ - config = OpenLayers.Util.extend(config, { │ │ │ │ - method: "HEAD" │ │ │ │ - }); │ │ │ │ - return OpenLayers.Request.issue(config); │ │ │ │ + touchend: function(evt) { │ │ │ │ + // override evt.xy with last position since touchend does not have │ │ │ │ + // any touch position │ │ │ │ + evt.xy = this.last; │ │ │ │ + return this.dragend(evt); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: OPTIONS │ │ │ │ - * Send an HTTP OPTIONS request. Additional configuration properties are │ │ │ │ - * documented in the <issue> method, with the method property set │ │ │ │ - * to OPTIONS. │ │ │ │ + * Method: mouseout │ │ │ │ + * Handle mouseout events │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * config - {Object} Object with properties for configuring the request. │ │ │ │ - * See the <issue> method for documentation of allowed properties. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ + * evt - {Event} │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ + */ │ │ │ │ + mouseout: function(evt) { │ │ │ │ + if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) { │ │ │ │ + if (this.documentDrag === true) { │ │ │ │ + this.addDocumentEvents(); │ │ │ │ + } else { │ │ │ │ + var dragged = (this.start != this.last); │ │ │ │ + this.started = false; │ │ │ │ + this.dragging = false; │ │ │ │ + OpenLayers.Element.removeClass( │ │ │ │ + this.map.viewPortDiv, "olDragDown" │ │ │ │ + ); │ │ │ │ + this.out(evt); │ │ │ │ + this.callback("out", []); │ │ │ │ + if (dragged) { │ │ │ │ + this.callback("done", [evt.xy]); │ │ │ │ + } │ │ │ │ + if (document.onselectstart) { │ │ │ │ + document.onselectstart = this.oldOnselectstart; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return true; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: click │ │ │ │ + * The drag handler captures the click event. If something else registers │ │ │ │ + * for clicks on the same element, its listener will not be called │ │ │ │ + * after a drag. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {XMLHttpRequest} Request object. │ │ │ │ + * {Boolean} Let the event propagate. │ │ │ │ */ │ │ │ │ - OPTIONS: function(config) { │ │ │ │ - config = OpenLayers.Util.extend(config, { │ │ │ │ - method: "OPTIONS" │ │ │ │ - }); │ │ │ │ - return OpenLayers.Request.issue(config); │ │ │ │ - } │ │ │ │ + click: function(evt) { │ │ │ │ + // let the click event propagate only if the mouse moved │ │ │ │ + return (this.start == this.last); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: activate │ │ │ │ + * Activate the handler. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The handler was successfully activated. │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + var activated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.dragging = false; │ │ │ │ + activated = true; │ │ │ │ + } │ │ │ │ + return activated; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: deactivate │ │ │ │ + * Deactivate the handler. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The handler was successfully deactivated. │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.started = false; │ │ │ │ + this.dragging = false; │ │ │ │ + this.start = null; │ │ │ │ + this.last = null; │ │ │ │ + deactivated = true; │ │ │ │ + OpenLayers.Element.removeClass( │ │ │ │ + this.map.viewPortDiv, "olDragDown" │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + return deactivated; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: adjustXY │ │ │ │ + * Converts event coordinates that are relative to the document body to │ │ │ │ + * ones that are relative to the map viewport. The latter is the default in │ │ │ │ + * OpenLayers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Object} │ │ │ │ + */ │ │ │ │ + adjustXY: function(evt) { │ │ │ │ + var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv); │ │ │ │ + evt.xy.x -= pos[0]; │ │ │ │ + evt.xy.y -= pos[1]; │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: addDocumentEvents │ │ │ │ + * Start observing document events when documentDrag is true and the mouse │ │ │ │ + * cursor leaves the map viewport while dragging. │ │ │ │ + */ │ │ │ │ + addDocumentEvents: function() { │ │ │ │ + OpenLayers.Element.addClass(document.body, "olDragDown"); │ │ │ │ + this.documentEvents = true; │ │ │ │ + OpenLayers.Event.observe(document, "mousemove", this._docMove); │ │ │ │ + OpenLayers.Event.observe(document, "mouseup", this._docUp); │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: removeDocumentEvents │ │ │ │ + * Stops observing document events when documentDrag is true and the mouse │ │ │ │ + * cursor re-enters the map viewport while dragging. │ │ │ │ + */ │ │ │ │ + removeDocumentEvents: function() { │ │ │ │ + OpenLayers.Element.removeClass(document.body, "olDragDown"); │ │ │ │ + this.documentEvents = false; │ │ │ │ + OpenLayers.Event.stopObserving(document, "mousemove", this._docMove); │ │ │ │ + OpenLayers.Event.stopObserving(document, "mouseup", this._docUp); │ │ │ │ + }, │ │ │ │ │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Drag" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Request/XMLHttpRequest.js │ │ │ │ + OpenLayers/Handler/Box.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ -// XMLHttpRequest.js Copyright (C) 2010 Sergey Ilinsky (http://www.ilinsky.com) │ │ │ │ -// │ │ │ │ -// Licensed under the Apache License, Version 2.0 (the "License"); │ │ │ │ -// you may not use this file except in compliance with the License. │ │ │ │ -// You may obtain a copy of the License at │ │ │ │ -// │ │ │ │ -// http://www.apache.org/licenses/LICENSE-2.0 │ │ │ │ -// │ │ │ │ -// Unless required by applicable law or agreed to in writing, software │ │ │ │ -// distributed under the License is distributed on an "AS IS" BASIS, │ │ │ │ -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ │ │ │ -// See the License for the specific language governing permissions and │ │ │ │ -// limitations under the License. │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Request.js │ │ │ │ + * @requires OpenLayers/Handler.js │ │ │ │ + * @requires OpenLayers/Handler/Drag.js │ │ │ │ */ │ │ │ │ │ │ │ │ -(function() { │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Handler.Box │ │ │ │ + * Handler for dragging a rectangle across the map. Box is displayed │ │ │ │ + * on mouse down, moves on mouse move, and is finished on mouse up. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Handler> │ │ │ │ + */ │ │ │ │ +OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ │ │ │ │ - // Save reference to earlier defined object implementation (if any) │ │ │ │ - var oXMLHttpRequest = window.XMLHttpRequest; │ │ │ │ + /** │ │ │ │ + * Property: dragHandler │ │ │ │ + * {<OpenLayers.Handler.Drag>} │ │ │ │ + */ │ │ │ │ + dragHandler: null, │ │ │ │ │ │ │ │ - // Define on browser type │ │ │ │ - var bGecko = !!window.controllers, │ │ │ │ - bIE = window.document.all && !window.opera, │ │ │ │ - bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/); │ │ │ │ + /** │ │ │ │ + * APIProperty: boxDivClassName │ │ │ │ + * {String} The CSS class to use for drawing the box. Default is │ │ │ │ + * olHandlerBoxZoomBox │ │ │ │ + */ │ │ │ │ + boxDivClassName: 'olHandlerBoxZoomBox', │ │ │ │ │ │ │ │ - // Enables "XMLHttpRequest()" call next to "new XMLHttpReques()" │ │ │ │ - function fXMLHttpRequest() { │ │ │ │ - this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP"); │ │ │ │ - this._listeners = []; │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Property: boxOffsets │ │ │ │ + * {Object} Caches box offsets from css. This is used by the getBoxOffsets │ │ │ │ + * method. │ │ │ │ + */ │ │ │ │ + boxOffsets: null, │ │ │ │ │ │ │ │ - // Constructor │ │ │ │ - function cXMLHttpRequest() { │ │ │ │ - return new fXMLHttpRequest; │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype = fXMLHttpRequest.prototype; │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Handler.Box │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * control - {<OpenLayers.Control>} │ │ │ │ + * callbacks - {Object} An object with a properties whose values are │ │ │ │ + * functions. Various callbacks described below. │ │ │ │ + * options - {Object} │ │ │ │ + * │ │ │ │ + * Named callbacks: │ │ │ │ + * start - Called when the box drag operation starts. │ │ │ │ + * done - Called when the box drag operation is finished. │ │ │ │ + * The callback should expect to receive a single argument, the box │ │ │ │ + * bounds or a pixel. If the box dragging didn't span more than a 5 │ │ │ │ + * pixel distance, a pixel will be returned instead of a bounds object. │ │ │ │ + */ │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ + this.dragHandler = new OpenLayers.Handler.Drag( │ │ │ │ + this, { │ │ │ │ + down: this.startBox, │ │ │ │ + move: this.moveBox, │ │ │ │ + out: this.removeBox, │ │ │ │ + up: this.endBox │ │ │ │ + }, { │ │ │ │ + keyMask: this.keyMask │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // BUGFIX: Firefox with Firebug installed would break pages if not executed │ │ │ │ - if (bGecko && oXMLHttpRequest.wrapped) │ │ │ │ - cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped; │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ + if (this.dragHandler) { │ │ │ │ + this.dragHandler.destroy(); │ │ │ │ + this.dragHandler = null; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Constants │ │ │ │ - cXMLHttpRequest.UNSENT = 0; │ │ │ │ - cXMLHttpRequest.OPENED = 1; │ │ │ │ - cXMLHttpRequest.HEADERS_RECEIVED = 2; │ │ │ │ - cXMLHttpRequest.LOADING = 3; │ │ │ │ - cXMLHttpRequest.DONE = 4; │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Handler.prototype.setMap.apply(this, arguments); │ │ │ │ + if (this.dragHandler) { │ │ │ │ + this.dragHandler.setMap(map); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Public Properties │ │ │ │ - cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ - cXMLHttpRequest.prototype.responseText = ''; │ │ │ │ - cXMLHttpRequest.prototype.responseXML = null; │ │ │ │ - cXMLHttpRequest.prototype.status = 0; │ │ │ │ - cXMLHttpRequest.prototype.statusText = ''; │ │ │ │ + /** │ │ │ │ + * Method: startBox │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * xy - {<OpenLayers.Pixel>} │ │ │ │ + */ │ │ │ │ + startBox: function(xy) { │ │ │ │ + this.callback("start", []); │ │ │ │ + this.zoomBox = OpenLayers.Util.createDiv('zoomBox', { │ │ │ │ + x: -9999, │ │ │ │ + y: -9999 │ │ │ │ + }); │ │ │ │ + this.zoomBox.className = this.boxDivClassName; │ │ │ │ + this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1; │ │ │ │ │ │ │ │ - // Priority proposal │ │ │ │ - cXMLHttpRequest.prototype.priority = "NORMAL"; │ │ │ │ + this.map.viewPortDiv.appendChild(this.zoomBox); │ │ │ │ │ │ │ │ - // Instance-level Events Handlers │ │ │ │ - cXMLHttpRequest.prototype.onreadystatechange = null; │ │ │ │ + OpenLayers.Element.addClass( │ │ │ │ + this.map.viewPortDiv, "olDrawBox" │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Class-level Events Handlers │ │ │ │ - cXMLHttpRequest.onreadystatechange = null; │ │ │ │ - cXMLHttpRequest.onopen = null; │ │ │ │ - cXMLHttpRequest.onsend = null; │ │ │ │ - cXMLHttpRequest.onabort = null; │ │ │ │ + /** │ │ │ │ + * Method: moveBox │ │ │ │ + */ │ │ │ │ + moveBox: function(xy) { │ │ │ │ + var startX = this.dragHandler.start.x; │ │ │ │ + var startY = this.dragHandler.start.y; │ │ │ │ + var deltaX = Math.abs(startX - xy.x); │ │ │ │ + var deltaY = Math.abs(startY - xy.y); │ │ │ │ │ │ │ │ - // Public Methods │ │ │ │ - cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) { │ │ │ │ - // Delete headers, required when object is reused │ │ │ │ - delete this._headers; │ │ │ │ + var offset = this.getBoxOffsets(); │ │ │ │ + this.zoomBox.style.width = (deltaX + offset.width + 1) + "px"; │ │ │ │ + this.zoomBox.style.height = (deltaY + offset.height + 1) + "px"; │ │ │ │ + this.zoomBox.style.left = (xy.x < startX ? │ │ │ │ + startX - deltaX - offset.left : startX - offset.left) + "px"; │ │ │ │ + this.zoomBox.style.top = (xy.y < startY ? │ │ │ │ + startY - deltaY - offset.top : startY - offset.top) + "px"; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // When bAsync parameter value is omitted, use true as default │ │ │ │ - if (arguments.length < 3) │ │ │ │ - bAsync = true; │ │ │ │ + /** │ │ │ │ + * Method: endBox │ │ │ │ + */ │ │ │ │ + endBox: function(end) { │ │ │ │ + var result; │ │ │ │ + if (Math.abs(this.dragHandler.start.x - end.x) > 5 || │ │ │ │ + Math.abs(this.dragHandler.start.y - end.y) > 5) { │ │ │ │ + var start = this.dragHandler.start; │ │ │ │ + var top = Math.min(start.y, end.y); │ │ │ │ + var bottom = Math.max(start.y, end.y); │ │ │ │ + var left = Math.min(start.x, end.x); │ │ │ │ + var right = Math.max(start.x, end.x); │ │ │ │ + result = new OpenLayers.Bounds(left, bottom, right, top); │ │ │ │ + } else { │ │ │ │ + result = this.dragHandler.start.clone(); // i.e. OL.Pixel │ │ │ │ + } │ │ │ │ + this.removeBox(); │ │ │ │ │ │ │ │ - // Save async parameter for fixing Gecko bug with missing readystatechange in synchronous requests │ │ │ │ - this._async = bAsync; │ │ │ │ + this.callback("done", [result]); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Set the onreadystatechange handler │ │ │ │ - var oRequest = this, │ │ │ │ - nState = this.readyState, │ │ │ │ - fOnUnload; │ │ │ │ + /** │ │ │ │ + * Method: removeBox │ │ │ │ + * Remove the zoombox from the screen and nullify our reference to it. │ │ │ │ + */ │ │ │ │ + removeBox: function() { │ │ │ │ + this.map.viewPortDiv.removeChild(this.zoomBox); │ │ │ │ + this.zoomBox = null; │ │ │ │ + this.boxOffsets = null; │ │ │ │ + OpenLayers.Element.removeClass( │ │ │ │ + this.map.viewPortDiv, "olDrawBox" │ │ │ │ + ); │ │ │ │ │ │ │ │ - // BUGFIX: IE - memory leak on page unload (inter-page leak) │ │ │ │ - if (bIE && bAsync) { │ │ │ │ - fOnUnload = function() { │ │ │ │ - if (nState != cXMLHttpRequest.DONE) { │ │ │ │ - fCleanTransport(oRequest); │ │ │ │ - // Safe to abort here since onreadystatechange handler removed │ │ │ │ - oRequest.abort(); │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - window.attachEvent("onunload", fOnUnload); │ │ │ │ - } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Add method sniffer │ │ │ │ - if (cXMLHttpRequest.onopen) │ │ │ │ - cXMLHttpRequest.onopen.apply(this, arguments); │ │ │ │ + /** │ │ │ │ + * Method: activate │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.dragHandler.activate(); │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (arguments.length > 4) │ │ │ │ - this._object.open(sMethod, sUrl, bAsync, sUser, sPassword); │ │ │ │ - else │ │ │ │ - if (arguments.length > 3) │ │ │ │ - this._object.open(sMethod, sUrl, bAsync, sUser); │ │ │ │ - else │ │ │ │ - this._object.open(sMethod, sUrl, bAsync); │ │ │ │ + /** │ │ │ │ + * Method: deactivate │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + if (this.dragHandler.deactivate()) { │ │ │ │ + if (this.zoomBox) { │ │ │ │ + this.removeBox(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.readyState = cXMLHttpRequest.OPENED; │ │ │ │ - fReadyStateChange(this); │ │ │ │ + /** │ │ │ │ + * Method: getBoxOffsets │ │ │ │ + * Determines border offsets for a box, according to the box model. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Object} an object with the following offsets: │ │ │ │ + * - left │ │ │ │ + * - right │ │ │ │ + * - top │ │ │ │ + * - bottom │ │ │ │ + * - width │ │ │ │ + * - height │ │ │ │ + */ │ │ │ │ + getBoxOffsets: function() { │ │ │ │ + if (!this.boxOffsets) { │ │ │ │ + // Determine the box model. If the testDiv's clientWidth is 3, then │ │ │ │ + // the borders are outside and we are dealing with the w3c box │ │ │ │ + // model. Otherwise, the browser uses the traditional box model and │ │ │ │ + // the borders are inside the box bounds, leaving us with a │ │ │ │ + // clientWidth of 1. │ │ │ │ + var testDiv = document.createElement("div"); │ │ │ │ + //testDiv.style.visibility = "hidden"; │ │ │ │ + testDiv.style.position = "absolute"; │ │ │ │ + testDiv.style.border = "1px solid black"; │ │ │ │ + testDiv.style.width = "3px"; │ │ │ │ + document.body.appendChild(testDiv); │ │ │ │ + var w3cBoxModel = testDiv.clientWidth == 3; │ │ │ │ + document.body.removeChild(testDiv); │ │ │ │ │ │ │ │ - this._object.onreadystatechange = function() { │ │ │ │ - if (bGecko && !bAsync) │ │ │ │ - return; │ │ │ │ + var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, │ │ │ │ + "border-left-width")); │ │ │ │ + var right = parseInt(OpenLayers.Element.getStyle( │ │ │ │ + this.zoomBox, "border-right-width")); │ │ │ │ + var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, │ │ │ │ + "border-top-width")); │ │ │ │ + var bottom = parseInt(OpenLayers.Element.getStyle( │ │ │ │ + this.zoomBox, "border-bottom-width")); │ │ │ │ + this.boxOffsets = { │ │ │ │ + left: left, │ │ │ │ + right: right, │ │ │ │ + top: top, │ │ │ │ + bottom: bottom, │ │ │ │ + width: w3cBoxModel === false ? left + right : 0, │ │ │ │ + height: w3cBoxModel === false ? top + bottom : 0 │ │ │ │ + }; │ │ │ │ + } │ │ │ │ + return this.boxOffsets; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Synchronize state │ │ │ │ - oRequest.readyState = oRequest._object.readyState; │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Box" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/ZoomBox.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - // │ │ │ │ - fSynchronizeValues(oRequest); │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - // BUGFIX: Firefox fires unnecessary DONE when aborting │ │ │ │ - if (oRequest._aborted) { │ │ │ │ - // Reset readyState to UNSENT │ │ │ │ - oRequest.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Handler/Box.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - // Return now │ │ │ │ - return; │ │ │ │ - } │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.ZoomBox │ │ │ │ + * The ZoomBox control enables zooming directly to a given extent, by drawing │ │ │ │ + * a box on the map. The box is drawn by holding down shift, whilst dragging │ │ │ │ + * the mouse. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + /** │ │ │ │ + * Property: type │ │ │ │ + * {OpenLayers.Control.TYPE} │ │ │ │ + */ │ │ │ │ + type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ │ │ │ │ - if (oRequest.readyState == cXMLHttpRequest.DONE) { │ │ │ │ - // Free up queue │ │ │ │ - delete oRequest._data; │ │ │ │ - /* if (bAsync) │ │ │ │ - fQueue_remove(oRequest);*/ │ │ │ │ - // │ │ │ │ - fCleanTransport(oRequest); │ │ │ │ - // Uncomment this block if you need a fix for IE cache │ │ │ │ - /* │ │ │ │ - // BUGFIX: IE - cache issue │ │ │ │ - if (!oRequest._object.getResponseHeader("Date")) { │ │ │ │ - // Save object to cache │ │ │ │ - oRequest._cached = oRequest._object; │ │ │ │ + /** │ │ │ │ + * Property: out │ │ │ │ + * {Boolean} Should the control be used for zooming out? │ │ │ │ + */ │ │ │ │ + out: false, │ │ │ │ │ │ │ │ - // Instantiate a new transport object │ │ │ │ - cXMLHttpRequest.call(oRequest); │ │ │ │ + /** │ │ │ │ + * APIProperty: keyMask │ │ │ │ + * {Integer} Zoom only occurs if the keyMask matches the combination of │ │ │ │ + * keys down. Use bitwise operators and one or more of the │ │ │ │ + * <OpenLayers.Handler> constants to construct a keyMask. Leave null if │ │ │ │ + * not used mask. Default is null. │ │ │ │ + */ │ │ │ │ + keyMask: null, │ │ │ │ │ │ │ │ - // Re-send request │ │ │ │ - if (sUser) { │ │ │ │ - if (sPassword) │ │ │ │ - oRequest._object.open(sMethod, sUrl, bAsync, sUser, sPassword); │ │ │ │ - else │ │ │ │ - oRequest._object.open(sMethod, sUrl, bAsync, sUser); │ │ │ │ - } │ │ │ │ - else │ │ │ │ - oRequest._object.open(sMethod, sUrl, bAsync); │ │ │ │ - oRequest._object.setRequestHeader("If-Modified-Since", oRequest._cached.getResponseHeader("Last-Modified") || new window.Date(0)); │ │ │ │ - // Copy headers set │ │ │ │ - if (oRequest._headers) │ │ │ │ - for (var sHeader in oRequest._headers) │ │ │ │ - if (typeof oRequest._headers[sHeader] == "string") // Some frameworks prototype objects with functions │ │ │ │ - oRequest._object.setRequestHeader(sHeader, oRequest._headers[sHeader]); │ │ │ │ + /** │ │ │ │ + * APIProperty: alwaysZoom │ │ │ │ + * {Boolean} Always zoom in/out when box drawn, even if the zoom level does │ │ │ │ + * not change. │ │ │ │ + */ │ │ │ │ + alwaysZoom: false, │ │ │ │ │ │ │ │ - oRequest._object.onreadystatechange = function() { │ │ │ │ - // Synchronize state │ │ │ │ - oRequest.readyState = oRequest._object.readyState; │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomOnClick │ │ │ │ + * {Boolean} Should we zoom when no box was dragged, i.e. the user only │ │ │ │ + * clicked? Default is true. │ │ │ │ + */ │ │ │ │ + zoomOnClick: true, │ │ │ │ │ │ │ │ - if (oRequest._aborted) { │ │ │ │ - // │ │ │ │ - oRequest.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + this.handler = new OpenLayers.Handler.Box(this, { │ │ │ │ + done: this.zoomBox │ │ │ │ + }, { │ │ │ │ + keyMask: this.keyMask │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Return │ │ │ │ - return; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Method: zoomBox │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * position - {<OpenLayers.Bounds>} or {<OpenLayers.Pixel>} │ │ │ │ + */ │ │ │ │ + zoomBox: function(position) { │ │ │ │ + if (position instanceof OpenLayers.Bounds) { │ │ │ │ + var bounds, │ │ │ │ + targetCenterPx = position.getCenterPixel(); │ │ │ │ + if (!this.out) { │ │ │ │ + var minXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.left, │ │ │ │ + y: position.bottom │ │ │ │ + }); │ │ │ │ + var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.right, │ │ │ │ + y: position.top │ │ │ │ + }); │ │ │ │ + bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, │ │ │ │ + maxXY.lon, maxXY.lat); │ │ │ │ + } else { │ │ │ │ + var pixWidth = position.right - position.left; │ │ │ │ + var pixHeight = position.bottom - position.top; │ │ │ │ + var zoomFactor = Math.min((this.map.size.h / pixHeight), │ │ │ │ + (this.map.size.w / pixWidth)); │ │ │ │ + var extent = this.map.getExtent(); │ │ │ │ + var center = this.map.getLonLatFromPixel(targetCenterPx); │ │ │ │ + var xmin = center.lon - (extent.getWidth() / 2) * zoomFactor; │ │ │ │ + var xmax = center.lon + (extent.getWidth() / 2) * zoomFactor; │ │ │ │ + var ymin = center.lat - (extent.getHeight() / 2) * zoomFactor; │ │ │ │ + var ymax = center.lat + (extent.getHeight() / 2) * zoomFactor; │ │ │ │ + bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax); │ │ │ │ + } │ │ │ │ + // always zoom in/out │ │ │ │ + var lastZoom = this.map.getZoom(), │ │ │ │ + size = this.map.getSize(), │ │ │ │ + centerPx = { │ │ │ │ + x: size.w / 2, │ │ │ │ + y: size.h / 2 │ │ │ │ + }, │ │ │ │ + zoom = this.map.getZoomForExtent(bounds), │ │ │ │ + oldRes = this.map.getResolution(), │ │ │ │ + newRes = this.map.getResolutionForZoom(zoom); │ │ │ │ + if (oldRes == newRes) { │ │ │ │ + this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx)); │ │ │ │ + } else { │ │ │ │ + var zoomOriginPx = { │ │ │ │ + x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / │ │ │ │ + (oldRes - newRes), │ │ │ │ + y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / │ │ │ │ + (oldRes - newRes) │ │ │ │ + }; │ │ │ │ + this.map.zoomTo(zoom, zoomOriginPx); │ │ │ │ + } │ │ │ │ + if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) { │ │ │ │ + this.map.zoomTo(lastZoom + (this.out ? -1 : 1)); │ │ │ │ + } │ │ │ │ + } else if (this.zoomOnClick) { // it's a pixel │ │ │ │ + if (!this.out) { │ │ │ │ + this.map.zoomTo(this.map.getZoom() + 1, position); │ │ │ │ + } else { │ │ │ │ + this.map.zoomTo(this.map.getZoom() - 1, position); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (oRequest.readyState == cXMLHttpRequest.DONE) { │ │ │ │ - // Clean Object │ │ │ │ - fCleanTransport(oRequest); │ │ │ │ + CLASS_NAME: "OpenLayers.Control.ZoomBox" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/DragPan.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - // get cached request │ │ │ │ - if (oRequest.status == 304) │ │ │ │ - oRequest._object = oRequest._cached; │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - // │ │ │ │ - delete oRequest._cached; │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Handler/Drag.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - // │ │ │ │ - fSynchronizeValues(oRequest); │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.DragPan │ │ │ │ + * The DragPan control pans the map with a drag of the mouse. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - // │ │ │ │ - fReadyStateChange(oRequest); │ │ │ │ + /** │ │ │ │ + * Property: type │ │ │ │ + * {OpenLayers.Control.TYPES} │ │ │ │ + */ │ │ │ │ + type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ │ │ │ │ - // BUGFIX: IE - memory leak in interrupted │ │ │ │ - if (bIE && bAsync) │ │ │ │ - window.detachEvent("onunload", fOnUnload); │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - oRequest._object.send(null); │ │ │ │ + /** │ │ │ │ + * Property: panned │ │ │ │ + * {Boolean} The map moved. │ │ │ │ + */ │ │ │ │ + panned: false, │ │ │ │ │ │ │ │ - // Return now - wait until re-sent request is finished │ │ │ │ - return; │ │ │ │ - }; │ │ │ │ - */ │ │ │ │ - // BUGFIX: IE - memory leak in interrupted │ │ │ │ - if (bIE && bAsync) │ │ │ │ - window.detachEvent("onunload", fOnUnload); │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * Property: interval │ │ │ │ + * {Integer} The number of milliseconds that should ellapse before │ │ │ │ + * panning the map again. Defaults to 0 milliseconds, which means that │ │ │ │ + * no separate cycle is used for panning. In most cases you won't want │ │ │ │ + * to change this value. For slow machines/devices larger values can be │ │ │ │ + * tried out. │ │ │ │ + */ │ │ │ │ + interval: 0, │ │ │ │ │ │ │ │ - // BUGFIX: Some browsers (Internet Explorer, Gecko) fire OPEN readystate twice │ │ │ │ - if (nState != oRequest.readyState) │ │ │ │ - fReadyStateChange(oRequest); │ │ │ │ + /** │ │ │ │ + * APIProperty: documentDrag │ │ │ │ + * {Boolean} If set to true, mouse dragging will continue even if the │ │ │ │ + * mouse cursor leaves the map viewport. Default is false. │ │ │ │ + */ │ │ │ │ + documentDrag: false, │ │ │ │ │ │ │ │ - nState = oRequest.readyState; │ │ │ │ - } │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Property: kinetic │ │ │ │ + * {<OpenLayers.Kinetic>} The OpenLayers.Kinetic object. │ │ │ │ + */ │ │ │ │ + kinetic: null, │ │ │ │ │ │ │ │ - function fXMLHttpRequest_send(oRequest) { │ │ │ │ - oRequest._object.send(oRequest._data); │ │ │ │ + /** │ │ │ │ + * APIProperty: enableKinetic │ │ │ │ + * {Boolean} Set this option to enable "kinetic dragging". Can be │ │ │ │ + * set to true or to an object. If set to an object this │ │ │ │ + * object will be passed to the {<OpenLayers.Kinetic>} │ │ │ │ + * constructor. Defaults to true. │ │ │ │ + * To get kinetic dragging, ensure that OpenLayers/Kinetic.js is │ │ │ │ + * included in your build config. │ │ │ │ + */ │ │ │ │ + enableKinetic: true, │ │ │ │ │ │ │ │ - // BUGFIX: Gecko - missing readystatechange calls in synchronous requests │ │ │ │ - if (bGecko && !oRequest._async) { │ │ │ │ - oRequest.readyState = cXMLHttpRequest.OPENED; │ │ │ │ + /** │ │ │ │ + * APIProperty: kineticInterval │ │ │ │ + * {Integer} Interval in milliseconds between 2 steps in the "kinetic │ │ │ │ + * scrolling". Applies only if enableKinetic is set. Defaults │ │ │ │ + * to 10 milliseconds. │ │ │ │ + */ │ │ │ │ + kineticInterval: 10, │ │ │ │ │ │ │ │ - // Synchronize state │ │ │ │ - fSynchronizeValues(oRequest); │ │ │ │ │ │ │ │ - // Simulate missing states │ │ │ │ - while (oRequest.readyState < cXMLHttpRequest.DONE) { │ │ │ │ - oRequest.readyState++; │ │ │ │ - fReadyStateChange(oRequest); │ │ │ │ - // Check if we are aborted │ │ │ │ - if (oRequest._aborted) │ │ │ │ - return; │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * Creates a Drag handler, using <panMap> and │ │ │ │ + * <panMapDone> as callbacks. │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + if (this.enableKinetic && OpenLayers.Kinetic) { │ │ │ │ + var config = { │ │ │ │ + interval: this.kineticInterval │ │ │ │ + }; │ │ │ │ + if (typeof this.enableKinetic === "object") { │ │ │ │ + config = OpenLayers.Util.extend(config, this.enableKinetic); │ │ │ │ } │ │ │ │ + this.kinetic = new OpenLayers.Kinetic(config); │ │ │ │ } │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.send = function(vData) { │ │ │ │ - // Add method sniffer │ │ │ │ - if (cXMLHttpRequest.onsend) │ │ │ │ - cXMLHttpRequest.onsend.apply(this, arguments); │ │ │ │ + this.handler = new OpenLayers.Handler.Drag(this, { │ │ │ │ + "move": this.panMap, │ │ │ │ + "done": this.panMapDone, │ │ │ │ + "down": this.panMapStart │ │ │ │ + }, { │ │ │ │ + interval: this.interval, │ │ │ │ + documentDrag: this.documentDrag │ │ │ │ + }); │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (!arguments.length) │ │ │ │ - vData = null; │ │ │ │ + /** │ │ │ │ + * Method: panMapStart │ │ │ │ + */ │ │ │ │ + panMapStart: function() { │ │ │ │ + if (this.kinetic) { │ │ │ │ + this.kinetic.begin(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // BUGFIX: Safari - fails sending documents created/modified dynamically, so an explicit serialization required │ │ │ │ - // BUGFIX: IE - rewrites any custom mime-type to "text/xml" in case an XMLNode is sent │ │ │ │ - // BUGFIX: Gecko - fails sending Element (this is up to the implementation either to standard) │ │ │ │ - if (vData && vData.nodeType) { │ │ │ │ - vData = window.XMLSerializer ? new window.XMLSerializer().serializeToString(vData) : vData.xml; │ │ │ │ - if (!this._headers["Content-Type"]) │ │ │ │ - this._object.setRequestHeader("Content-Type", "application/xml"); │ │ │ │ + /** │ │ │ │ + * Method: panMap │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * xy - {<OpenLayers.Pixel>} Pixel of the mouse position │ │ │ │ + */ │ │ │ │ + panMap: function(xy) { │ │ │ │ + if (this.kinetic) { │ │ │ │ + this.kinetic.update(xy); │ │ │ │ } │ │ │ │ + this.panned = true; │ │ │ │ + this.map.pan( │ │ │ │ + this.handler.last.x - xy.x, │ │ │ │ + this.handler.last.y - xy.y, { │ │ │ │ + dragging: true, │ │ │ │ + animate: false │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ - this._data = vData; │ │ │ │ - /* │ │ │ │ - // Add to queue │ │ │ │ - if (this._async) │ │ │ │ - fQueue_add(this); │ │ │ │ - else*/ │ │ │ │ - fXMLHttpRequest_send(this); │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.abort = function() { │ │ │ │ - // Add method sniffer │ │ │ │ - if (cXMLHttpRequest.onabort) │ │ │ │ - cXMLHttpRequest.onabort.apply(this, arguments); │ │ │ │ + /** │ │ │ │ + * Method: panMapDone │ │ │ │ + * Finish the panning operation. Only call setCenter (through <panMap>) │ │ │ │ + * if the map has actually been moved. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * xy - {<OpenLayers.Pixel>} Pixel of the mouse position │ │ │ │ + */ │ │ │ │ + panMapDone: function(xy) { │ │ │ │ + if (this.panned) { │ │ │ │ + var res = null; │ │ │ │ + if (this.kinetic) { │ │ │ │ + res = this.kinetic.end(xy); │ │ │ │ + } │ │ │ │ + this.map.pan( │ │ │ │ + this.handler.last.x - xy.x, │ │ │ │ + this.handler.last.y - xy.y, { │ │ │ │ + dragging: !!res, │ │ │ │ + animate: false │ │ │ │ + } │ │ │ │ + ); │ │ │ │ + if (res) { │ │ │ │ + var self = this; │ │ │ │ + this.kinetic.move(res, function(x, y, end) { │ │ │ │ + self.map.pan(x, y, { │ │ │ │ + dragging: !end, │ │ │ │ + animate: false │ │ │ │ + }); │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + this.panned = false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // BUGFIX: Gecko - unnecessary DONE when aborting │ │ │ │ - if (this.readyState > cXMLHttpRequest.UNSENT) │ │ │ │ - this._aborted = true; │ │ │ │ + CLASS_NAME: "OpenLayers.Control.DragPan" │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Handler/MouseWheel.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - this._object.abort(); │ │ │ │ +/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ + * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ + * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ + * full text of the license. */ │ │ │ │ │ │ │ │ - // BUGFIX: IE - memory leak │ │ │ │ - fCleanTransport(this); │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Handler.js │ │ │ │ + */ │ │ │ │ │ │ │ │ - this.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Handler.MouseWheel │ │ │ │ + * Handler for wheel up/down events. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Handler> │ │ │ │ + */ │ │ │ │ +OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + /** │ │ │ │ + * Property: wheelListener │ │ │ │ + * {function} │ │ │ │ + */ │ │ │ │ + wheelListener: null, │ │ │ │ │ │ │ │ - delete this._data; │ │ │ │ - /* if (this._async) │ │ │ │ - fQueue_remove(this);*/ │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.getAllResponseHeaders = function() { │ │ │ │ - return this._object.getAllResponseHeaders(); │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.getResponseHeader = function(sName) { │ │ │ │ - return this._object.getResponseHeader(sName); │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) { │ │ │ │ - // BUGFIX: IE - cache issue │ │ │ │ - if (!this._headers) │ │ │ │ - this._headers = {}; │ │ │ │ - this._headers[sName] = sValue; │ │ │ │ + /** │ │ │ │ + * Property: interval │ │ │ │ + * {Integer} In order to increase server performance, an interval (in │ │ │ │ + * milliseconds) can be set to reduce the number of up/down events │ │ │ │ + * called. If set, a new up/down event will not be set until the │ │ │ │ + * interval has passed. │ │ │ │ + * Defaults to 0, meaning no interval. │ │ │ │ + */ │ │ │ │ + interval: 0, │ │ │ │ │ │ │ │ - return this._object.setRequestHeader(sName, sValue); │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Property: maxDelta │ │ │ │ + * {Integer} Maximum delta to collect before breaking from the current │ │ │ │ + * interval. In cumulative mode, this also limits the maximum delta │ │ │ │ + * returned from the handler. Default is Number.POSITIVE_INFINITY. │ │ │ │ + */ │ │ │ │ + maxDelta: Number.POSITIVE_INFINITY, │ │ │ │ │ │ │ │ - // EventTarget interface implementation │ │ │ │ - cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) { │ │ │ │ - for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) │ │ │ │ - if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) │ │ │ │ - return; │ │ │ │ - // Add listener │ │ │ │ - this._listeners.push([sName, fHandler, bUseCapture]); │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Property: delta │ │ │ │ + * {Integer} When interval is set, delta collects the mousewheel z-deltas │ │ │ │ + * of the events that occur within the interval. │ │ │ │ + * See also the cumulative option │ │ │ │ + */ │ │ │ │ + delta: 0, │ │ │ │ │ │ │ │ - cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) { │ │ │ │ - for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) │ │ │ │ - if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) │ │ │ │ - break; │ │ │ │ - // Remove listener │ │ │ │ - if (oListener) │ │ │ │ - this._listeners.splice(nIndex, 1); │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Property: cumulative │ │ │ │ + * {Boolean} When interval is set: true to collect all the mousewheel │ │ │ │ + * z-deltas, false to only record the delta direction (positive or │ │ │ │ + * negative) │ │ │ │ + */ │ │ │ │ + cumulative: true, │ │ │ │ │ │ │ │ - cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) { │ │ │ │ - var oEventPseudo = { │ │ │ │ - 'type': oEvent.type, │ │ │ │ - 'target': this, │ │ │ │ - 'currentTarget': this, │ │ │ │ - 'eventPhase': 2, │ │ │ │ - 'bubbles': oEvent.bubbles, │ │ │ │ - 'cancelable': oEvent.cancelable, │ │ │ │ - 'timeStamp': oEvent.timeStamp, │ │ │ │ - 'stopPropagation': function() {}, // There is no flow │ │ │ │ - 'preventDefault': function() {}, // There is no default action │ │ │ │ - 'initEvent': function() {} // Original event object should be initialized │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Handler.MouseWheel │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * control - {<OpenLayers.Control>} │ │ │ │ + * callbacks - {Object} An object containing a single function to be │ │ │ │ + * called when the drag operation is finished. │ │ │ │ + * The callback should expect to recieve a single │ │ │ │ + * argument, the point geometry. │ │ │ │ + * options - {Object} │ │ │ │ + */ │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ + this.wheelListener = OpenLayers.Function.bindAsEventListener( │ │ │ │ + this.onWheelEvent, this │ │ │ │ + ); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Execute onreadystatechange │ │ │ │ - if (oEventPseudo.type == "readystatechange" && this.onreadystatechange) │ │ │ │ - (this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]); │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ + this.wheelListener = null; │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Execute listeners │ │ │ │ - for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) │ │ │ │ - if (oListener[0] == oEventPseudo.type && !oListener[2]) │ │ │ │ - (oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]); │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/ │ │ │ │ + */ │ │ │ │ │ │ │ │ - // │ │ │ │ - cXMLHttpRequest.prototype.toString = function() { │ │ │ │ - return '[' + "object" + ' ' + "XMLHttpRequest" + ']'; │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * Method: onWheelEvent │ │ │ │ + * Catch the wheel event and handle it xbrowserly │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * e - {Event} │ │ │ │ + */ │ │ │ │ + onWheelEvent: function(e) { │ │ │ │ │ │ │ │ - cXMLHttpRequest.toString = function() { │ │ │ │ - return '[' + "XMLHttpRequest" + ']'; │ │ │ │ - }; │ │ │ │ + // make sure we have a map and check keyboard modifiers │ │ │ │ + if (!this.map || !this.checkModifiers(e)) { │ │ │ │ + return; │ │ │ │ + } │ │ │ │ │ │ │ │ - // Helper function │ │ │ │ - function fReadyStateChange(oRequest) { │ │ │ │ - // Sniffing code │ │ │ │ - if (cXMLHttpRequest.onreadystatechange) │ │ │ │ - cXMLHttpRequest.onreadystatechange.apply(oRequest); │ │ │ │ + // Ride up the element's DOM hierarchy to determine if it or any of │ │ │ │ + // its ancestors was: │ │ │ │ + // * specifically marked as scrollable (CSS overflow property) │ │ │ │ + // * one of our layer divs or a div marked as scrollable │ │ │ │ + // ('olScrollable' CSS class) │ │ │ │ + // * the map div │ │ │ │ + // │ │ │ │ + var overScrollableDiv = false; │ │ │ │ + var allowScroll = false; │ │ │ │ + var overMapDiv = false; │ │ │ │ │ │ │ │ - // Fake event │ │ │ │ - oRequest.dispatchEvent({ │ │ │ │ - 'type': "readystatechange", │ │ │ │ - 'bubbles': false, │ │ │ │ - 'cancelable': false, │ │ │ │ - 'timeStamp': new Date + 0 │ │ │ │ - }); │ │ │ │ - }; │ │ │ │ + var elem = OpenLayers.Event.element(e); │ │ │ │ + while ((elem != null) && !overMapDiv && !overScrollableDiv) { │ │ │ │ │ │ │ │ - function fGetDocument(oRequest) { │ │ │ │ - var oDocument = oRequest.responseXML, │ │ │ │ - sResponse = oRequest.responseText; │ │ │ │ - // Try parsing responseText │ │ │ │ - if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) { │ │ │ │ - oDocument = new window.ActiveXObject("Microsoft.XMLDOM"); │ │ │ │ - oDocument.async = false; │ │ │ │ - oDocument.validateOnParse = false; │ │ │ │ - oDocument.loadXML(sResponse); │ │ │ │ - } │ │ │ │ - // Check if there is no error in document │ │ │ │ - if (oDocument) │ │ │ │ - if ((bIE && oDocument.parseError != 0) || !oDocument.documentElement || (oDocument.documentElement && oDocument.documentElement.tagName == "parsererror")) │ │ │ │ - return null; │ │ │ │ - return oDocument; │ │ │ │ - }; │ │ │ │ + if (!overScrollableDiv) { │ │ │ │ + try { │ │ │ │ + var overflow; │ │ │ │ + if (elem.currentStyle) { │ │ │ │ + overflow = elem.currentStyle["overflow"]; │ │ │ │ + } else { │ │ │ │ + var style = │ │ │ │ + document.defaultView.getComputedStyle(elem, null); │ │ │ │ + overflow = style.getPropertyValue("overflow"); │ │ │ │ + } │ │ │ │ + overScrollableDiv = (overflow && │ │ │ │ + (overflow == "auto") || (overflow == "scroll")); │ │ │ │ + } catch (err) { │ │ │ │ + //sometimes when scrolling in a popup, this causes │ │ │ │ + // obscure browser error │ │ │ │ + } │ │ │ │ + } │ │ │ │ │ │ │ │ - function fSynchronizeValues(oRequest) { │ │ │ │ - try { │ │ │ │ - oRequest.responseText = oRequest._object.responseText; │ │ │ │ - } catch (e) {} │ │ │ │ - try { │ │ │ │ - oRequest.responseXML = fGetDocument(oRequest._object); │ │ │ │ - } catch (e) {} │ │ │ │ - try { │ │ │ │ - oRequest.status = oRequest._object.status; │ │ │ │ - } catch (e) {} │ │ │ │ - try { │ │ │ │ - oRequest.statusText = oRequest._object.statusText; │ │ │ │ - } catch (e) {} │ │ │ │ - }; │ │ │ │ + if (!allowScroll) { │ │ │ │ + allowScroll = OpenLayers.Element.hasClass(elem, 'olScrollable'); │ │ │ │ + if (!allowScroll) { │ │ │ │ + for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ + // Are we in the layer div? Note that we have two cases │ │ │ │ + // here: one is to catch EventPane layers, which have a │ │ │ │ + // pane above the layer (layer.pane) │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + if (elem == layer.div || elem == layer.pane) { │ │ │ │ + allowScroll = true; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + overMapDiv = (elem == this.map.div); │ │ │ │ │ │ │ │ - function fCleanTransport(oRequest) { │ │ │ │ - // BUGFIX: IE - memory leak (on-page leak) │ │ │ │ - oRequest._object.onreadystatechange = new window.Function; │ │ │ │ - }; │ │ │ │ - /* │ │ │ │ - // Queue manager │ │ │ │ - var oQueuePending = {"CRITICAL":[],"HIGH":[],"NORMAL":[],"LOW":[],"LOWEST":[]}, │ │ │ │ - aQueueRunning = []; │ │ │ │ - function fQueue_add(oRequest) { │ │ │ │ - oQueuePending[oRequest.priority in oQueuePending ? oRequest.priority : "NORMAL"].push(oRequest); │ │ │ │ - // │ │ │ │ - setTimeout(fQueue_process); │ │ │ │ - }; │ │ │ │ + elem = elem.parentNode; │ │ │ │ + } │ │ │ │ │ │ │ │ - function fQueue_remove(oRequest) { │ │ │ │ - for (var nIndex = 0, bFound = false; nIndex < aQueueRunning.length; nIndex++) │ │ │ │ - if (bFound) │ │ │ │ - aQueueRunning[nIndex - 1] = aQueueRunning[nIndex]; │ │ │ │ - else │ │ │ │ - if (aQueueRunning[nIndex] == oRequest) │ │ │ │ - bFound = true; │ │ │ │ - if (bFound) │ │ │ │ - aQueueRunning.length--; │ │ │ │ - // │ │ │ │ - setTimeout(fQueue_process); │ │ │ │ - }; │ │ │ │ + // Logic below is the following: │ │ │ │ + // │ │ │ │ + // If we are over a scrollable div or not over the map div: │ │ │ │ + // * do nothing (let the browser handle scrolling) │ │ │ │ + // │ │ │ │ + // otherwise │ │ │ │ + // │ │ │ │ + // If we are over the layer div or a 'olScrollable' div: │ │ │ │ + // * zoom/in out │ │ │ │ + // then │ │ │ │ + // * kill event (so as not to also scroll the page after zooming) │ │ │ │ + // │ │ │ │ + // otherwise │ │ │ │ + // │ │ │ │ + // Kill the event (dont scroll the page if we wheel over the │ │ │ │ + // layerswitcher or the pan/zoom control) │ │ │ │ + // │ │ │ │ + if (!overScrollableDiv && overMapDiv) { │ │ │ │ + if (allowScroll) { │ │ │ │ + var delta = 0; │ │ │ │ │ │ │ │ - function fQueue_process() { │ │ │ │ - if (aQueueRunning.length < 6) { │ │ │ │ - for (var sPriority in oQueuePending) { │ │ │ │ - if (oQueuePending[sPriority].length) { │ │ │ │ - var oRequest = oQueuePending[sPriority][0]; │ │ │ │ - oQueuePending[sPriority] = oQueuePending[sPriority].slice(1); │ │ │ │ - // │ │ │ │ - aQueueRunning.push(oRequest); │ │ │ │ - // Send request │ │ │ │ - fXMLHttpRequest_send(oRequest); │ │ │ │ - break; │ │ │ │ + if (e.wheelDelta) { │ │ │ │ + delta = e.wheelDelta; │ │ │ │ + if (delta % 160 === 0) { │ │ │ │ + // opera have steps of 160 instead of 120 │ │ │ │ + delta = delta * 0.75; │ │ │ │ } │ │ │ │ + delta = delta / 120; │ │ │ │ + } else if (e.detail) { │ │ │ │ + // detail in Firefox on OS X is 1/3 of Windows │ │ │ │ + // so force delta 1 / -1 │ │ │ │ + delta = -(e.detail / Math.abs(e.detail)); │ │ │ │ + } │ │ │ │ + this.delta += delta; │ │ │ │ + │ │ │ │ + window.clearTimeout(this._timeoutId); │ │ │ │ + if (this.interval && Math.abs(this.delta) < this.maxDelta) { │ │ │ │ + // store e because window.event might change during delay │ │ │ │ + var evt = OpenLayers.Util.extend({}, e); │ │ │ │ + this._timeoutId = window.setTimeout( │ │ │ │ + OpenLayers.Function.bind(function() { │ │ │ │ + this.wheelZoom(evt); │ │ │ │ + }, this), │ │ │ │ + this.interval │ │ │ │ + ); │ │ │ │ + } else { │ │ │ │ + this.wheelZoom(e); │ │ │ │ } │ │ │ │ } │ │ │ │ - }; │ │ │ │ - */ │ │ │ │ - // Internet Explorer 5.0 (missing apply) │ │ │ │ - if (!window.Function.prototype.apply) { │ │ │ │ - window.Function.prototype.apply = function(oRequest, oArguments) { │ │ │ │ - if (!oArguments) │ │ │ │ - oArguments = []; │ │ │ │ - oRequest.__func = this; │ │ │ │ - oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]); │ │ │ │ - delete oRequest.__func; │ │ │ │ - }; │ │ │ │ - }; │ │ │ │ + OpenLayers.Event.stop(e); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: wheelZoom │ │ │ │ + * Given the wheel event, we carry out the appropriate zooming in or out, │ │ │ │ + * based on the 'wheelDelta' or 'detail' property of the event. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * e - {Event} │ │ │ │ + */ │ │ │ │ + wheelZoom: function(e) { │ │ │ │ + var delta = this.delta; │ │ │ │ + this.delta = 0; │ │ │ │ + │ │ │ │ + if (delta) { │ │ │ │ + e.xy = this.map.events.getMousePosition(e); │ │ │ │ + if (delta < 0) { │ │ │ │ + this.callback("down", │ │ │ │ + [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]); │ │ │ │ + } else { │ │ │ │ + this.callback("up", │ │ │ │ + [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // Register new object with window │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Request.XMLHttpRequest │ │ │ │ - * Standard-compliant (W3C) cross-browser implementation of the │ │ │ │ - * XMLHttpRequest object. From │ │ │ │ - * http://code.google.com/p/xmlhttprequest/. │ │ │ │ + * Method: activate │ │ │ │ */ │ │ │ │ - if (!OpenLayers.Request) { │ │ │ │ - /** │ │ │ │ - * This allows for OpenLayers/Request.js to be included │ │ │ │ - * before or after this script. │ │ │ │ - */ │ │ │ │ - OpenLayers.Request = {}; │ │ │ │ - } │ │ │ │ - OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest; │ │ │ │ -})(); │ │ │ │ + activate: function(evt) { │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + //register mousewheel events specifically on the window and document │ │ │ │ + var wheelListener = this.wheelListener; │ │ │ │ + OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener); │ │ │ │ + OpenLayers.Event.observe(window, "mousewheel", wheelListener); │ │ │ │ + OpenLayers.Event.observe(document, "mousewheel", wheelListener); │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Method: deactivate │ │ │ │ + */ │ │ │ │ + deactivate: function(evt) { │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + // unregister mousewheel events specifically on the window and document │ │ │ │ + var wheelListener = this.wheelListener; │ │ │ │ + OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener); │ │ │ │ + OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener); │ │ │ │ + OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener); │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.MouseWheel" │ │ │ │ +}); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Protocol/HTTP.js │ │ │ │ + OpenLayers/Handler/Click.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Protocol.js │ │ │ │ - * @requires OpenLayers/Request/XMLHttpRequest.js │ │ │ │ - */ │ │ │ │ - │ │ │ │ -/** │ │ │ │ - * if application uses the query string, for example, for BBOX parameters, │ │ │ │ - * OpenLayers/Format/QueryStringFilter.js should be included in the build config file │ │ │ │ + * @requires OpenLayers/Handler.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Protocol.HTTP │ │ │ │ - * A basic HTTP protocol for vector layers. Create a new instance with the │ │ │ │ - * <OpenLayers.Protocol.HTTP> constructor. │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Handler.Click │ │ │ │ + * A handler for mouse clicks. The intention of this handler is to give │ │ │ │ + * controls more flexibility with handling clicks. Browsers trigger │ │ │ │ + * click events twice for a double-click. In addition, the mousedown, │ │ │ │ + * mousemove, mouseup sequence fires a click event. With this handler, │ │ │ │ + * controls can decide whether to ignore clicks associated with a double │ │ │ │ + * click. By setting a <pixelTolerance>, controls can also ignore clicks │ │ │ │ + * that include a drag. Create a new instance with the │ │ │ │ + * <OpenLayers.Handler.Click> constructor. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Protocol> │ │ │ │ + * - <OpenLayers.Handler> │ │ │ │ */ │ │ │ │ -OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ +OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + /** │ │ │ │ + * APIProperty: delay │ │ │ │ + * {Number} Number of milliseconds between clicks before the event is │ │ │ │ + * considered a double-click. │ │ │ │ + */ │ │ │ │ + delay: 300, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: url │ │ │ │ - * {String} Service URL, read-only, set through the options │ │ │ │ - * passed to constructor. │ │ │ │ + * APIProperty: single │ │ │ │ + * {Boolean} Handle single clicks. Default is true. If false, clicks │ │ │ │ + * will not be reported. If true, single-clicks will be reported. │ │ │ │ */ │ │ │ │ - url: null, │ │ │ │ + single: true, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: headers │ │ │ │ - * {Object} HTTP request headers, read-only, set through the options │ │ │ │ - * passed to the constructor, │ │ │ │ - * Example: {'Content-Type': 'plain/text'} │ │ │ │ + * APIProperty: double │ │ │ │ + * {Boolean} Handle double-clicks. Default is false. │ │ │ │ */ │ │ │ │ - headers: null, │ │ │ │ + 'double': false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: params │ │ │ │ - * {Object} Parameters of GET requests, read-only, set through the options │ │ │ │ - * passed to the constructor, │ │ │ │ - * Example: {'bbox': '5,5,5,5'} │ │ │ │ + * APIProperty: pixelTolerance │ │ │ │ + * {Number} Maximum number of pixels between mouseup and mousedown for an │ │ │ │ + * event to be considered a click. Default is 0. If set to an │ │ │ │ + * integer value, clicks with a drag greater than the value will be │ │ │ │ + * ignored. This property can only be set when the handler is │ │ │ │ + * constructed. │ │ │ │ */ │ │ │ │ - params: null, │ │ │ │ + pixelTolerance: 0, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: callback │ │ │ │ - * {Object} Function to be called when the <read>, <create>, │ │ │ │ - * <update>, <delete> or <commit> operation completes, read-only, │ │ │ │ - * set through the options passed to the constructor. │ │ │ │ + * APIProperty: dblclickTolerance │ │ │ │ + * {Number} Maximum distance in pixels between clicks for a sequence of │ │ │ │ + * events to be considered a double click. Default is 13. If the │ │ │ │ + * distance between two clicks is greater than this value, a double- │ │ │ │ + * click will not be fired. │ │ │ │ */ │ │ │ │ - callback: null, │ │ │ │ + dblclickTolerance: 13, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: scope │ │ │ │ - * {Object} Callback execution scope, read-only, set through the │ │ │ │ - * options passed to the constructor. │ │ │ │ + * APIProperty: stopSingle │ │ │ │ + * {Boolean} Stop other listeners from being notified of clicks. Default │ │ │ │ + * is false. If true, any listeners registered before this one for │ │ │ │ + * click or rightclick events will not be notified. │ │ │ │ */ │ │ │ │ - scope: null, │ │ │ │ + stopSingle: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: readWithPOST │ │ │ │ - * {Boolean} true if read operations are done with POST requests │ │ │ │ - * instead of GET, defaults to false. │ │ │ │ + * APIProperty: stopDouble │ │ │ │ + * {Boolean} Stop other listeners from being notified of double-clicks. │ │ │ │ + * Default is false. If true, any click listeners registered before │ │ │ │ + * this one will not be notified of *any* double-click events. │ │ │ │ + * │ │ │ │ + * The one caveat with stopDouble is that given a map with two click │ │ │ │ + * handlers, one with stopDouble true and the other with stopSingle │ │ │ │ + * true, the stopSingle handler should be activated last to get │ │ │ │ + * uniform cross-browser performance. Since IE triggers one click │ │ │ │ + * with a dblclick and FF triggers two, if a stopSingle handler is │ │ │ │ + * activated first, all it gets in IE is a single click when the │ │ │ │ + * second handler stops propagation on the dblclick. │ │ │ │ */ │ │ │ │ - readWithPOST: false, │ │ │ │ + stopDouble: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: updateWithPOST │ │ │ │ - * {Boolean} true if update operations are done with POST requests │ │ │ │ - * defaults to false. │ │ │ │ + * Property: timerId │ │ │ │ + * {Number} The id of the timeout waiting to clear the <delayedCall>. │ │ │ │ */ │ │ │ │ - updateWithPOST: false, │ │ │ │ + timerId: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: deleteWithPOST │ │ │ │ - * {Boolean} true if delete operations are done with POST requests │ │ │ │ - * defaults to false. │ │ │ │ - * if true, POST data is set to output of format.write(). │ │ │ │ + * Property: down │ │ │ │ + * {Object} Object that store relevant information about the last │ │ │ │ + * mousedown or touchstart. Its 'xy' OpenLayers.Pixel property gives │ │ │ │ + * the average location of the mouse/touch event. Its 'touches' │ │ │ │ + * property records clientX/clientY of each touches. │ │ │ │ */ │ │ │ │ - deleteWithPOST: false, │ │ │ │ + down: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: wildcarded. │ │ │ │ - * {Boolean} If true percent signs are added around values │ │ │ │ - * read from LIKE filters, for example if the protocol │ │ │ │ - * read method is passed a LIKE filter whose property │ │ │ │ - * is "foo" and whose value is "bar" the string │ │ │ │ - * "foo__ilike=%bar%" will be sent in the query string; │ │ │ │ - * defaults to false. │ │ │ │ + * Property: last │ │ │ │ + * {Object} Object that store relevant information about the last │ │ │ │ + * mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives │ │ │ │ + * the average location of the mouse/touch event. Its 'touches' │ │ │ │ + * property records clientX/clientY of each touches. │ │ │ │ */ │ │ │ │ - wildcarded: false, │ │ │ │ + last: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: first │ │ │ │ + * {Object} When waiting for double clicks, this object will store │ │ │ │ + * information about the first click in a two click sequence. │ │ │ │ + */ │ │ │ │ + first: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: srsInBBOX │ │ │ │ - * {Boolean} Include the SRS identifier in BBOX query string parameter. │ │ │ │ - * Default is false. If true and the layer has a projection object set, │ │ │ │ - * any BBOX filter will be serialized with a fifth item identifying the │ │ │ │ - * projection. E.g. bbox=-1000,-1000,1000,1000,EPSG:900913 │ │ │ │ + * Property: rightclickTimerId │ │ │ │ + * {Number} The id of the right mouse timeout waiting to clear the │ │ │ │ + * <delayedEvent>. │ │ │ │ */ │ │ │ │ - srsInBBOX: false, │ │ │ │ + rightclickTimerId: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Constructor: OpenLayers.Protocol.HTTP │ │ │ │ - * A class for giving layers generic HTTP protocol. │ │ │ │ - * │ │ │ │ + * Constructor: OpenLayers.Handler.Click │ │ │ │ + * Create a new click handler. │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ + * control - {<OpenLayers.Control>} The control that is making use of │ │ │ │ + * this handler. If a handler is being used without a control, the │ │ │ │ + * handler's setMap method must be overridden to deal properly with │ │ │ │ + * the map. │ │ │ │ + * callbacks - {Object} An object with keys corresponding to callbacks │ │ │ │ + * that will be called by the handler. The callbacks should │ │ │ │ + * expect to recieve a single argument, the click event. │ │ │ │ + * Callbacks for 'click' and 'dblclick' are supported. │ │ │ │ * options - {Object} Optional object whose properties will be set on the │ │ │ │ - * instance. │ │ │ │ - * │ │ │ │ - * Valid options include: │ │ │ │ - * url - {String} │ │ │ │ - * headers - {Object} │ │ │ │ - * params - {Object} URL parameters for GET requests │ │ │ │ - * format - {<OpenLayers.Format>} │ │ │ │ - * callback - {Function} │ │ │ │ - * scope - {Object} │ │ │ │ + * handler. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - this.params = {}; │ │ │ │ - this.headers = {}; │ │ │ │ - OpenLayers.Protocol.prototype.initialize.apply(this, arguments); │ │ │ │ - │ │ │ │ - if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { │ │ │ │ - var format = new OpenLayers.Format.QueryStringFilter({ │ │ │ │ - wildcarded: this.wildcarded, │ │ │ │ - srsInBBOX: this.srsInBBOX │ │ │ │ - }); │ │ │ │ - this.filterToParams = function(filter, params) { │ │ │ │ - return format.write(filter, params); │ │ │ │ - }; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Clean up the protocol. │ │ │ │ + * Method: touchstart │ │ │ │ + * Handle touchstart. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Continue propagating this event. │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.params = null; │ │ │ │ - this.headers = null; │ │ │ │ - OpenLayers.Protocol.prototype.destroy.apply(this); │ │ │ │ + touchstart: function(evt) { │ │ │ │ + this.startTouch(); │ │ │ │ + this.down = this.getEventInfo(evt); │ │ │ │ + this.last = this.getEventInfo(evt); │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: filterToParams │ │ │ │ - * Optional method to translate an <OpenLayers.Filter> object into an object │ │ │ │ - * that can be serialized as request query string provided. If a custom │ │ │ │ - * method is not provided, the filter will be serialized using the │ │ │ │ - * <OpenLayers.Format.QueryStringFilter> class. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * filter - {<OpenLayers.Filter>} filter to convert. │ │ │ │ - * params - {Object} The parameters object. │ │ │ │ + * Method: touchmove │ │ │ │ + * Store position of last move, because touchend event can have │ │ │ │ + * an empty "touches" property. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Object} The resulting parameters object. │ │ │ │ + * {Boolean} Continue propagating this event. │ │ │ │ */ │ │ │ │ + touchmove: function(evt) { │ │ │ │ + this.last = this.getEventInfo(evt); │ │ │ │ + return true; │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: read │ │ │ │ - * Construct a request for reading new features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * url - {String} Url for the request. │ │ │ │ - * params - {Object} Parameters to get serialized as a query string. │ │ │ │ - * headers - {Object} Headers to be set on the request. │ │ │ │ - * filter - {<OpenLayers.Filter>} Filter to get serialized as a │ │ │ │ - * query string. │ │ │ │ - * readWithPOST - {Boolean} If the request should be done with POST. │ │ │ │ + * Method: touchend │ │ │ │ + * Correctly set event xy property, and add lastTouches to have │ │ │ │ + * touches property from last touchstart or touchmove │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property │ │ │ │ - * references the HTTP request, this object is also passed to the │ │ │ │ - * callback function when the request completes, its "features" property │ │ │ │ - * is then populated with the features received from the server. │ │ │ │ + * {Boolean} Continue propagating this event. │ │ │ │ */ │ │ │ │ - read: function(options) { │ │ │ │ - OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ - options = options || {}; │ │ │ │ - options.params = OpenLayers.Util.applyDefaults( │ │ │ │ - options.params, this.options.params); │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - if (options.filter && this.filterToParams) { │ │ │ │ - options.params = this.filterToParams( │ │ │ │ - options.filter, options.params │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - var readWithPOST = (options.readWithPOST !== undefined) ? │ │ │ │ - options.readWithPOST : this.readWithPOST; │ │ │ │ - var resp = new OpenLayers.Protocol.Response({ │ │ │ │ - requestType: "read" │ │ │ │ - }); │ │ │ │ - if (readWithPOST) { │ │ │ │ - var headers = options.headers || {}; │ │ │ │ - headers["Content-Type"] = "application/x-www-form-urlencoded"; │ │ │ │ - resp.priv = OpenLayers.Request.POST({ │ │ │ │ - url: options.url, │ │ │ │ - callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ - data: OpenLayers.Util.getParameterString(options.params), │ │ │ │ - headers: headers │ │ │ │ - }); │ │ │ │ - } else { │ │ │ │ - resp.priv = OpenLayers.Request.GET({ │ │ │ │ - url: options.url, │ │ │ │ - callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ - params: options.params, │ │ │ │ - headers: options.headers │ │ │ │ - }); │ │ │ │ + touchend: function(evt) { │ │ │ │ + // touchstart may not have been allowed to propagate │ │ │ │ + if (this.down) { │ │ │ │ + evt.xy = this.last.xy; │ │ │ │ + evt.lastTouches = this.last.touches; │ │ │ │ + this.handleSingle(evt); │ │ │ │ + this.down = null; │ │ │ │ } │ │ │ │ - return resp; │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleRead │ │ │ │ - * Individual callbacks are created for read, create and update, should │ │ │ │ - * a subclass need to override each one separately. │ │ │ │ + * Method: mousedown │ │ │ │ + * Handle mousedown. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ - * the user callback. │ │ │ │ - * options - {Object} The user options passed to the read call. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Continue propagating this event. │ │ │ │ */ │ │ │ │ - handleRead: function(resp, options) { │ │ │ │ - this.handleResponse(resp, options); │ │ │ │ + mousedown: function(evt) { │ │ │ │ + this.down = this.getEventInfo(evt); │ │ │ │ + this.last = this.getEventInfo(evt); │ │ │ │ + return true; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: create │ │ │ │ - * Construct a request for writing newly created features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ - * {<OpenLayers.Feature.Vector>} │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ + * Method: mouseup │ │ │ │ + * Handle mouseup. Installed to support collection of right mouse events. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ - * object, whose "priv" property references the HTTP request, this │ │ │ │ - * object is also passed to the callback function when the request │ │ │ │ - * completes, its "features" property is then populated with the │ │ │ │ - * the features received from the server. │ │ │ │ + * {Boolean} Continue propagating this event. │ │ │ │ */ │ │ │ │ - create: function(features, options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - │ │ │ │ - var resp = new OpenLayers.Protocol.Response({ │ │ │ │ - reqFeatures: features, │ │ │ │ - requestType: "create" │ │ │ │ - }); │ │ │ │ + mouseup: function(evt) { │ │ │ │ + var propagate = true; │ │ │ │ │ │ │ │ - resp.priv = OpenLayers.Request.POST({ │ │ │ │ - url: options.url, │ │ │ │ - callback: this.createCallback(this.handleCreate, resp, options), │ │ │ │ - headers: options.headers, │ │ │ │ - data: this.format.write(features) │ │ │ │ - }); │ │ │ │ + // Collect right mouse clicks from the mouseup │ │ │ │ + // IE - ignores the second right click in mousedown so using │ │ │ │ + // mouseup instead │ │ │ │ + if (this.checkModifiers(evt) && this.control.handleRightClicks && │ │ │ │ + OpenLayers.Event.isRightClick(evt)) { │ │ │ │ + propagate = this.rightclick(evt); │ │ │ │ + } │ │ │ │ │ │ │ │ - return resp; │ │ │ │ + return propagate; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleCreate │ │ │ │ - * Called the the request issued by <create> is complete. May be overridden │ │ │ │ - * by subclasses. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ - * any user callback. │ │ │ │ - * options - {Object} The user options passed to the create call. │ │ │ │ + * Method: rightclick │ │ │ │ + * Handle rightclick. For a dblrightclick, we get two clicks so we need │ │ │ │ + * to always register for dblrightclick to properly handle single │ │ │ │ + * clicks. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Continue propagating this event. │ │ │ │ */ │ │ │ │ - handleCreate: function(resp, options) { │ │ │ │ - this.handleResponse(resp, options); │ │ │ │ + rightclick: function(evt) { │ │ │ │ + if (this.passesTolerance(evt)) { │ │ │ │ + if (this.rightclickTimerId != null) { │ │ │ │ + //Second click received before timeout this must be │ │ │ │ + // a double click │ │ │ │ + this.clearTimer(); │ │ │ │ + this.callback('dblrightclick', [evt]); │ │ │ │ + return !this.stopDouble; │ │ │ │ + } else { │ │ │ │ + //Set the rightclickTimerId, send evt only if double is │ │ │ │ + // true else trigger single │ │ │ │ + var clickEvent = this['double'] ? │ │ │ │ + OpenLayers.Util.extend({}, evt) : │ │ │ │ + this.callback('rightclick', [evt]); │ │ │ │ + │ │ │ │ + var delayedRightCall = OpenLayers.Function.bind( │ │ │ │ + this.delayedRightCall, │ │ │ │ + this, │ │ │ │ + clickEvent │ │ │ │ + ); │ │ │ │ + this.rightclickTimerId = window.setTimeout( │ │ │ │ + delayedRightCall, this.delay │ │ │ │ + ); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return !this.stopSingle; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: update │ │ │ │ - * Construct a request updating modified feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ - * object, whose "priv" property references the HTTP request, this │ │ │ │ - * object is also passed to the callback function when the request │ │ │ │ - * completes, its "features" property is then populated with the │ │ │ │ - * the feature received from the server. │ │ │ │ + * Method: delayedRightCall │ │ │ │ + * Sets <rightclickTimerId> to null. And optionally triggers the │ │ │ │ + * rightclick callback if evt is set. │ │ │ │ */ │ │ │ │ - update: function(feature, options) { │ │ │ │ - options = options || {}; │ │ │ │ - var url = options.url || │ │ │ │ - feature.url || │ │ │ │ - this.options.url + "/" + feature.fid; │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - │ │ │ │ - var resp = new OpenLayers.Protocol.Response({ │ │ │ │ - reqFeatures: feature, │ │ │ │ - requestType: "update" │ │ │ │ - }); │ │ │ │ - │ │ │ │ - var method = this.updateWithPOST ? "POST" : "PUT"; │ │ │ │ - resp.priv = OpenLayers.Request[method]({ │ │ │ │ - url: url, │ │ │ │ - callback: this.createCallback(this.handleUpdate, resp, options), │ │ │ │ - headers: options.headers, │ │ │ │ - data: this.format.write(feature) │ │ │ │ - }); │ │ │ │ - │ │ │ │ - return resp; │ │ │ │ + delayedRightCall: function(evt) { │ │ │ │ + this.rightclickTimerId = null; │ │ │ │ + if (evt) { │ │ │ │ + this.callback('rightclick', [evt]); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleUpdate │ │ │ │ - * Called the the request issued by <update> is complete. May be overridden │ │ │ │ - * by subclasses. │ │ │ │ + * Method: click │ │ │ │ + * Handle click events from the browser. This is registered as a listener │ │ │ │ + * for click events and should not be called from other events in this │ │ │ │ + * handler. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ - * any user callback. │ │ │ │ - * options - {Object} The user options passed to the update call. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} Continue propagating this event. │ │ │ │ */ │ │ │ │ - handleUpdate: function(resp, options) { │ │ │ │ - this.handleResponse(resp, options); │ │ │ │ + click: function(evt) { │ │ │ │ + if (!this.last) { │ │ │ │ + this.last = this.getEventInfo(evt); │ │ │ │ + } │ │ │ │ + this.handleSingle(evt); │ │ │ │ + return !this.stopSingle; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: delete │ │ │ │ - * Construct a request deleting a removed feature. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * feature - {<OpenLayers.Feature.Vector>} │ │ │ │ - * options - {Object} Optional object for configuring the request. │ │ │ │ - * This object is modified and should not be reused. │ │ │ │ - * │ │ │ │ + * Method: dblclick │ │ │ │ + * Handle dblclick. For a dblclick, we get two clicks in some browsers │ │ │ │ + * (FF) and one in others (IE). So we need to always register for │ │ │ │ + * dblclick to properly handle single clicks. This method is registered │ │ │ │ + * as a listener for the dblclick browser event. It should *not* be │ │ │ │ + * called by other methods in this handler. │ │ │ │ + * │ │ │ │ * Returns: │ │ │ │ - * {<OpenLayers.Protocol.Response>} An <OpenLayers.Protocol.Response> │ │ │ │ - * object, whose "priv" property references the HTTP request, this │ │ │ │ - * object is also passed to the callback function when the request │ │ │ │ - * completes. │ │ │ │ + * {Boolean} Continue propagating this event. │ │ │ │ */ │ │ │ │ - "delete": function(feature, options) { │ │ │ │ - options = options || {}; │ │ │ │ - var url = options.url || │ │ │ │ - feature.url || │ │ │ │ - this.options.url + "/" + feature.fid; │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - │ │ │ │ - var resp = new OpenLayers.Protocol.Response({ │ │ │ │ - reqFeatures: feature, │ │ │ │ - requestType: "delete" │ │ │ │ - }); │ │ │ │ + dblclick: function(evt) { │ │ │ │ + this.handleDouble(evt); │ │ │ │ + return !this.stopDouble; │ │ │ │ + }, │ │ │ │ │ │ │ │ - var method = this.deleteWithPOST ? "POST" : "DELETE"; │ │ │ │ - var requestOptions = { │ │ │ │ - url: url, │ │ │ │ - callback: this.createCallback(this.handleDelete, resp, options), │ │ │ │ - headers: options.headers │ │ │ │ - }; │ │ │ │ - if (this.deleteWithPOST) { │ │ │ │ - requestOptions.data = this.format.write(feature); │ │ │ │ + /** │ │ │ │ + * Method: handleDouble │ │ │ │ + * Handle double-click sequence. │ │ │ │ + */ │ │ │ │ + handleDouble: function(evt) { │ │ │ │ + if (this.passesDblclickTolerance(evt)) { │ │ │ │ + if (this["double"]) { │ │ │ │ + this.callback("dblclick", [evt]); │ │ │ │ + } │ │ │ │ + // to prevent a dblclick from firing the click callback in IE │ │ │ │ + this.clearTimer(); │ │ │ │ } │ │ │ │ - resp.priv = OpenLayers.Request[method](requestOptions); │ │ │ │ + }, │ │ │ │ │ │ │ │ - return resp; │ │ │ │ + /** │ │ │ │ + * Method: handleSingle │ │ │ │ + * Handle single click sequence. │ │ │ │ + */ │ │ │ │ + handleSingle: function(evt) { │ │ │ │ + if (this.passesTolerance(evt)) { │ │ │ │ + if (this.timerId != null) { │ │ │ │ + // already received a click │ │ │ │ + if (this.last.touches && this.last.touches.length === 1) { │ │ │ │ + // touch device, no dblclick event - this may be a double │ │ │ │ + if (this["double"]) { │ │ │ │ + // on Android don't let the browser zoom on the page │ │ │ │ + OpenLayers.Event.preventDefault(evt); │ │ │ │ + } │ │ │ │ + this.handleDouble(evt); │ │ │ │ + } │ │ │ │ + // if we're not in a touch environment we clear the click timer │ │ │ │ + // if we've got a second touch, we'll get two touchend events │ │ │ │ + if (!this.last.touches || this.last.touches.length !== 2) { │ │ │ │ + this.clearTimer(); │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + // remember the first click info so we can compare to the second │ │ │ │ + this.first = this.getEventInfo(evt); │ │ │ │ + // set the timer, send evt only if single is true │ │ │ │ + //use a clone of the event object because it will no longer │ │ │ │ + //be a valid event object in IE in the timer callback │ │ │ │ + var clickEvent = this.single ? │ │ │ │ + OpenLayers.Util.extend({}, evt) : null; │ │ │ │ + this.queuePotentialClick(clickEvent); │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: handleDelete │ │ │ │ - * Called the the request issued by <delete> is complete. May be overridden │ │ │ │ - * by subclasses. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ - * any user callback. │ │ │ │ - * options - {Object} The user options passed to the delete call. │ │ │ │ + /** │ │ │ │ + * Method: queuePotentialClick │ │ │ │ + * This method is separated out largely to make testing easier (so we │ │ │ │ + * don't have to override window.setTimeout) │ │ │ │ */ │ │ │ │ - handleDelete: function(resp, options) { │ │ │ │ - this.handleResponse(resp, options); │ │ │ │ + queuePotentialClick: function(evt) { │ │ │ │ + this.timerId = window.setTimeout( │ │ │ │ + OpenLayers.Function.bind(this.delayedCall, this, evt), │ │ │ │ + this.delay │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: handleResponse │ │ │ │ - * Called by CRUD specific handlers. │ │ │ │ + * Method: passesTolerance │ │ │ │ + * Determine whether the event is within the optional pixel tolerance. Note │ │ │ │ + * that the pixel tolerance check only works if mousedown events get to │ │ │ │ + * the listeners registered here. If they are stopped by other elements, │ │ │ │ + * the <pixelTolerance> will have no effect here (this method will always │ │ │ │ + * return true). │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} The response object to pass to │ │ │ │ - * any user callback. │ │ │ │ - * options - {Object} The user options passed to the create, read, update, │ │ │ │ - * or delete call. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The click is within the pixel tolerance (if specified). │ │ │ │ */ │ │ │ │ - handleResponse: function(resp, options) { │ │ │ │ - var request = resp.priv; │ │ │ │ - if (options.callback) { │ │ │ │ - if (request.status >= 200 && request.status < 300) { │ │ │ │ - // success │ │ │ │ - if (resp.requestType != "delete") { │ │ │ │ - resp.features = this.parseFeatures(request); │ │ │ │ + passesTolerance: function(evt) { │ │ │ │ + var passes = true; │ │ │ │ + if (this.pixelTolerance != null && this.down && this.down.xy) { │ │ │ │ + passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy); │ │ │ │ + // for touch environments, we also enforce that all touches │ │ │ │ + // start and end within the given tolerance to be considered a click │ │ │ │ + if (passes && this.touch && │ │ │ │ + this.down.touches.length === this.last.touches.length) { │ │ │ │ + // the touchend event doesn't come with touches, so we check │ │ │ │ + // down and last │ │ │ │ + for (var i = 0, ii = this.down.touches.length; i < ii; ++i) { │ │ │ │ + if (this.getTouchDistance( │ │ │ │ + this.down.touches[i], │ │ │ │ + this.last.touches[i] │ │ │ │ + ) > this.pixelTolerance) { │ │ │ │ + passes = false; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ } │ │ │ │ - resp.code = OpenLayers.Protocol.Response.SUCCESS; │ │ │ │ - } else { │ │ │ │ - // failure │ │ │ │ - resp.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ } │ │ │ │ - options.callback.call(options.scope, resp); │ │ │ │ } │ │ │ │ + return passes; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: parseFeatures │ │ │ │ - * Read HTTP response body and return features. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * request - {XMLHttpRequest} The request object │ │ │ │ + /** │ │ │ │ + * Method: getTouchDistance │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array({<OpenLayers.Feature.Vector>})} or │ │ │ │ - * {<OpenLayers.Feature.Vector>} Array of features or a single feature. │ │ │ │ + * {Boolean} The pixel displacement between two touches. │ │ │ │ */ │ │ │ │ - parseFeatures: function(request) { │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText; │ │ │ │ - } │ │ │ │ - if (!doc || doc.length <= 0) { │ │ │ │ - return null; │ │ │ │ - } │ │ │ │ - return this.format.read(doc); │ │ │ │ + getTouchDistance: function(from, to) { │ │ │ │ + return Math.sqrt( │ │ │ │ + Math.pow(from.clientX - to.clientX, 2) + │ │ │ │ + Math.pow(from.clientY - to.clientY, 2) │ │ │ │ + ); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: commit │ │ │ │ - * Iterate over each feature and take action based on the feature state. │ │ │ │ - * Possible actions are create, update and delete. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * features - {Array({<OpenLayers.Feature.Vector>})} │ │ │ │ - * options - {Object} Optional object for setting up intermediate commit │ │ │ │ - * callbacks. │ │ │ │ - * │ │ │ │ - * Valid options: │ │ │ │ - * create - {Object} Optional object to be passed to the <create> method. │ │ │ │ - * update - {Object} Optional object to be passed to the <update> method. │ │ │ │ - * delete - {Object} Optional object to be passed to the <delete> method. │ │ │ │ - * callback - {Function} Optional function to be called when the commit │ │ │ │ - * is complete. │ │ │ │ - * scope - {Object} Optional object to be set as the scope of the callback. │ │ │ │ + * Method: passesDblclickTolerance │ │ │ │ + * Determine whether the event is within the optional double-cick pixel │ │ │ │ + * tolerance. │ │ │ │ * │ │ │ │ * Returns: │ │ │ │ - * {Array(<OpenLayers.Protocol.Response>)} An array of response objects, │ │ │ │ - * one per request made to the server, each object's "priv" property │ │ │ │ - * references the corresponding HTTP request. │ │ │ │ + * {Boolean} The click is within the double-click pixel tolerance. │ │ │ │ */ │ │ │ │ - commit: function(features, options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - var resp = [], │ │ │ │ - nResponses = 0; │ │ │ │ - │ │ │ │ - // Divide up features before issuing any requests. This properly │ │ │ │ - // counts requests in the event that any responses come in before │ │ │ │ - // all requests have been issued. │ │ │ │ - var types = {}; │ │ │ │ - types[OpenLayers.State.INSERT] = []; │ │ │ │ - types[OpenLayers.State.UPDATE] = []; │ │ │ │ - types[OpenLayers.State.DELETE] = []; │ │ │ │ - var feature, list, requestFeatures = []; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - feature = features[i]; │ │ │ │ - list = types[feature.state]; │ │ │ │ - if (list) { │ │ │ │ - list.push(feature); │ │ │ │ - requestFeatures.push(feature); │ │ │ │ - } │ │ │ │ + passesDblclickTolerance: function(evt) { │ │ │ │ + var passes = true; │ │ │ │ + if (this.down && this.first) { │ │ │ │ + passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance; │ │ │ │ } │ │ │ │ - // tally up number of requests │ │ │ │ - var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + │ │ │ │ - types[OpenLayers.State.UPDATE].length + │ │ │ │ - types[OpenLayers.State.DELETE].length; │ │ │ │ - │ │ │ │ - // This response will be sent to the final callback after all the others │ │ │ │ - // have been fired. │ │ │ │ - var success = true; │ │ │ │ - var finalResponse = new OpenLayers.Protocol.Response({ │ │ │ │ - reqFeatures: requestFeatures │ │ │ │ - }); │ │ │ │ + return passes; │ │ │ │ + }, │ │ │ │ │ │ │ │ - function insertCallback(response) { │ │ │ │ - var len = response.features ? response.features.length : 0; │ │ │ │ - var fids = new Array(len); │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ - fids[i] = response.features[i].fid; │ │ │ │ - } │ │ │ │ - finalResponse.insertIds = fids; │ │ │ │ - callback.apply(this, [response]); │ │ │ │ + /** │ │ │ │ + * Method: clearTimer │ │ │ │ + * Clear the timer and set <timerId> to null. │ │ │ │ + */ │ │ │ │ + clearTimer: function() { │ │ │ │ + if (this.timerId != null) { │ │ │ │ + window.clearTimeout(this.timerId); │ │ │ │ + this.timerId = null; │ │ │ │ } │ │ │ │ - │ │ │ │ - function callback(response) { │ │ │ │ - this.callUserCallback(response, options); │ │ │ │ - success = success && response.success(); │ │ │ │ - nResponses++; │ │ │ │ - if (nResponses >= nRequests) { │ │ │ │ - if (options.callback) { │ │ │ │ - finalResponse.code = success ? │ │ │ │ - OpenLayers.Protocol.Response.SUCCESS : │ │ │ │ - OpenLayers.Protocol.Response.FAILURE; │ │ │ │ - options.callback.apply(options.scope, [finalResponse]); │ │ │ │ - } │ │ │ │ - } │ │ │ │ + if (this.rightclickTimerId != null) { │ │ │ │ + window.clearTimeout(this.rightclickTimerId); │ │ │ │ + this.rightclickTimerId = null; │ │ │ │ } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // start issuing requests │ │ │ │ - var queue = types[OpenLayers.State.INSERT]; │ │ │ │ - if (queue.length > 0) { │ │ │ │ - resp.push(this.create( │ │ │ │ - queue, OpenLayers.Util.applyDefaults({ │ │ │ │ - callback: insertCallback, │ │ │ │ - scope: this │ │ │ │ - }, options.create) │ │ │ │ - )); │ │ │ │ - } │ │ │ │ - queue = types[OpenLayers.State.UPDATE]; │ │ │ │ - for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ - resp.push(this.update( │ │ │ │ - queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ - callback: callback, │ │ │ │ - scope: this │ │ │ │ - }, options.update))); │ │ │ │ - } │ │ │ │ - queue = types[OpenLayers.State.DELETE]; │ │ │ │ - for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ - resp.push(this["delete"]( │ │ │ │ - queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ - callback: callback, │ │ │ │ - scope: this │ │ │ │ - }, options["delete"]))); │ │ │ │ + /** │ │ │ │ + * Method: delayedCall │ │ │ │ + * Sets <timerId> to null. And optionally triggers the click callback if │ │ │ │ + * evt is set. │ │ │ │ + */ │ │ │ │ + delayedCall: function(evt) { │ │ │ │ + this.timerId = null; │ │ │ │ + if (evt) { │ │ │ │ + this.callback("click", [evt]); │ │ │ │ } │ │ │ │ - return resp; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: abort │ │ │ │ - * Abort an ongoing request, the response object passed to │ │ │ │ - * this method must come from this HTTP protocol (as a result │ │ │ │ - * of a create, read, update, delete or commit operation). │ │ │ │ + * Method: getEventInfo │ │ │ │ + * This method allows us to store event information without storing the │ │ │ │ + * actual event. In touch devices (at least), the same event is │ │ │ │ + * modified between touchstart, touchmove, and touchend. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * response - {<OpenLayers.Protocol.Response>} │ │ │ │ + * Returns: │ │ │ │ + * {Object} An object with event related info. │ │ │ │ */ │ │ │ │ - abort: function(response) { │ │ │ │ - if (response) { │ │ │ │ - response.priv.abort(); │ │ │ │ + getEventInfo: function(evt) { │ │ │ │ + var touches; │ │ │ │ + if (evt.touches) { │ │ │ │ + var len = evt.touches.length; │ │ │ │ + touches = new Array(len); │ │ │ │ + var touch; │ │ │ │ + for (var i = 0; i < len; i++) { │ │ │ │ + touch = evt.touches[i]; │ │ │ │ + touches[i] = { │ │ │ │ + clientX: touch.olClientX, │ │ │ │ + clientY: touch.olClientY │ │ │ │ + }; │ │ │ │ + } │ │ │ │ } │ │ │ │ + return { │ │ │ │ + xy: evt.xy, │ │ │ │ + touches: touches │ │ │ │ + }; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: callUserCallback │ │ │ │ - * This method is used from within the commit method each time an │ │ │ │ - * an HTTP response is received from the server, it is responsible │ │ │ │ - * for calling the user-supplied callbacks. │ │ │ │ + * APIMethod: deactivate │ │ │ │ + * Deactivate the handler. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * resp - {<OpenLayers.Protocol.Response>} │ │ │ │ - * options - {Object} The map of options passed to the commit call. │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The handler was successfully deactivated. │ │ │ │ */ │ │ │ │ - callUserCallback: function(resp, options) { │ │ │ │ - var opt = options[resp.requestType]; │ │ │ │ - if (opt && opt.callback) { │ │ │ │ - opt.callback.call(opt.scope, resp); │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.clearTimer(); │ │ │ │ + this.down = null; │ │ │ │ + this.first = null; │ │ │ │ + this.last = null; │ │ │ │ + deactivated = true; │ │ │ │ } │ │ │ │ + return deactivated; │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.HTTP" │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Click" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Filter/Logical.js │ │ │ │ + OpenLayers/Control/Navigation.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Filter.js │ │ │ │ + * @requires OpenLayers/Control/ZoomBox.js │ │ │ │ + * @requires OpenLayers/Control/DragPan.js │ │ │ │ + * @requires OpenLayers/Handler/MouseWheel.js │ │ │ │ + * @requires OpenLayers/Handler/Click.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Filter.Logical │ │ │ │ - * This class represents ogc:And, ogc:Or and ogc:Not rules. │ │ │ │ + * Class: OpenLayers.Control.Navigation │ │ │ │ + * The navigation control handles map browsing with mouse events (dragging, │ │ │ │ + * double-clicking, and scrolling the wheel). Create a new navigation │ │ │ │ + * control with the <OpenLayers.Control.Navigation> control. │ │ │ │ * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Filter> │ │ │ │ + * Note that this control is added to the map by default (if no controls │ │ │ │ + * array is sent in the options object to the <OpenLayers.Map> │ │ │ │ + * constructor). │ │ │ │ + * │ │ │ │ + * Inherits: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ +OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: dragPan │ │ │ │ + * {<OpenLayers.Control.DragPan>} │ │ │ │ + */ │ │ │ │ + dragPan: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: filters │ │ │ │ - * {Array(<OpenLayers.Filter>)} Child filters for this filter. │ │ │ │ + * APIProperty: dragPanOptions │ │ │ │ + * {Object} Options passed to the DragPan control. │ │ │ │ */ │ │ │ │ - filters: null, │ │ │ │ + dragPanOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: type │ │ │ │ - * {String} type of logical operator. Available types are: │ │ │ │ - * - OpenLayers.Filter.Logical.AND = "&&"; │ │ │ │ - * - OpenLayers.Filter.Logical.OR = "||"; │ │ │ │ - * - OpenLayers.Filter.Logical.NOT = "!"; │ │ │ │ + * Property: pinchZoom │ │ │ │ + * {<OpenLayers.Control.PinchZoom>} │ │ │ │ */ │ │ │ │ - type: null, │ │ │ │ + pinchZoom: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Filter.Logical │ │ │ │ - * Creates a logical filter (And, Or, Not). │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * options - {Object} An optional object with properties to set on the │ │ │ │ - * filter. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Filter.Logical>} │ │ │ │ + /** │ │ │ │ + * APIProperty: pinchZoomOptions │ │ │ │ + * {Object} Options passed to the PinchZoom control. │ │ │ │ */ │ │ │ │ - initialize: function(options) { │ │ │ │ - this.filters = []; │ │ │ │ - OpenLayers.Filter.prototype.initialize.apply(this, [options]); │ │ │ │ - }, │ │ │ │ + pinchZoomOptions: null, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: documentDrag │ │ │ │ + * {Boolean} Allow panning of the map by dragging outside map viewport. │ │ │ │ + * Default is false. │ │ │ │ + */ │ │ │ │ + documentDrag: false, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - * Remove reference to child filters. │ │ │ │ + * Property: zoomBox │ │ │ │ + * {<OpenLayers.Control.ZoomBox>} │ │ │ │ */ │ │ │ │ - destroy: function() { │ │ │ │ - this.filters = null; │ │ │ │ - OpenLayers.Filter.prototype.destroy.apply(this); │ │ │ │ - }, │ │ │ │ + zoomBox: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: evaluate │ │ │ │ - * Evaluates this filter in a specific context. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * context - {Object} Context to use in evaluating the filter. A vector │ │ │ │ - * feature may also be provided to evaluate feature attributes in │ │ │ │ - * comparison filters or geometries in spatial filters. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} The filter applies. │ │ │ │ + * APIProperty: zoomBoxEnabled │ │ │ │ + * {Boolean} Whether the user can draw a box to zoom │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + zoomBoxEnabled: 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; │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomWheelEnabled │ │ │ │ + * {Boolean} Whether the mousewheel should zoom the map │ │ │ │ + */ │ │ │ │ + zoomWheelEnabled: true, │ │ │ │ │ │ │ │ - case OpenLayers.Filter.Logical.NOT: │ │ │ │ - return (!this.filters[0].evaluate(context)); │ │ │ │ - } │ │ │ │ - return undefined; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Property: mouseWheelOptions │ │ │ │ + * {Object} Options passed to the MouseWheel control (only useful if │ │ │ │ + * <zoomWheelEnabled> is set to true). Default is no options for maps │ │ │ │ + * with fractionalZoom set to true, otherwise │ │ │ │ + * {cumulative: false, interval: 50, maxDelta: 6} │ │ │ │ + */ │ │ │ │ + mouseWheelOptions: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Clones this filter. │ │ │ │ + * APIProperty: handleRightClicks │ │ │ │ + * {Boolean} Whether or not to handle right clicks. Default is false. │ │ │ │ + */ │ │ │ │ + handleRightClicks: false, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomBoxKeyMask │ │ │ │ + * {Integer} <OpenLayers.Handler> key code of the key, which has to be │ │ │ │ + * pressed, while drawing the zoom box with the mouse on the screen. │ │ │ │ + * You should probably set handleRightClicks to true if you use this │ │ │ │ + * with MOD_CTRL, to disable the context menu for machines which use │ │ │ │ + * CTRL-Click as a right click. │ │ │ │ + * Default: <OpenLayers.Handler.MOD_SHIFT> │ │ │ │ + */ │ │ │ │ + zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * true. │ │ │ │ + */ │ │ │ │ + autoActivate: true, │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.Navigation │ │ │ │ + * Create a new navigation control │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Filter.Logical>} Clone of this filter. │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be set on │ │ │ │ + * the control │ │ │ │ */ │ │ │ │ - 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 │ │ │ │ - }); │ │ │ │ + initialize: function(options) { │ │ │ │ + this.handlers = {}; │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Filter.Logical" │ │ │ │ -}); │ │ │ │ - │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * The destroy method is used to perform any clean up before the control │ │ │ │ + * is dereferenced. Typically this is where event listeners are removed │ │ │ │ + * to prevent memory leaks. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ │ │ │ │ -OpenLayers.Filter.Logical.AND = "&&"; │ │ │ │ -OpenLayers.Filter.Logical.OR = "||"; │ │ │ │ -OpenLayers.Filter.Logical.NOT = "!"; │ │ │ │ -/* ====================================================================== │ │ │ │ - OpenLayers/Filter/Comparison.js │ │ │ │ - ====================================================================== */ │ │ │ │ + if (this.dragPan) { │ │ │ │ + this.dragPan.destroy(); │ │ │ │ + } │ │ │ │ + this.dragPan = 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 (this.zoomBox) { │ │ │ │ + this.zoomBox.destroy(); │ │ │ │ + } │ │ │ │ + this.zoomBox = null; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * @requires OpenLayers/Filter.js │ │ │ │ - */ │ │ │ │ + if (this.pinchZoom) { │ │ │ │ + this.pinchZoom.destroy(); │ │ │ │ + } │ │ │ │ + this.pinchZoom = null; │ │ │ │ │ │ │ │ -/** │ │ │ │ - * Class: OpenLayers.Filter.Comparison │ │ │ │ - * This class represents a comparison filter. │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Filter> │ │ │ │ - */ │ │ │ │ -OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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"; │ │ │ │ + * Method: activate │ │ │ │ */ │ │ │ │ - type: null, │ │ │ │ + activate: function() { │ │ │ │ + this.dragPan.activate(); │ │ │ │ + if (this.zoomWheelEnabled) { │ │ │ │ + this.handlers.wheel.activate(); │ │ │ │ + } │ │ │ │ + this.handlers.click.activate(); │ │ │ │ + if (this.zoomBoxEnabled) { │ │ │ │ + this.zoomBox.activate(); │ │ │ │ + } │ │ │ │ + if (this.pinchZoom) { │ │ │ │ + this.pinchZoom.activate(); │ │ │ │ + } │ │ │ │ + return OpenLayers.Control.prototype.activate.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: property │ │ │ │ - * {String} │ │ │ │ - * name of the context property to compare │ │ │ │ + * Method: deactivate │ │ │ │ */ │ │ │ │ - property: null, │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.pinchZoom) { │ │ │ │ + this.pinchZoom.deactivate(); │ │ │ │ + } │ │ │ │ + this.zoomBox.deactivate(); │ │ │ │ + this.dragPan.deactivate(); │ │ │ │ + this.handlers.click.deactivate(); │ │ │ │ + this.handlers.wheel.deactivate(); │ │ │ │ + return OpenLayers.Control.prototype.deactivate.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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}" │ │ │ │ + * Method: draw │ │ │ │ */ │ │ │ │ - value: null, │ │ │ │ + draw: function() { │ │ │ │ + // disable right mouse context menu for support of right click events │ │ │ │ + if (this.handleRightClicks) { │ │ │ │ + this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False; │ │ │ │ + } │ │ │ │ + │ │ │ │ + var clickCallbacks = { │ │ │ │ + 'click': this.defaultClick, │ │ │ │ + 'dblclick': this.defaultDblClick, │ │ │ │ + 'dblrightclick': this.defaultDblRightClick │ │ │ │ + }; │ │ │ │ + var clickOptions = { │ │ │ │ + 'double': true, │ │ │ │ + 'stopDouble': true │ │ │ │ + }; │ │ │ │ + this.handlers.click = new OpenLayers.Handler.Click( │ │ │ │ + this, clickCallbacks, clickOptions │ │ │ │ + ); │ │ │ │ + this.dragPan = new OpenLayers.Control.DragPan( │ │ │ │ + OpenLayers.Util.extend({ │ │ │ │ + map: this.map, │ │ │ │ + documentDrag: this.documentDrag │ │ │ │ + }, this.dragPanOptions) │ │ │ │ + ); │ │ │ │ + this.zoomBox = new OpenLayers.Control.ZoomBox({ │ │ │ │ + map: this.map, │ │ │ │ + keyMask: this.zoomBoxKeyMask │ │ │ │ + }); │ │ │ │ + this.dragPan.draw(); │ │ │ │ + this.zoomBox.draw(); │ │ │ │ + var wheelOptions = this.map.fractionalZoom ? {} : { │ │ │ │ + cumulative: false, │ │ │ │ + interval: 50, │ │ │ │ + maxDelta: 6 │ │ │ │ + }; │ │ │ │ + this.handlers.wheel = new OpenLayers.Handler.MouseWheel( │ │ │ │ + this, { │ │ │ │ + up: this.wheelUp, │ │ │ │ + down: this.wheelDown │ │ │ │ + }, │ │ │ │ + OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions) │ │ │ │ + ); │ │ │ │ + if (OpenLayers.Control.PinchZoom) { │ │ │ │ + this.pinchZoom = new OpenLayers.Control.PinchZoom( │ │ │ │ + OpenLayers.Util.extend({ │ │ │ │ + map: this.map │ │ │ │ + }, this.pinchZoomOptions)); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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. │ │ │ │ + * Method: defaultClick │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - matchCase: true, │ │ │ │ + defaultClick: function(evt) { │ │ │ │ + if (evt.lastTouches && evt.lastTouches.length == 2) { │ │ │ │ + this.map.zoomOut(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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}" │ │ │ │ + * Method: defaultDblClick │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - lowerBoundary: null, │ │ │ │ + defaultDblClick: function(evt) { │ │ │ │ + this.map.zoomTo(this.map.zoom + 1, evt.xy); │ │ │ │ + }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * 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}" │ │ │ │ + * Method: defaultDblRightClick │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - upperBoundary: null, │ │ │ │ + defaultDblRightClick: function(evt) { │ │ │ │ + this.map.zoomTo(this.map.zoom - 1, evt.xy); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Filter.Comparison │ │ │ │ - * Creates a comparison rule. │ │ │ │ + /** │ │ │ │ + * Method: wheelChange │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * options - {Object} An optional object with properties to set on the │ │ │ │ - * rule │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Filter.Comparison>} │ │ │ │ + * evt - {Event} │ │ │ │ + * deltaZ - {Integer} │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + wheelChange: function(evt, deltaZ) { │ │ │ │ + if (!this.map.fractionalZoom) { │ │ │ │ + deltaZ = Math.round(deltaZ); │ │ │ │ + } │ │ │ │ + var currentZoom = this.map.getZoom(), │ │ │ │ + newZoom = currentZoom + deltaZ; │ │ │ │ + newZoom = Math.max(newZoom, 0); │ │ │ │ + newZoom = Math.min(newZoom, this.map.getNumZoomLevels()); │ │ │ │ + if (newZoom === currentZoom) { │ │ │ │ + return; │ │ │ │ } │ │ │ │ + this.map.zoomTo(newZoom, evt.xy); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: evaluate │ │ │ │ - * Evaluates this filter in a specific context. │ │ │ │ + /** │ │ │ │ + * Method: wheelUp │ │ │ │ + * User spun scroll wheel up │ │ │ │ * │ │ │ │ * 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. │ │ │ │ + * evt - {Event} │ │ │ │ + * delta - {Integer} │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ + wheelUp: function(evt, delta) { │ │ │ │ + this.wheelChange(evt, delta || 1); │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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. │ │ │ │ + /** │ │ │ │ + * Method: wheelDown │ │ │ │ + * User spun scroll wheel down │ │ │ │ * │ │ │ │ * 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 │ │ │ │ + * evt - {Event} │ │ │ │ + * delta - {Integer} │ │ │ │ */ │ │ │ │ - value2regex: function(wildCard, singleChar, escapeChar) { │ │ │ │ - if (wildCard == ".") { │ │ │ │ - throw new Error("'.' is an unsupported wildCard character for " + │ │ │ │ - "OpenLayers.Filter.Comparison"); │ │ │ │ - } │ │ │ │ - │ │ │ │ - │ │ │ │ - // set UMN MapServer defaults for unspecified parameters │ │ │ │ - wildCard = wildCard ? wildCard : "*"; │ │ │ │ - singleChar = singleChar ? singleChar : "."; │ │ │ │ - escapeChar = escapeChar ? escapeChar : "!"; │ │ │ │ - │ │ │ │ - this.value = this.value.replace( │ │ │ │ - new RegExp("\\" + escapeChar + "(.|$)", "g"), "\\$1"); │ │ │ │ - this.value = this.value.replace( │ │ │ │ - new RegExp("\\" + singleChar, "g"), "."); │ │ │ │ - this.value = this.value.replace( │ │ │ │ - new RegExp("\\" + wildCard, "g"), ".*"); │ │ │ │ - this.value = this.value.replace( │ │ │ │ - new RegExp("\\\\.\\*", "g"), "\\" + wildCard); │ │ │ │ - this.value = this.value.replace( │ │ │ │ - new RegExp("\\\\\\.", "g"), "\\" + singleChar); │ │ │ │ - │ │ │ │ - return this.value; │ │ │ │ + wheelDown: function(evt, delta) { │ │ │ │ + this.wheelChange(evt, delta || -1); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: regex2value │ │ │ │ - * Convert the value of this rule from a regular expression string into an │ │ │ │ - * ogc literal string using a wildCard of *, a singleChar of ., and an │ │ │ │ - * escape of !. Leaves the <value> property unmodified. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} A string value. │ │ │ │ + * Method: disableZoomBox │ │ │ │ */ │ │ │ │ - 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 : "!*"; │ │ │ │ - }); │ │ │ │ + disableZoomBox: function() { │ │ │ │ + this.zoomBoxEnabled = false; │ │ │ │ + this.zoomBox.deactivate(); │ │ │ │ + }, │ │ │ │ │ │ │ │ - // replace \\ with \ │ │ │ │ - value = value.replace(/\\\\/g, "\\"); │ │ │ │ + /** │ │ │ │ + * Method: enableZoomBox │ │ │ │ + */ │ │ │ │ + enableZoomBox: function() { │ │ │ │ + this.zoomBoxEnabled = true; │ │ │ │ + if (this.active) { │ │ │ │ + this.zoomBox.activate(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - // convert .* to * (the sequence #.* is not allowed) │ │ │ │ - value = value.replace(/\.\*/g, "*"); │ │ │ │ + /** │ │ │ │ + * Method: disableZoomWheel │ │ │ │ + */ │ │ │ │ │ │ │ │ - return value; │ │ │ │ + disableZoomWheel: function() { │ │ │ │ + this.zoomWheelEnabled = false; │ │ │ │ + this.handlers.wheel.deactivate(); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: clone │ │ │ │ - * Clones this filter. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Filter.Comparison>} Clone of this filter. │ │ │ │ + * Method: enableZoomWheel │ │ │ │ */ │ │ │ │ - clone: function() { │ │ │ │ - return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison(), this); │ │ │ │ + │ │ │ │ + enableZoomWheel: function() { │ │ │ │ + this.zoomWheelEnabled = true; │ │ │ │ + if (this.active) { │ │ │ │ + this.handlers.wheel.activate(); │ │ │ │ + } │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Filter.Comparison" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Navigation" │ │ │ │ }); │ │ │ │ - │ │ │ │ - │ │ │ │ -OpenLayers.Filter.Comparison.EQUAL_TO = "=="; │ │ │ │ -OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!="; │ │ │ │ -OpenLayers.Filter.Comparison.LESS_THAN = "<"; │ │ │ │ -OpenLayers.Filter.Comparison.GREATER_THAN = ">"; │ │ │ │ -OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<="; │ │ │ │ -OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">="; │ │ │ │ -OpenLayers.Filter.Comparison.BETWEEN = ".."; │ │ │ │ -OpenLayers.Filter.Comparison.LIKE = "~"; │ │ │ │ -OpenLayers.Filter.Comparison.IS_NULL = "NULL"; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Popup.js │ │ │ │ + OpenLayers/Events/buttonclick.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/BaseTypes/Class.js │ │ │ │ + * @requires OpenLayers/Events.js │ │ │ │ */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Popup │ │ │ │ - * A popup is a small div that can opened and closed on the map. │ │ │ │ - * Typically opened in response to clicking on a marker. │ │ │ │ - * See <OpenLayers.Marker>. Popup's don't require their own │ │ │ │ - * layer and are added the the map using the <OpenLayers.Map.addPopup> │ │ │ │ - * method. │ │ │ │ + * Class: OpenLayers.Events.buttonclick │ │ │ │ + * Extension event type for handling buttons on top of a dom element. This │ │ │ │ + * event type fires "buttonclick" on its <target> when a button was │ │ │ │ + * clicked. Buttons are detected by the "olButton" class. │ │ │ │ * │ │ │ │ - * Example: │ │ │ │ - * (code) │ │ │ │ - * popup = new OpenLayers.Popup("chicken", │ │ │ │ - * new OpenLayers.LonLat(5,40), │ │ │ │ - * new OpenLayers.Size(200,200), │ │ │ │ - * "example popup", │ │ │ │ - * true); │ │ │ │ - * │ │ │ │ - * map.addPopup(popup); │ │ │ │ - * (end) │ │ │ │ + * This event type makes sure that button clicks do not interfere with other │ │ │ │ + * events that are registered on the same <element>. │ │ │ │ + * │ │ │ │ + * Event types provided by this extension: │ │ │ │ + * - *buttonclick* Triggered when a button is clicked. Listeners receive an │ │ │ │ + * object with a *buttonElement* property referencing the dom element of │ │ │ │ + * the clicked button, and an *buttonXY* property with the click position │ │ │ │ + * relative to the button. │ │ │ │ */ │ │ │ │ -OpenLayers.Popup = OpenLayers.Class({ │ │ │ │ +OpenLayers.Events.buttonclick = OpenLayers.Class({ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: events │ │ │ │ - * {<OpenLayers.Events>} custom event manager │ │ │ │ + /** │ │ │ │ + * Property: target │ │ │ │ + * {<OpenLayers.Events>} The events instance that the buttonclick event will │ │ │ │ + * be triggered on. │ │ │ │ */ │ │ │ │ - events: null, │ │ │ │ + target: null, │ │ │ │ │ │ │ │ - /** Property: id │ │ │ │ - * {String} the unique identifier assigned to this popup. │ │ │ │ + /** │ │ │ │ + * Property: events │ │ │ │ + * {Array} Events to observe and conditionally stop from propagating when │ │ │ │ + * an element with the olButton class (or its olAlphaImg child) is │ │ │ │ + * clicked. │ │ │ │ */ │ │ │ │ - id: "", │ │ │ │ + events: [ │ │ │ │ + 'mousedown', 'mouseup', 'click', 'dblclick', │ │ │ │ + 'touchstart', 'touchmove', 'touchend', 'keydown' │ │ │ │ + ], │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: lonlat │ │ │ │ - * {<OpenLayers.LonLat>} the position of this popup on the map │ │ │ │ + /** │ │ │ │ + * Property: startRegEx │ │ │ │ + * {RegExp} Regular expression to test Event.type for events that start │ │ │ │ + * a buttonclick sequence. │ │ │ │ */ │ │ │ │ - lonlat: null, │ │ │ │ + startRegEx: /^mousedown|touchstart$/, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: div │ │ │ │ - * {DOMElement} the div that contains this popup. │ │ │ │ + /** │ │ │ │ + * Property: cancelRegEx │ │ │ │ + * {RegExp} Regular expression to test Event.type for events that cancel │ │ │ │ + * a buttonclick sequence. │ │ │ │ */ │ │ │ │ - div: null, │ │ │ │ + cancelRegEx: /^touchmove$/, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: contentSize │ │ │ │ - * {<OpenLayers.Size>} the width and height of the content. │ │ │ │ + /** │ │ │ │ + * Property: completeRegEx │ │ │ │ + * {RegExp} Regular expression to test Event.type for events that complete │ │ │ │ + * a buttonclick sequence. │ │ │ │ */ │ │ │ │ - contentSize: null, │ │ │ │ + completeRegEx: /^mouseup|touchend$/, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: size │ │ │ │ - * {<OpenLayers.Size>} the width and height of the popup. │ │ │ │ + /** │ │ │ │ + * Property: startEvt │ │ │ │ + * {Event} The event that started the click sequence │ │ │ │ */ │ │ │ │ - size: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: contentHTML │ │ │ │ - * {String} An HTML string for this popup to display. │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Events.buttonclick │ │ │ │ + * Construct a buttonclick event type. Applications are not supposed to │ │ │ │ + * create instances of this class - they are created on demand by │ │ │ │ + * <OpenLayers.Events> instances. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * target - {<OpenLayers.Events>} The events instance that the buttonclick │ │ │ │ + * event will be triggered on. │ │ │ │ */ │ │ │ │ - contentHTML: null, │ │ │ │ + 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 │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: backgroundColor │ │ │ │ - * {String} the background color used by the popup. │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ */ │ │ │ │ - backgroundColor: "", │ │ │ │ + destroy: function() { │ │ │ │ + for (var i = this.events.length - 1; i >= 0; --i) { │ │ │ │ + this.target.unregister(this.events[i], this, this.buttonClick); │ │ │ │ + } │ │ │ │ + delete this.target; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: opacity │ │ │ │ - * {float} the opacity of this popup (between 0.0 and 1.0) │ │ │ │ + /** │ │ │ │ + * Method: getPressedButton │ │ │ │ + * Get the pressed button, if any. Returns undefined if no button │ │ │ │ + * was pressed. │ │ │ │ + * │ │ │ │ + * Arguments: │ │ │ │ + * element - {DOMElement} The event target. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The button element, or undefined. │ │ │ │ */ │ │ │ │ - opacity: "", │ │ │ │ + getPressedButton: function(element) { │ │ │ │ + var depth = 3, // limit the search depth │ │ │ │ + button; │ │ │ │ + do { │ │ │ │ + if (OpenLayers.Element.hasClass(element, "olButton")) { │ │ │ │ + // hit! │ │ │ │ + button = element; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + element = element.parentNode; │ │ │ │ + } while (--depth > 0 && element); │ │ │ │ + return button; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: border │ │ │ │ - * {String} the border size of the popup. (eg 2px) │ │ │ │ + /** │ │ │ │ + * Method: ignore │ │ │ │ + * Check for event target elements that should be ignored by OpenLayers. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * element - {DOMElement} The event target. │ │ │ │ */ │ │ │ │ - border: "", │ │ │ │ + ignore: function(element) { │ │ │ │ + var depth = 3, │ │ │ │ + ignore = false; │ │ │ │ + do { │ │ │ │ + if (element.nodeName.toLowerCase() === 'a') { │ │ │ │ + ignore = true; │ │ │ │ + break; │ │ │ │ + } │ │ │ │ + element = element.parentNode; │ │ │ │ + } while (--depth > 0 && element); │ │ │ │ + return ignore; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: contentDiv │ │ │ │ - * {DOMElement} a reference to the element that holds the content of │ │ │ │ - * the div. │ │ │ │ + /** │ │ │ │ + * Method: buttonClick │ │ │ │ + * Check if a button was clicked, and fire the buttonclick event │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - contentDiv: null, │ │ │ │ + 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; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: groupDiv │ │ │ │ - * {DOMElement} First and only child of 'div'. The group Div contains the │ │ │ │ - * 'contentDiv' and the 'closeDiv'. │ │ │ │ - */ │ │ │ │ - groupDiv: null, │ │ │ │ + this.target.triggerEvent("buttonclick", { │ │ │ │ + buttonElement: button, │ │ │ │ + buttonXY: { │ │ │ │ + x: this.startEvt.clientX - pos[0], │ │ │ │ + y: this.startEvt.clientY - pos[1] │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + if (this.cancelRegEx.test(evt.type)) { │ │ │ │ + delete this.startEvt; │ │ │ │ + } │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + propagate = false; │ │ │ │ + } │ │ │ │ + if (this.startRegEx.test(evt.type)) { │ │ │ │ + this.startEvt = evt; │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + propagate = false; │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + propagate = !this.ignore(OpenLayers.Event.element(evt)); │ │ │ │ + delete this.startEvt; │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return propagate; │ │ │ │ + } │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: closeDiv │ │ │ │ - * {DOMElement} the optional closer image │ │ │ │ - */ │ │ │ │ - closeDiv: null, │ │ │ │ +}); │ │ │ │ +/* ====================================================================== │ │ │ │ + OpenLayers/Control/LayerSwitcher.js │ │ │ │ + ====================================================================== */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: autoSize │ │ │ │ - * {Boolean} Resize the popup to auto-fit the contents. │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - autoSize: 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. */ │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: minSize │ │ │ │ - * {<OpenLayers.Size>} Minimum size allowed for the popup's contents. │ │ │ │ +/** │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Lang.js │ │ │ │ + * @requires OpenLayers/Util.js │ │ │ │ + * @requires OpenLayers/Events/buttonclick.js │ │ │ │ + */ │ │ │ │ + │ │ │ │ +/** │ │ │ │ + * Class: OpenLayers.Control.LayerSwitcher │ │ │ │ + * The LayerSwitcher control displays a table of contents for the map. This │ │ │ │ + * allows the user interface to switch between BaseLasyers and to show or hide │ │ │ │ + * Overlays. By default the switcher is shown minimized on the right edge of │ │ │ │ + * the map, the user may expand it by clicking on the handle. │ │ │ │ + * │ │ │ │ + * To create the LayerSwitcher outside of the map, pass the Id of a html div │ │ │ │ + * as the first argument to the constructor. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ + */ │ │ │ │ +OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + │ │ │ │ + /** │ │ │ │ + * Property: layerStates │ │ │ │ + * {Array(Object)} Basically a copy of the "state" of the map's layers │ │ │ │ + * the last time the control was drawn. We have this in order to avoid │ │ │ │ + * unnecessarily redrawing the control. │ │ │ │ */ │ │ │ │ - minSize: null, │ │ │ │ + layerStates: null, │ │ │ │ + │ │ │ │ + // DOM Elements │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: maxSize │ │ │ │ - * {<OpenLayers.Size>} Maximum size allowed for the popup's contents. │ │ │ │ + * Property: layersDiv │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - maxSize: null, │ │ │ │ + layersDiv: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: displayClass │ │ │ │ - * {String} The CSS class of the popup. │ │ │ │ + /** │ │ │ │ + * Property: baseLayersDiv │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - displayClass: "olPopup", │ │ │ │ + baseLayersDiv: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: contentDisplayClass │ │ │ │ - * {String} The CSS class of the popup content div. │ │ │ │ + /** │ │ │ │ + * Property: baseLayers │ │ │ │ + * {Array(Object)} │ │ │ │ */ │ │ │ │ - contentDisplayClass: "olPopupContent", │ │ │ │ + baseLayers: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: padding │ │ │ │ - * {int or <OpenLayers.Bounds>} An extra opportunity to specify internal │ │ │ │ - * padding of the content div inside the popup. This was originally │ │ │ │ - * confused with the css padding as specified in style.css's │ │ │ │ - * 'olPopupContent' class. We would like to get rid of this altogether, │ │ │ │ - * except that it does come in handy for the framed and anchoredbubble │ │ │ │ - * popups, who need to maintain yet another barrier between their │ │ │ │ - * content and the outer border of the popup itself. │ │ │ │ - * │ │ │ │ - * Note that in order to not break API, we must continue to support │ │ │ │ - * this property being set as an integer. Really, though, we'd like to │ │ │ │ - * have this specified as a Bounds object so that user can specify │ │ │ │ - * distinct left, top, right, bottom paddings. With the 3.0 release │ │ │ │ - * we can make this only a bounds. │ │ │ │ - */ │ │ │ │ - padding: 0, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: disableFirefoxOverflowHack │ │ │ │ - * {Boolean} The hack for overflow in Firefox causes all elements │ │ │ │ - * to be re-drawn, which causes Flash elements to be │ │ │ │ - * re-initialized, which is troublesome. │ │ │ │ - * With this property the hack can be disabled. │ │ │ │ + /** │ │ │ │ + * Property: dataLbl │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - disableFirefoxOverflowHack: false, │ │ │ │ + dataLbl: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: fixPadding │ │ │ │ - * To be removed in 3.0, this function merely helps us to deal with the │ │ │ │ - * case where the user may have set an integer value for padding, │ │ │ │ - * instead of an <OpenLayers.Bounds> object. │ │ │ │ + * Property: dataLayersDiv │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - fixPadding: function() { │ │ │ │ - if (typeof this.padding == "number") { │ │ │ │ - this.padding = new OpenLayers.Bounds( │ │ │ │ - this.padding, this.padding, this.padding, this.padding │ │ │ │ - ); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + dataLayersDiv: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: panMapIfOutOfView │ │ │ │ - * {Boolean} When drawn, pan map such that the entire popup is visible in │ │ │ │ - * the current viewport (if necessary). │ │ │ │ - * Default is false. │ │ │ │ + * Property: dataLayers │ │ │ │ + * {Array(Object)} │ │ │ │ */ │ │ │ │ - panMapIfOutOfView: false, │ │ │ │ + dataLayers: null, │ │ │ │ + │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: keepInMap │ │ │ │ - * {Boolean} If panMapIfOutOfView is false, and this property is true, │ │ │ │ - * contrain the popup such that it always fits in the available map │ │ │ │ - * space. By default, this is 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. │ │ │ │ + * Property: minimizeDiv │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - keepInMap: false, │ │ │ │ + minimizeDiv: null, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: closeOnMove │ │ │ │ - * {Boolean} When map pans, close the popup. │ │ │ │ - * Default is false. │ │ │ │ + * Property: maximizeDiv │ │ │ │ + * {DOMElement} │ │ │ │ */ │ │ │ │ - closeOnMove: false, │ │ │ │ + maximizeDiv: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: map │ │ │ │ - * {<OpenLayers.Map>} this gets set in Map.js when the popup is added to the map │ │ │ │ + /** │ │ │ │ + * APIProperty: ascending │ │ │ │ + * {Boolean} │ │ │ │ */ │ │ │ │ - map: null, │ │ │ │ + ascending: true, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Popup │ │ │ │ - * Create a popup. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * id - {String} a unqiue identifier for this popup. If null is passed │ │ │ │ - * an identifier will be automatically generated. │ │ │ │ - * lonlat - {<OpenLayers.LonLat>} The position on the map the popup will │ │ │ │ - * be shown. │ │ │ │ - * contentSize - {<OpenLayers.Size>} The size of the content. │ │ │ │ - * contentHTML - {String} An HTML string to display inside the │ │ │ │ - * popup. │ │ │ │ - * closeBox - {Boolean} Whether to display a close box inside │ │ │ │ - * the popup. │ │ │ │ - * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.LayerSwitcher │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ + this.layerStates = []; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: destroy │ │ │ │ - * nullify references to prevent circular references and memory leaks │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ */ │ │ │ │ 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); │ │ │ │ - } │ │ │ │ + //clear out layers info and unregister their events │ │ │ │ + this.clearLayersArray("base"); │ │ │ │ + this.clearLayersArray("data"); │ │ │ │ │ │ │ │ - this.events.destroy(); │ │ │ │ - this.events = null; │ │ │ │ + this.map.events.un({ │ │ │ │ + buttonclick: this.onButtonClick, │ │ │ │ + addlayer: this.redraw, │ │ │ │ + changelayer: this.redraw, │ │ │ │ + removelayer: this.redraw, │ │ │ │ + changebaselayer: this.redraw, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ │ │ │ │ - if (this.closeDiv) { │ │ │ │ - OpenLayers.Event.stopObservingElement(this.closeDiv); │ │ │ │ - this.groupDiv.removeChild(this.closeDiv); │ │ │ │ - } │ │ │ │ - this.closeDiv = null; │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.div.removeChild(this.groupDiv); │ │ │ │ - this.groupDiv = null; │ │ │ │ + /** │ │ │ │ + * Method: setMap │ │ │ │ + * │ │ │ │ + * Properties: │ │ │ │ + * map - {<OpenLayers.Map>} │ │ │ │ + */ │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ │ │ │ │ - if (this.map != null) { │ │ │ │ - this.map.removePopup(this); │ │ │ │ + this.map.events.on({ │ │ │ │ + addlayer: this.redraw, │ │ │ │ + changelayer: this.redraw, │ │ │ │ + removelayer: this.redraw, │ │ │ │ + changebaselayer: this.redraw, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + if (this.outsideViewport) { │ │ │ │ + this.events.attachToElement(this.div); │ │ │ │ + this.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ + } else { │ │ │ │ + this.map.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ } │ │ │ │ - this.map = null; │ │ │ │ - this.div = null; │ │ │ │ - │ │ │ │ - this.autoSize = null; │ │ │ │ - this.minSize = null; │ │ │ │ - this.maxSize = null; │ │ │ │ - this.padding = null; │ │ │ │ - this.panMapIfOutOfView = null; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ + /** │ │ │ │ * Method: draw │ │ │ │ - * Constructs the elements that make up the popup. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} the position the popup in pixels. │ │ │ │ - * │ │ │ │ * Returns: │ │ │ │ - * {DOMElement} Reference to a div that contains the drawn popup │ │ │ │ + * {DOMElement} A reference to the DIV DOMElement containing the │ │ │ │ + * switcher tabs. │ │ │ │ */ │ │ │ │ - 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); │ │ │ │ - } │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this); │ │ │ │ │ │ │ │ - //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; │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - } │ │ │ │ + // create layout divs │ │ │ │ + this.loadContents(); │ │ │ │ │ │ │ │ - this.moveTo(px); │ │ │ │ - if (!this.autoSize && !this.size) { │ │ │ │ - this.setSize(this.contentSize); │ │ │ │ + // set mode to minimize │ │ │ │ + if (!this.outsideViewport) { │ │ │ │ + this.minimizeControl(); │ │ │ │ } │ │ │ │ - this.setBackgroundColor(); │ │ │ │ - this.setOpacity(); │ │ │ │ - this.setBorder(); │ │ │ │ - this.setContentHTML(); │ │ │ │ │ │ │ │ - if (this.panMapIfOutOfView) { │ │ │ │ - this.panIntoView(); │ │ │ │ - } │ │ │ │ + // populate div with current info │ │ │ │ + this.redraw(); │ │ │ │ │ │ │ │ return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: updatePosition │ │ │ │ - * if the popup has a lonlat and its map members set, │ │ │ │ - * then have it move itself to its proper position │ │ │ │ - */ │ │ │ │ - updatePosition: function() { │ │ │ │ - if ((this.lonlat) && (this.map)) { │ │ │ │ - var px = this.map.getLayerPxFromLonLat(this.lonlat); │ │ │ │ - if (px) { │ │ │ │ - this.moveTo(px); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - │ │ │ │ /** │ │ │ │ - * Method: moveTo │ │ │ │ - * │ │ │ │ + * Method: onButtonClick │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} the top and left position of the popup div. │ │ │ │ + * evt - {Event} │ │ │ │ */ │ │ │ │ - moveTo: function(px) { │ │ │ │ - if ((px != null) && (this.div != null)) { │ │ │ │ - this.div.style.left = px.x + "px"; │ │ │ │ - this.div.style.top = px.y + "px"; │ │ │ │ + onButtonClick: function(evt) { │ │ │ │ + var button = evt.buttonElement; │ │ │ │ + if (button === this.minimizeDiv) { │ │ │ │ + this.minimizeControl(); │ │ │ │ + } else if (button === this.maximizeDiv) { │ │ │ │ + this.maximizeControl(); │ │ │ │ + } else if (button._layerSwitcher === this.id) { │ │ │ │ + if (button["for"]) { │ │ │ │ + button = document.getElementById(button["for"]); │ │ │ │ + } │ │ │ │ + if (!button.disabled) { │ │ │ │ + if (button.type == "radio") { │ │ │ │ + button.checked = true; │ │ │ │ + this.map.setBaseLayer(this.map.getLayer(button._layer)); │ │ │ │ + } else { │ │ │ │ + button.checked = !button.checked; │ │ │ │ + this.updateMap(); │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: visible │ │ │ │ + * Method: clearLayersArray │ │ │ │ + * User specifies either "base" or "data". we then clear all the │ │ │ │ + * corresponding listeners, the div, and reinitialize a new array. │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {Boolean} Boolean indicating whether or not the popup is visible │ │ │ │ + * Parameters: │ │ │ │ + * layersType - {String} │ │ │ │ */ │ │ │ │ - visible: function() { │ │ │ │ - return OpenLayers.Element.visible(this.div); │ │ │ │ + clearLayersArray: function(layersType) { │ │ │ │ + this[layersType + "LayersDiv"].innerHTML = ""; │ │ │ │ + this[layersType + "Layers"] = []; │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: toggle │ │ │ │ - * Toggles visibility of the popup. │ │ │ │ - */ │ │ │ │ - toggle: function() { │ │ │ │ - if (this.visible()) { │ │ │ │ - this.hide(); │ │ │ │ - } else { │ │ │ │ - this.show(); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: show │ │ │ │ - * Makes the popup visible. │ │ │ │ + * Method: checkRedraw │ │ │ │ + * Checks if the layer state has changed since the last redraw() call. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Boolean} The layer state changed since the last redraw() call. │ │ │ │ */ │ │ │ │ - show: function() { │ │ │ │ - this.div.style.display = ''; │ │ │ │ + checkRedraw: function() { │ │ │ │ + if (!this.layerStates.length || │ │ │ │ + (this.map.layers.length != this.layerStates.length)) { │ │ │ │ + return true; │ │ │ │ + } │ │ │ │ │ │ │ │ - if (this.panMapIfOutOfView) { │ │ │ │ - this.panIntoView(); │ │ │ │ + for (var i = 0, len = this.layerStates.length; i < len; i++) { │ │ │ │ + var layerState = this.layerStates[i]; │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + if ((layerState.name != layer.name) || │ │ │ │ + (layerState.inRange != layer.inRange) || │ │ │ │ + (layerState.id != layer.id) || │ │ │ │ + (layerState.visibility != layer.visibility)) { │ │ │ │ + return true; │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: hide │ │ │ │ - * Makes the popup invisible. │ │ │ │ - */ │ │ │ │ - hide: function() { │ │ │ │ - this.div.style.display = 'none'; │ │ │ │ + return false; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setSize │ │ │ │ - * Used to adjust the size of the popup. │ │ │ │ + * Method: redraw │ │ │ │ + * Goes through and takes the current state of the Map and rebuilds the │ │ │ │ + * control to display that state. Groups base layers into a │ │ │ │ + * radio-button group and lists each data layer with a checkbox. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * contentSize - {<OpenLayers.Size>} the new size for the popup's │ │ │ │ - * contents div (in pixels). │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A reference to the DIV DOMElement containing the control │ │ │ │ */ │ │ │ │ - setSize: function(contentSize) { │ │ │ │ - this.size = contentSize.clone(); │ │ │ │ + redraw: function() { │ │ │ │ + //if the state hasn't changed since last redraw, no need │ │ │ │ + // to do anything. Just return the existing div. │ │ │ │ + if (!this.checkRedraw()) { │ │ │ │ + return this.div; │ │ │ │ + } │ │ │ │ │ │ │ │ - // 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; │ │ │ │ + //clear out previous layers │ │ │ │ + this.clearLayersArray("base"); │ │ │ │ + this.clearLayersArray("data"); │ │ │ │ │ │ │ │ - // take into account the popup's 'padding' property │ │ │ │ - this.fixPadding(); │ │ │ │ - wPadding += this.padding.left + this.padding.right; │ │ │ │ - hPadding += this.padding.top + this.padding.bottom; │ │ │ │ + var containsOverlays = false; │ │ │ │ + var containsBaseLayers = false; │ │ │ │ │ │ │ │ - // make extra space for the close div │ │ │ │ - if (this.closeDiv) { │ │ │ │ - var closeDivWidth = parseInt(this.closeDiv.style.width); │ │ │ │ - wPadding += closeDivWidth + contentDivPadding.right; │ │ │ │ + // Save state -- for checking layer if the map state changed. │ │ │ │ + // We save this before redrawing, because in the process of redrawing │ │ │ │ + // we will trigger more visibility changes, and we want to not redraw │ │ │ │ + // and enter an infinite loop. │ │ │ │ + var len = this.map.layers.length; │ │ │ │ + this.layerStates = new Array(len); │ │ │ │ + for (var i = 0; i < len; i++) { │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + this.layerStates[i] = { │ │ │ │ + 'name': layer.name, │ │ │ │ + 'visibility': layer.visibility, │ │ │ │ + 'inRange': layer.inRange, │ │ │ │ + 'id': layer.id │ │ │ │ + }; │ │ │ │ } │ │ │ │ │ │ │ │ - //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; │ │ │ │ + var layers = this.map.layers.slice(); │ │ │ │ + if (!this.ascending) { │ │ │ │ + layers.reverse(); │ │ │ │ } │ │ │ │ + for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ + var layer = layers[i]; │ │ │ │ + var baseLayer = layer.isBaseLayer; │ │ │ │ │ │ │ │ - if (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"; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + if (layer.displayInLayerSwitcher) { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: updateSize │ │ │ │ - * Auto size the popup so that it precisely fits its contents (as │ │ │ │ - * determined by this.contentDiv.innerHTML). Popup size will, of │ │ │ │ - * course, be limited by the available space on the current map │ │ │ │ - */ │ │ │ │ - updateSize: function() { │ │ │ │ + if (baseLayer) { │ │ │ │ + containsBaseLayers = true; │ │ │ │ + } else { │ │ │ │ + containsOverlays = true; │ │ │ │ + } │ │ │ │ │ │ │ │ - // determine actual render dimensions of the contents by putting its │ │ │ │ - // contents into a fake contentDiv (for the CSS) and then measuring it │ │ │ │ - var preparedHTML = "<div class='" + this.contentDisplayClass + "'>" + │ │ │ │ - this.contentDiv.innerHTML + │ │ │ │ - "</div>"; │ │ │ │ + // only check a baselayer if it is *the* baselayer, check data │ │ │ │ + // layers if they are visible │ │ │ │ + var checked = (baseLayer) ? (layer == this.map.baseLayer) : │ │ │ │ + layer.getVisibility(); │ │ │ │ │ │ │ │ - var containerElement = (this.map) ? this.map.div : document.body; │ │ │ │ - var realSize = OpenLayers.Util.getRenderedDimensions( │ │ │ │ - preparedHTML, null, { │ │ │ │ - displayClass: this.displayClass, │ │ │ │ - containerElement: containerElement │ │ │ │ - } │ │ │ │ - ); │ │ │ │ + // create input element │ │ │ │ + var inputElem = document.createElement("input"), │ │ │ │ + // The input shall have an id attribute so we can use │ │ │ │ + // labels to interact with them. │ │ │ │ + inputId = OpenLayers.Util.createUniqueID( │ │ │ │ + this.id + "_input_" │ │ │ │ + ); │ │ │ │ │ │ │ │ - // is the "real" size of the div is safe to display in our map? │ │ │ │ - var safeSize = this.getSafeContentSize(realSize); │ │ │ │ + inputElem.id = inputId; │ │ │ │ + inputElem.name = (baseLayer) ? this.id + "_baseLayers" : layer.name; │ │ │ │ + inputElem.type = (baseLayer) ? "radio" : "checkbox"; │ │ │ │ + inputElem.value = layer.name; │ │ │ │ + inputElem.checked = checked; │ │ │ │ + inputElem.defaultChecked = checked; │ │ │ │ + inputElem.className = "olButton"; │ │ │ │ + inputElem._layer = layer.id; │ │ │ │ + inputElem._layerSwitcher = this.id; │ │ │ │ │ │ │ │ - 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; │ │ │ │ + if (!baseLayer && !layer.inRange) { │ │ │ │ + inputElem.disabled = true; │ │ │ │ + } │ │ │ │ │ │ │ │ - } else { │ │ │ │ + // create span │ │ │ │ + var labelSpan = document.createElement("label"); │ │ │ │ + // this isn't the DOM attribute 'for', but an arbitrary name we │ │ │ │ + // use to find the appropriate input element in <onButtonClick> │ │ │ │ + labelSpan["for"] = inputElem.id; │ │ │ │ + OpenLayers.Element.addClass(labelSpan, "labelSpan olButton"); │ │ │ │ + labelSpan._layer = layer.id; │ │ │ │ + labelSpan._layerSwitcher = this.id; │ │ │ │ + if (!baseLayer && !layer.inRange) { │ │ │ │ + labelSpan.style.color = "gray"; │ │ │ │ + } │ │ │ │ + labelSpan.innerHTML = layer.name; │ │ │ │ + labelSpan.style.verticalAlign = (baseLayer) ? "bottom" : │ │ │ │ + "baseline"; │ │ │ │ + // create line break │ │ │ │ + var br = document.createElement("br"); │ │ │ │ │ │ │ │ - // 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 │ │ │ │ - } │ │ │ │ - ); │ │ │ │ + var groupArray = (baseLayer) ? this.baseLayers : │ │ │ │ + this.dataLayers; │ │ │ │ + groupArray.push({ │ │ │ │ + 'layer': layer, │ │ │ │ + 'inputElem': inputElem, │ │ │ │ + 'labelSpan': labelSpan │ │ │ │ + }); │ │ │ │ │ │ │ │ - //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); │ │ │ │ + var groupDiv = (baseLayer) ? this.baseLayersDiv : │ │ │ │ + this.dataLayersDiv; │ │ │ │ + groupDiv.appendChild(inputElem); │ │ │ │ + groupDiv.appendChild(labelSpan); │ │ │ │ + groupDiv.appendChild(br); │ │ │ │ } │ │ │ │ } │ │ │ │ - this.setSize(newSize); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setBackgroundColor │ │ │ │ - * Sets the background color of the popup. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * color - {String} the background color. eg "#FFBBBB" │ │ │ │ - */ │ │ │ │ - setBackgroundColor: function(color) { │ │ │ │ - if (color != undefined) { │ │ │ │ - this.backgroundColor = color; │ │ │ │ - } │ │ │ │ + // if no overlays, dont display the overlay label │ │ │ │ + this.dataLbl.style.display = (containsOverlays) ? "" : "none"; │ │ │ │ │ │ │ │ - if (this.div != null) { │ │ │ │ - this.div.style.backgroundColor = this.backgroundColor; │ │ │ │ - } │ │ │ │ + // if no baselayers, dont display the baselayer label │ │ │ │ + this.baseLbl.style.display = (containsBaseLayers) ? "" : "none"; │ │ │ │ + │ │ │ │ + return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setOpacity │ │ │ │ - * Sets the opacity of the popup. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). │ │ │ │ + * Method: updateMap │ │ │ │ + * Cycles through the loaded data and base layer input arrays and makes │ │ │ │ + * the necessary calls to the Map object such that that the map's │ │ │ │ + * visual state corresponds to what the user has selected in │ │ │ │ + * the control. │ │ │ │ */ │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ - if (opacity != undefined) { │ │ │ │ - this.opacity = opacity; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if (this.div != null) { │ │ │ │ - // for Mozilla and Safari │ │ │ │ - this.div.style.opacity = this.opacity; │ │ │ │ + updateMap: function() { │ │ │ │ │ │ │ │ - // for IE │ │ │ │ - this.div.style.filter = 'alpha(opacity=' + this.opacity * 100 + ')'; │ │ │ │ + // set the newly selected base layer │ │ │ │ + for (var i = 0, len = this.baseLayers.length; i < len; i++) { │ │ │ │ + var layerEntry = this.baseLayers[i]; │ │ │ │ + if (layerEntry.inputElem.checked) { │ │ │ │ + this.map.setBaseLayer(layerEntry.layer, false); │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: setBorder │ │ │ │ - * Sets the border style of the popup. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * border - {String} The border style value. eg 2px │ │ │ │ - */ │ │ │ │ - setBorder: function(border) { │ │ │ │ - if (border != undefined) { │ │ │ │ - this.border = border; │ │ │ │ + // set the correct visibilities for the overlays │ │ │ │ + for (var i = 0, len = this.dataLayers.length; i < len; i++) { │ │ │ │ + var layerEntry = this.dataLayers[i]; │ │ │ │ + layerEntry.layer.setVisibility(layerEntry.inputElem.checked); │ │ │ │ } │ │ │ │ │ │ │ │ - if (this.div != null) { │ │ │ │ - this.div.style.border = this.border; │ │ │ │ - } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: setContentHTML │ │ │ │ - * Allows the user to set the HTML content of the popup. │ │ │ │ + * Method: maximizeControl │ │ │ │ + * Set up the labels and divs for the control │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * contentHTML - {String} HTML for the div. │ │ │ │ - */ │ │ │ │ - setContentHTML: function(contentHTML) { │ │ │ │ - │ │ │ │ - if (contentHTML != null) { │ │ │ │ - this.contentHTML = contentHTML; │ │ │ │ - } │ │ │ │ - │ │ │ │ - if ((this.contentDiv != null) && │ │ │ │ - (this.contentHTML != null) && │ │ │ │ - (this.contentHTML != this.contentDiv.innerHTML)) { │ │ │ │ - │ │ │ │ - this.contentDiv.innerHTML = this.contentHTML; │ │ │ │ - │ │ │ │ - if (this.autoSize) { │ │ │ │ - │ │ │ │ - //if popup has images, listen for when they finish │ │ │ │ - // loading and resize accordingly │ │ │ │ - this.registerImageListeners(); │ │ │ │ - │ │ │ │ - //auto size the popup to its current contents │ │ │ │ - this.updateSize(); │ │ │ │ - } │ │ │ │ - } │ │ │ │ - │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: registerImageListeners │ │ │ │ - * Called when an image contained by the popup loaded. this function │ │ │ │ - * updates the popup size, then unregisters the image load listener. │ │ │ │ + * e - {Event} │ │ │ │ */ │ │ │ │ - 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) { │ │ │ │ + maximizeControl: function(e) { │ │ │ │ │ │ │ │ - var context = { │ │ │ │ - 'popup': this, │ │ │ │ - 'img': img │ │ │ │ - }; │ │ │ │ + // set the div's width and height to empty values, so │ │ │ │ + // the div dimensions can be controlled by CSS │ │ │ │ + this.div.style.width = ""; │ │ │ │ + this.div.style.height = ""; │ │ │ │ │ │ │ │ - //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); │ │ │ │ + this.showControls(false); │ │ │ │ │ │ │ │ - OpenLayers.Event.observe(img, 'load', img._onImgLoad); │ │ │ │ - } │ │ │ │ + if (e != null) { │ │ │ │ + OpenLayers.Event.stop(e); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: getSafeContentSize │ │ │ │ - * │ │ │ │ + * Method: minimizeControl │ │ │ │ + * Hide all the contents of the control, shrink the size, │ │ │ │ + * add the maximize icon │ │ │ │ + * │ │ │ │ * Parameters: │ │ │ │ - * size - {<OpenLayers.Size>} Desired size to make the popup. │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Size>} A size to make the popup which is neither smaller │ │ │ │ - * than the specified minimum size, nor bigger than the maximum │ │ │ │ - * size (which is calculated relative to the size of the viewport). │ │ │ │ + * e - {Event} │ │ │ │ */ │ │ │ │ - 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; │ │ │ │ - } │ │ │ │ - } │ │ │ │ + minimizeControl: function(e) { │ │ │ │ │ │ │ │ - var maxY = this.map.size.h - │ │ │ │ - this.map.paddingForPopups.top - │ │ │ │ - this.map.paddingForPopups.bottom - │ │ │ │ - hPadding - extraY; │ │ │ │ + // to minimize the control we set its div's width │ │ │ │ + // and height to 0px, we cannot just set "display" │ │ │ │ + // to "none" because it would hide the maximize │ │ │ │ + // div │ │ │ │ + this.div.style.width = "0px"; │ │ │ │ + this.div.style.height = "0px"; │ │ │ │ │ │ │ │ - var maxX = this.map.size.w - │ │ │ │ - this.map.paddingForPopups.left - │ │ │ │ - this.map.paddingForPopups.right - │ │ │ │ - wPadding - extraX; │ │ │ │ + this.showControls(true); │ │ │ │ │ │ │ │ - safeContentSize.w = Math.min(safeContentSize.w, maxX); │ │ │ │ - safeContentSize.h = Math.min(safeContentSize.h, maxY); │ │ │ │ + if (e != null) { │ │ │ │ + OpenLayers.Event.stop(e); │ │ │ │ } │ │ │ │ - │ │ │ │ - return safeContentSize; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: getContentDivPadding │ │ │ │ - * Glorious, oh glorious hack in order to determine the css 'padding' of │ │ │ │ - * the contentDiv. IE/Opera return null here unless we actually add the │ │ │ │ - * popup's main 'div' element (which contains contentDiv) to the DOM. │ │ │ │ - * So we make it invisible and then add it to the document temporarily. │ │ │ │ - * │ │ │ │ - * Once we've taken the padding readings we need, we then remove it │ │ │ │ - * from the DOM (it will actually get added to the DOM in │ │ │ │ - * Map.js's addPopup) │ │ │ │ + * Method: showControls │ │ │ │ + * Hide/Show all LayerSwitcher controls depending on whether we are │ │ │ │ + * minimized or not │ │ │ │ * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Bounds>} │ │ │ │ - */ │ │ │ │ - getContentDivPadding: function() { │ │ │ │ - │ │ │ │ - //use cached value if we have it │ │ │ │ - var contentDivPadding = this._contentDivPadding; │ │ │ │ - if (!contentDivPadding) { │ │ │ │ - │ │ │ │ - if (this.div.parentNode == null) { │ │ │ │ - //make the div invisible and add it to the page │ │ │ │ - this.div.style.display = "none"; │ │ │ │ - document.body.appendChild(this.div); │ │ │ │ - } │ │ │ │ - │ │ │ │ - //read the padding settings from css, put them in an OL.Bounds │ │ │ │ - contentDivPadding = new OpenLayers.Bounds( │ │ │ │ - OpenLayers.Element.getStyle(this.contentDiv, "padding-left"), │ │ │ │ - OpenLayers.Element.getStyle(this.contentDiv, "padding-bottom"), │ │ │ │ - OpenLayers.Element.getStyle(this.contentDiv, "padding-right"), │ │ │ │ - OpenLayers.Element.getStyle(this.contentDiv, "padding-top") │ │ │ │ - ); │ │ │ │ - │ │ │ │ - //cache the value │ │ │ │ - this._contentDivPadding = contentDivPadding; │ │ │ │ - │ │ │ │ - if (this.div.parentNode == document.body) { │ │ │ │ - //remove the div from the page and make it visible again │ │ │ │ - document.body.removeChild(this.div); │ │ │ │ - this.div.style.display = ""; │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return contentDivPadding; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: addCloseBox │ │ │ │ - * │ │ │ │ * Parameters: │ │ │ │ - * callback - {Function} The callback to be called when the close button │ │ │ │ - * is clicked. │ │ │ │ + * minimize - {Boolean} │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ + showControls: function(minimize) { │ │ │ │ │ │ │ │ - this.closeDiv.style.right = contentDivPadding.right + "px"; │ │ │ │ - this.closeDiv.style.top = contentDivPadding.top + "px"; │ │ │ │ - this.groupDiv.appendChild(this.closeDiv); │ │ │ │ + this.maximizeDiv.style.display = minimize ? "" : "none"; │ │ │ │ + this.minimizeDiv.style.display = minimize ? "none" : ""; │ │ │ │ │ │ │ │ - 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)); │ │ │ │ + this.layersDiv.style.display = minimize ? "none" : ""; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: panIntoView │ │ │ │ - * Pans the map such that the popup is totaly viewable (if necessary) │ │ │ │ + * Method: loadContents │ │ │ │ + * Set up the labels and divs for the control │ │ │ │ */ │ │ │ │ - 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(); │ │ │ │ + loadContents: function() { │ │ │ │ │ │ │ │ - //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; │ │ │ │ - } │ │ │ │ + // layers list div │ │ │ │ + this.layersDiv = document.createElement("div"); │ │ │ │ + this.layersDiv.id = this.id + "_layersDiv"; │ │ │ │ + OpenLayers.Element.addClass(this.layersDiv, "layersDiv"); │ │ │ │ │ │ │ │ - //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; │ │ │ │ - } │ │ │ │ + this.baseLbl = document.createElement("div"); │ │ │ │ + this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer"); │ │ │ │ + OpenLayers.Element.addClass(this.baseLbl, "baseLbl"); │ │ │ │ │ │ │ │ - var dx = origTL.x - newTL.x; │ │ │ │ - var dy = origTL.y - newTL.y; │ │ │ │ + this.baseLayersDiv = document.createElement("div"); │ │ │ │ + OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv"); │ │ │ │ │ │ │ │ - this.map.pan(dx, dy); │ │ │ │ - }, │ │ │ │ + this.dataLbl = document.createElement("div"); │ │ │ │ + this.dataLbl.innerHTML = OpenLayers.i18n("Overlays"); │ │ │ │ + OpenLayers.Element.addClass(this.dataLbl, "dataLbl"); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: registerEvents │ │ │ │ - * Registers events on the popup. │ │ │ │ - * │ │ │ │ - * Do this in a separate function so that subclasses can │ │ │ │ - * choose to override it if they wish to deal differently │ │ │ │ - * with mouse events │ │ │ │ - * │ │ │ │ - * Note in the following handler functions that some special │ │ │ │ - * care is needed to deal correctly with mousing and popups. │ │ │ │ - * │ │ │ │ - * Because the user might select the zoom-rectangle option and │ │ │ │ - * then drag it over a popup, we need a safe way to allow the │ │ │ │ - * mousemove and mouseup events to pass through the popup when │ │ │ │ - * they are initiated from outside. The same procedure is needed for │ │ │ │ - * touchmove and touchend events. │ │ │ │ - * │ │ │ │ - * Otherwise, we want to essentially kill the event propagation │ │ │ │ - * for all other events, though we have to do so carefully, │ │ │ │ - * without disabling basic html functionality, like clicking on │ │ │ │ - * hyperlinks or drag-selecting text. │ │ │ │ - */ │ │ │ │ - registerEvents: function() { │ │ │ │ - this.events = new OpenLayers.Events(this, this.div, null, true); │ │ │ │ + this.dataLayersDiv = document.createElement("div"); │ │ │ │ + OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv"); │ │ │ │ │ │ │ │ - function onTouchstart(evt) { │ │ │ │ - OpenLayers.Event.stop(evt, true); │ │ │ │ + if (this.ascending) { │ │ │ │ + this.layersDiv.appendChild(this.baseLbl); │ │ │ │ + this.layersDiv.appendChild(this.baseLayersDiv); │ │ │ │ + this.layersDiv.appendChild(this.dataLbl); │ │ │ │ + this.layersDiv.appendChild(this.dataLayersDiv); │ │ │ │ + } else { │ │ │ │ + this.layersDiv.appendChild(this.dataLbl); │ │ │ │ + this.layersDiv.appendChild(this.dataLayersDiv); │ │ │ │ + this.layersDiv.appendChild(this.baseLbl); │ │ │ │ + this.layersDiv.appendChild(this.baseLayersDiv); │ │ │ │ } │ │ │ │ - this.events.on({ │ │ │ │ - "mousedown": this.onmousedown, │ │ │ │ - "mousemove": this.onmousemove, │ │ │ │ - "mouseup": this.onmouseup, │ │ │ │ - "click": this.onclick, │ │ │ │ - "mouseout": this.onmouseout, │ │ │ │ - "dblclick": this.ondblclick, │ │ │ │ - "touchstart": onTouchstart, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: onmousedown │ │ │ │ - * When mouse goes down within the popup, make a note of │ │ │ │ - * it locally, and then do not propagate the mousedown │ │ │ │ - * (but do so safely so that user can select text inside) │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - */ │ │ │ │ - onmousedown: function(evt) { │ │ │ │ - this.mousedown = true; │ │ │ │ - OpenLayers.Event.stop(evt, true); │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: onmousemove │ │ │ │ - * If the drag was started within the popup, then │ │ │ │ - * do not propagate the mousemove (but do so safely │ │ │ │ - * so that user can select text inside) │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - */ │ │ │ │ - onmousemove: function(evt) { │ │ │ │ - if (this.mousedown) { │ │ │ │ - OpenLayers.Event.stop(evt, true); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + this.div.appendChild(this.layersDiv); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + // maximize button div │ │ │ │ + var img = OpenLayers.Util.getImageLocation('layer-switcher-maximize.png'); │ │ │ │ + this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ + "OpenLayers_Control_MaximizeDiv", │ │ │ │ + null, │ │ │ │ + null, │ │ │ │ + img, │ │ │ │ + "absolute"); │ │ │ │ + OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton"); │ │ │ │ + this.maximizeDiv.style.display = "none"; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: onclick │ │ │ │ - * Ignore clicks, but allowing default browser handling │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - */ │ │ │ │ - onclick: function(evt) { │ │ │ │ - OpenLayers.Event.stop(evt, true); │ │ │ │ - }, │ │ │ │ + this.div.appendChild(this.maximizeDiv); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * 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; │ │ │ │ - }, │ │ │ │ + // minimize button div │ │ │ │ + var img = OpenLayers.Util.getImageLocation('layer-switcher-minimize.png'); │ │ │ │ + this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv( │ │ │ │ + "OpenLayers_Control_MinimizeDiv", │ │ │ │ + null, │ │ │ │ + null, │ │ │ │ + img, │ │ │ │ + "absolute"); │ │ │ │ + OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton"); │ │ │ │ + this.minimizeDiv.style.display = "none"; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: ondblclick │ │ │ │ - * Ignore double-clicks, but allowing default browser handling │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * evt - {Event} │ │ │ │ - */ │ │ │ │ - ondblclick: function(evt) { │ │ │ │ - OpenLayers.Event.stop(evt, true); │ │ │ │ + this.div.appendChild(this.minimizeDiv); │ │ │ │ }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Popup" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.LayerSwitcher" │ │ │ │ }); │ │ │ │ - │ │ │ │ -OpenLayers.Popup.WIDTH = 200; │ │ │ │ -OpenLayers.Popup.HEIGHT = 200; │ │ │ │ -OpenLayers.Popup.COLOR = "white"; │ │ │ │ -OpenLayers.Popup.OPACITY = 1; │ │ │ │ -OpenLayers.Popup.BORDER = "0px"; │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Popup/Anchored.js │ │ │ │ + OpenLayers/Control/Attribution.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ - │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Popup.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Popup.Anchored │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Control.Attribution │ │ │ │ + * The attribution control adds attribution from layers to the map display. │ │ │ │ + * It uses 'attribution' property of each layer. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Popup> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Popup.Anchored = │ │ │ │ - OpenLayers.Class(OpenLayers.Popup, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: relativePosition │ │ │ │ - * {String} Relative position of the popup ("br", "tr", "tl" or "bl"). │ │ │ │ - */ │ │ │ │ - relativePosition: null, │ │ │ │ +OpenLayers.Control.Attribution = │ │ │ │ + OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIProperty: keepInMap │ │ │ │ - * {Boolean} If panMapIfOutOfView is false, and this property is true, │ │ │ │ - * contrain the popup such that it always fits in the available map │ │ │ │ - * space. By default, this is set. If you are creating popups that are │ │ │ │ - * near map edges and not allowing pannning, and especially if you have │ │ │ │ - * a popup which has a fixedRelativePosition, setting this to false may │ │ │ │ - * be a smart thing to do. │ │ │ │ - * │ │ │ │ - * For anchored popups, default is true, since subclasses will │ │ │ │ - * usually want this functionality. │ │ │ │ + * APIProperty: separator │ │ │ │ + * {String} String used to separate layers. │ │ │ │ */ │ │ │ │ - keepInMap: true, │ │ │ │ + separator: ", ", │ │ │ │ │ │ │ │ /** │ │ │ │ - * Property: anchor │ │ │ │ - * {Object} Object to which we'll anchor the popup. Must expose a │ │ │ │ - * 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>). │ │ │ │ + * APIProperty: template │ │ │ │ + * {String} Template for the attribution. This has to include the substring │ │ │ │ + * "${layers}", which will be replaced by the layer specific │ │ │ │ + * attributions, separated by <separator>. The default is "${layers}". │ │ │ │ */ │ │ │ │ - anchor: null, │ │ │ │ + template: "${layers}", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Popup.Anchored │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.Attribution │ │ │ │ * │ │ │ │ * Parameters: │ │ │ │ - * id - {String} │ │ │ │ - * lonlat - {<OpenLayers.LonLat>} │ │ │ │ - * contentSize - {<OpenLayers.Size>} │ │ │ │ - * contentHTML - {String} │ │ │ │ - * anchor - {Object} Object which must expose a 'size' <OpenLayers.Size> │ │ │ │ - * and 'offset' <OpenLayers.Pixel> (generally an <OpenLayers.Icon>). │ │ │ │ - * closeBox - {Boolean} │ │ │ │ - * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ + * options - {Object} Options for control. │ │ │ │ */ │ │ │ │ - initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, │ │ │ │ - closeBoxCallback) { │ │ │ │ - var newArguments = [ │ │ │ │ - id, lonlat, contentSize, contentHTML, closeBox, closeBoxCallback │ │ │ │ - ]; │ │ │ │ - OpenLayers.Popup.prototype.initialize.apply(this, newArguments); │ │ │ │ - │ │ │ │ - this.anchor = (anchor != null) ? anchor : │ │ │ │ - { │ │ │ │ - size: new OpenLayers.Size(0, 0), │ │ │ │ - offset: new OpenLayers.Pixel(0, 0) │ │ │ │ - }; │ │ │ │ - }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * Destroy control. │ │ │ │ */ │ │ │ │ destroy: function() { │ │ │ │ - this.anchor = null; │ │ │ │ - this.relativePosition = null; │ │ │ │ - │ │ │ │ - OpenLayers.Popup.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ + this.map.events.un({ │ │ │ │ + "removelayer": this.updateAttribution, │ │ │ │ + "addlayer": this.updateAttribution, │ │ │ │ + "changelayer": this.updateAttribution, │ │ │ │ + "changebaselayer": this.updateAttribution, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIMethod: show │ │ │ │ - * Overridden from Popup since user might hide popup and then show() it │ │ │ │ - * in a new location (meaning we might want to update the relative │ │ │ │ - * position on the show) │ │ │ │ - */ │ │ │ │ - show: function() { │ │ │ │ - this.updatePosition(); │ │ │ │ - OpenLayers.Popup.prototype.show.apply(this, arguments); │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * Method: moveTo │ │ │ │ - * Since the popup is moving to a new px, it might need also to be moved │ │ │ │ - * relative to where the marker is. We first calculate the new │ │ │ │ - * relativePosition, and then we calculate the new px where we will │ │ │ │ - * put the popup, based on the new relative position. │ │ │ │ - * │ │ │ │ - * If the relativePosition has changed, we must also call │ │ │ │ - * updateRelativePosition() to make any visual changes to the popup │ │ │ │ - * which are associated with putting it in a new relativePosition. │ │ │ │ + * Method: draw │ │ │ │ + * Initialize control. │ │ │ │ * │ │ │ │ - * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A reference to the DIV DOMElement containing the control │ │ │ │ */ │ │ │ │ - moveTo: function(px) { │ │ │ │ - var oldRelativePosition = this.relativePosition; │ │ │ │ - this.relativePosition = this.calculateRelativePosition(px); │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ │ │ │ │ - OpenLayers.Popup.prototype.moveTo.call(this, this.calculateNewPx(px)); │ │ │ │ + this.map.events.on({ │ │ │ │ + 'changebaselayer': this.updateAttribution, │ │ │ │ + 'changelayer': this.updateAttribution, │ │ │ │ + 'addlayer': this.updateAttribution, │ │ │ │ + 'removelayer': this.updateAttribution, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.updateAttribution(); │ │ │ │ │ │ │ │ - //if this move has caused the popup to change its relative position, │ │ │ │ - // we need to make the appropriate cosmetic changes. │ │ │ │ - if (this.relativePosition != oldRelativePosition) { │ │ │ │ - this.updateRelativePosition(); │ │ │ │ - } │ │ │ │ + return this.div; │ │ │ │ }, │ │ │ │ │ │ │ │ /** │ │ │ │ - * APIMethod: setSize │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * contentSize - {<OpenLayers.Size>} the new size for the popup's │ │ │ │ - * contents div (in pixels). │ │ │ │ + * Method: updateAttribution │ │ │ │ + * Update attribution string. │ │ │ │ */ │ │ │ │ - setSize: function(contentSize) { │ │ │ │ - OpenLayers.Popup.prototype.setSize.apply(this, arguments); │ │ │ │ - │ │ │ │ - if ((this.lonlat) && (this.map)) { │ │ │ │ - var px = this.map.getLayerPxFromLonLat(this.lonlat); │ │ │ │ - this.moveTo(px); │ │ │ │ + updateAttribution: function() { │ │ │ │ + var attributions = []; │ │ │ │ + if (this.map && this.map.layers) { │ │ │ │ + for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + if (layer.attribution && layer.getVisibility()) { │ │ │ │ + // add attribution only if attribution text is unique │ │ │ │ + if (OpenLayers.Util.indexOf( │ │ │ │ + attributions, layer.attribution) === -1) { │ │ │ │ + attributions.push(layer.attribution); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.div.innerHTML = OpenLayers.String.format(this.template, { │ │ │ │ + layers: attributions.join(this.separator) │ │ │ │ + }); │ │ │ │ } │ │ │ │ }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: calculateRelativePosition │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {String} The relative position ("br" "tr" "tl" "bl") at which the popup │ │ │ │ - * should be placed. │ │ │ │ - */ │ │ │ │ - calculateRelativePosition: function(px) { │ │ │ │ - var lonlat = this.map.getLonLatFromLayerPx(px); │ │ │ │ - │ │ │ │ - var extent = this.map.getExtent(); │ │ │ │ - var quadrant = extent.determineQuadrant(lonlat); │ │ │ │ - │ │ │ │ - return OpenLayers.Bounds.oppositeQuadrant(quadrant); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: updateRelativePosition │ │ │ │ - * The popup has been moved to a new relative location, so we may want to │ │ │ │ - * make some cosmetic adjustments to it. │ │ │ │ - * │ │ │ │ - * Note that in the classic Anchored popup, there is nothing to do │ │ │ │ - * here, since the popup looks exactly the same in all four positions. │ │ │ │ - * Subclasses such as Framed, however, will want to do something │ │ │ │ - * special here. │ │ │ │ - */ │ │ │ │ - updateRelativePosition: function() { │ │ │ │ - //to be overridden by subclasses │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: calculateNewPx │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} The the new px position of the popup on the screen │ │ │ │ - * relative to the passed-in px. │ │ │ │ - */ │ │ │ │ - calculateNewPx: function(px) { │ │ │ │ - var newPx = px.offset(this.anchor.offset); │ │ │ │ - │ │ │ │ - //use contentSize if size is not already set │ │ │ │ - var size = this.size || this.contentSize; │ │ │ │ - │ │ │ │ - var top = (this.relativePosition.charAt(0) == 't'); │ │ │ │ - newPx.y += (top) ? -size.h : this.anchor.size.h; │ │ │ │ - │ │ │ │ - var left = (this.relativePosition.charAt(1) == 'l'); │ │ │ │ - newPx.x += (left) ? -size.w : this.anchor.size.w; │ │ │ │ - │ │ │ │ - return newPx; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - CLASS_NAME: "OpenLayers.Popup.Anchored" │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Attribution" │ │ │ │ }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Popup/Framed.js │ │ │ │ + OpenLayers/Control/Panel.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Popup/Anchored.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Events/buttonclick.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Popup.Framed │ │ │ │ - * │ │ │ │ + * Class: OpenLayers.Control.Panel │ │ │ │ + * The Panel control is a container for other controls. With it toolbars │ │ │ │ + * may be composed. │ │ │ │ + * │ │ │ │ * Inherits from: │ │ │ │ - * - <OpenLayers.Popup.Anchored> │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Popup.Framed = │ │ │ │ - OpenLayers.Class(OpenLayers.Popup.Anchored, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: imageSrc │ │ │ │ - * {String} location of the image to be used as the popup frame │ │ │ │ - */ │ │ │ │ - imageSrc: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: imageSize │ │ │ │ - * {<OpenLayers.Size>} Size (measured in pixels) of the image located │ │ │ │ - * by the 'imageSrc' property. │ │ │ │ - */ │ │ │ │ - imageSize: null, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIProperty: isAlphaImage │ │ │ │ - * {Boolean} The image has some alpha and thus needs to use the alpha │ │ │ │ - * image hack. Note that setting this to true will have no noticeable │ │ │ │ - * effect in FF or IE7 browsers, but will all but crush the ie6 │ │ │ │ - * browser. │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - isAlphaImage: false, │ │ │ │ +OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + /** │ │ │ │ + * Property: controls │ │ │ │ + * {Array(<OpenLayers.Control>)} │ │ │ │ + */ │ │ │ │ + controls: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: positionBlocks │ │ │ │ - * {Object} Hash of different position blocks (Object/Hashs). Each block │ │ │ │ - * will be keyed by a two-character 'relativePosition' │ │ │ │ - * code string (ie "tl", "tr", "bl", "br"). Block properties are │ │ │ │ - * 'offset', 'padding' (self-explanatory), and finally the 'blocks' │ │ │ │ - * parameter, which is an array of the block objects. │ │ │ │ - * │ │ │ │ - * Each block object must have 'size', 'anchor', and 'position' │ │ │ │ - * properties. │ │ │ │ - * │ │ │ │ - * Note that positionBlocks should never be modified at runtime. │ │ │ │ - */ │ │ │ │ - positionBlocks: null, │ │ │ │ + /** │ │ │ │ + * APIProperty: autoActivate │ │ │ │ + * {Boolean} Activate the control when it is added to a map. Default is │ │ │ │ + * true. │ │ │ │ + */ │ │ │ │ + autoActivate: true, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: blocks │ │ │ │ - * {Array[Object]} Array of objects, each of which is one "block" of the │ │ │ │ - * popup. Each block has a 'div' and an 'image' property, both of │ │ │ │ - * which are DOMElements, and the latter of which is appended to the │ │ │ │ - * former. These are reused as the popup goes changing positions for │ │ │ │ - * great economy and elegance. │ │ │ │ - */ │ │ │ │ - blocks: null, │ │ │ │ + /** │ │ │ │ + * APIProperty: defaultControl │ │ │ │ + * {<OpenLayers.Control>} The control which is activated when the control is │ │ │ │ + * activated (turned on), which also happens at instantiation. │ │ │ │ + * If <saveState> is true, <defaultControl> will be nullified after the │ │ │ │ + * first activation of the panel. │ │ │ │ + */ │ │ │ │ + defaultControl: null, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: fixedRelativePosition │ │ │ │ - * {Boolean} We want the framed popup to work dynamically placed relative │ │ │ │ - * to its anchor but also in just one fixed position. A well designed │ │ │ │ - * framed popup will have the pixels and logic to display itself in │ │ │ │ - * any of the four relative positions, but (understandably), this will │ │ │ │ - * not be the case for all of them. By setting this property to 'true', │ │ │ │ - * framed popup will not recalculate for the best placement each time │ │ │ │ - * it's open, but will always open the same way. │ │ │ │ - * Note that if this is set to true, it is generally advisable to also │ │ │ │ - * set the 'panIntoView' property to true so that the popup can be │ │ │ │ - * scrolled into view (since it will often be offscreen on open) │ │ │ │ - * Default is false. │ │ │ │ - */ │ │ │ │ - fixedRelativePosition: false, │ │ │ │ + /** │ │ │ │ + * APIProperty: saveState │ │ │ │ + * {Boolean} If set to true, the active state of this panel's controls will │ │ │ │ + * be stored on panel deactivation, and restored on reactivation. Default │ │ │ │ + * is false. │ │ │ │ + */ │ │ │ │ + saveState: false, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Popup.Framed │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * id - {String} │ │ │ │ - * lonlat - {<OpenLayers.LonLat>} │ │ │ │ - * contentSize - {<OpenLayers.Size>} │ │ │ │ - * contentHTML - {String} │ │ │ │ - * anchor - {Object} Object to which we'll anchor the popup. Must expose │ │ │ │ - * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) │ │ │ │ - * (Note that this is generally an <OpenLayers.Icon>). │ │ │ │ - * closeBox - {Boolean} │ │ │ │ - * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ - */ │ │ │ │ - initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, │ │ │ │ - closeBoxCallback) { │ │ │ │ + /** │ │ │ │ + * APIProperty: allowDepress │ │ │ │ + * {Boolean} If is true the <OpenLayers.Control.TYPE_TOOL> controls can │ │ │ │ + * be deactivated by clicking the icon that represents them. Default │ │ │ │ + * is false. │ │ │ │ + */ │ │ │ │ + allowDepress: false, │ │ │ │ │ │ │ │ - OpenLayers.Popup.Anchored.prototype.initialize.apply(this, arguments); │ │ │ │ + /** │ │ │ │ + * Property: activeState │ │ │ │ + * {Object} stores the active state of this panel's controls. │ │ │ │ + */ │ │ │ │ + activeState: null, │ │ │ │ │ │ │ │ - if (this.fixedRelativePosition) { │ │ │ │ - //based on our decided relativePostion, set the current padding │ │ │ │ - // this keeps us from getting into trouble │ │ │ │ - this.updateRelativePosition(); │ │ │ │ + /** │ │ │ │ + * Constructor: OpenLayers.Control.Panel │ │ │ │ + * Create a new control panel. │ │ │ │ + * │ │ │ │ + * Each control in the panel is represented by an icon. When clicking │ │ │ │ + * on an icon, the <activateControl> method is called. │ │ │ │ + * │ │ │ │ + * Specific properties for controls on a panel: │ │ │ │ + * type - {Number} One of <OpenLayers.Control.TYPE_TOOL>, │ │ │ │ + * <OpenLayers.Control.TYPE_TOGGLE>, <OpenLayers.Control.TYPE_BUTTON>. │ │ │ │ + * If not provided, <OpenLayers.Control.TYPE_TOOL> is assumed. │ │ │ │ + * title - {string} Text displayed when mouse is over the icon that │ │ │ │ + * represents the control. │ │ │ │ + * │ │ │ │ + * The <OpenLayers.Control.type> of a control determines the behavior when │ │ │ │ + * clicking its icon: │ │ │ │ + * <OpenLayers.Control.TYPE_TOOL> - The control is activated and other │ │ │ │ + * controls of this type in the same panel are deactivated. This is │ │ │ │ + * the default type. │ │ │ │ + * <OpenLayers.Control.TYPE_TOGGLE> - The active state of the control is │ │ │ │ + * toggled. │ │ │ │ + * <OpenLayers.Control.TYPE_BUTTON> - The │ │ │ │ + * <OpenLayers.Control.Button.trigger> method of the control is called, │ │ │ │ + * but its active state is not changed. │ │ │ │ + * │ │ │ │ + * If a control is <OpenLayers.Control.active>, it will be drawn with the │ │ │ │ + * olControl[Name]ItemActive class, otherwise with the │ │ │ │ + * olControl[Name]ItemInactive class. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * options - {Object} An optional object whose properties will be used │ │ │ │ + * to extend the control. │ │ │ │ + */ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + this.controls = []; │ │ │ │ + this.activeState = {}; │ │ │ │ + }, │ │ │ │ │ │ │ │ - //make calculateRelativePosition always return the specified │ │ │ │ - // fixed position. │ │ │ │ - this.calculateRelativePosition = function(px) { │ │ │ │ - return this.relativePosition; │ │ │ │ - }; │ │ │ │ + /** │ │ │ │ + * APIMethod: destroy │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + if (this.map) { │ │ │ │ + this.map.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ + } │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + for (var ctl, i = this.controls.length - 1; i >= 0; i--) { │ │ │ │ + ctl = this.controls[i]; │ │ │ │ + if (ctl.events) { │ │ │ │ + ctl.events.un({ │ │ │ │ + activate: this.iconOn, │ │ │ │ + deactivate: this.iconOff │ │ │ │ + }); │ │ │ │ } │ │ │ │ + ctl.panel_div = null; │ │ │ │ + } │ │ │ │ + this.activeState = null; │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.contentDiv.style.position = "absolute"; │ │ │ │ - this.contentDiv.style.zIndex = 1; │ │ │ │ - │ │ │ │ - if (closeBox) { │ │ │ │ - this.closeDiv.style.zIndex = 1; │ │ │ │ + /** │ │ │ │ + * APIMethod: activate │ │ │ │ + */ │ │ │ │ + activate: function() { │ │ │ │ + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ + var control; │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ + control = this.controls[i]; │ │ │ │ + if (control === this.defaultControl || │ │ │ │ + (this.saveState && this.activeState[control.id])) { │ │ │ │ + control.activate(); │ │ │ │ + } │ │ │ │ } │ │ │ │ + if (this.saveState === true) { │ │ │ │ + this.defaultControl = null; │ │ │ │ + } │ │ │ │ + this.redraw(); │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.groupDiv.style.position = "absolute"; │ │ │ │ - this.groupDiv.style.top = "0px"; │ │ │ │ - this.groupDiv.style.left = "0px"; │ │ │ │ - this.groupDiv.style.height = "100%"; │ │ │ │ - this.groupDiv.style.width = "100%"; │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: destroy │ │ │ │ - */ │ │ │ │ - destroy: function() { │ │ │ │ - this.imageSrc = null; │ │ │ │ - this.imageSize = null; │ │ │ │ - this.isAlphaImage = null; │ │ │ │ - │ │ │ │ - this.fixedRelativePosition = false; │ │ │ │ - this.positionBlocks = null; │ │ │ │ + /** │ │ │ │ + * APIMethod: deactivate │ │ │ │ + */ │ │ │ │ + deactivate: function() { │ │ │ │ + if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + var control; │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ + control = this.controls[i]; │ │ │ │ + this.activeState[control.id] = control.deactivate(); │ │ │ │ + } │ │ │ │ + this.redraw(); │ │ │ │ + return true; │ │ │ │ + } else { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - //remove our blocks │ │ │ │ - for (var i = 0; i < this.blocks.length; i++) { │ │ │ │ - var block = this.blocks[i]; │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ + if (this.outsideViewport) { │ │ │ │ + this.events.attachToElement(this.div); │ │ │ │ + this.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ + } else { │ │ │ │ + this.map.events.register("buttonclick", this, this.onButtonClick); │ │ │ │ + } │ │ │ │ + this.addControlsToMap(this.controls); │ │ │ │ + return this.div; │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (block.image) { │ │ │ │ - block.div.removeChild(block.image); │ │ │ │ - } │ │ │ │ - block.image = null; │ │ │ │ + /** │ │ │ │ + * Method: redraw │ │ │ │ + */ │ │ │ │ + redraw: function() { │ │ │ │ + for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) { │ │ │ │ + this.div.removeChild(this.div.childNodes[i]); │ │ │ │ + } │ │ │ │ + this.div.innerHTML = ""; │ │ │ │ + if (this.active) { │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ + this.div.appendChild(this.controls[i].panel_div); │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (block.div) { │ │ │ │ - this.groupDiv.removeChild(block.div); │ │ │ │ + /** │ │ │ │ + * APIMethod: activateControl │ │ │ │ + * This method is called when the user click on the icon representing a │ │ │ │ + * control in the panel. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * control - {<OpenLayers.Control>} │ │ │ │ + */ │ │ │ │ + activateControl: function(control) { │ │ │ │ + if (!this.active) { │ │ │ │ + return false; │ │ │ │ + } │ │ │ │ + if (control.type == OpenLayers.Control.TYPE_BUTTON) { │ │ │ │ + control.trigger(); │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (control.type == OpenLayers.Control.TYPE_TOGGLE) { │ │ │ │ + if (control.active) { │ │ │ │ + control.deactivate(); │ │ │ │ + } else { │ │ │ │ + control.activate(); │ │ │ │ + } │ │ │ │ + return; │ │ │ │ + } │ │ │ │ + if (this.allowDepress && control.active) { │ │ │ │ + control.deactivate(); │ │ │ │ + } else { │ │ │ │ + var c; │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ + c = this.controls[i]; │ │ │ │ + if (c != control && │ │ │ │ + (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) { │ │ │ │ + c.deactivate(); │ │ │ │ } │ │ │ │ - block.div = null; │ │ │ │ } │ │ │ │ - this.blocks = null; │ │ │ │ - │ │ │ │ - OpenLayers.Popup.Anchored.prototype.destroy.apply(this, arguments); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setBackgroundColor │ │ │ │ - */ │ │ │ │ - setBackgroundColor: function(color) { │ │ │ │ - //does nothing since the framed popup's entire scheme is based on a │ │ │ │ - // an image -- changing the background color makes no sense. │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setBorder │ │ │ │ - */ │ │ │ │ - setBorder: function() { │ │ │ │ - //does nothing since the framed popup's entire scheme is based on a │ │ │ │ - // an image -- changing the popup's border makes no sense. │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: setOpacity │ │ │ │ - * Sets the opacity of the popup. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * opacity - {float} A value between 0.0 (transparent) and 1.0 (solid). │ │ │ │ - */ │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ - //does nothing since we suppose that we'll never apply an opacity │ │ │ │ - // to a framed popup │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * APIMethod: setSize │ │ │ │ - * Overridden here, because we need to update the blocks whenever the size │ │ │ │ - * of the popup has changed. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * contentSize - {<OpenLayers.Size>} the new size for the popup's │ │ │ │ - * contents div (in pixels). │ │ │ │ - */ │ │ │ │ - setSize: function(contentSize) { │ │ │ │ - OpenLayers.Popup.Anchored.prototype.setSize.apply(this, arguments); │ │ │ │ - │ │ │ │ - this.updateBlocks(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: updateRelativePosition │ │ │ │ - * When the relative position changes, we need to set the new padding │ │ │ │ - * BBOX on the popup, reposition the close div, and update the blocks. │ │ │ │ - */ │ │ │ │ - updateRelativePosition: function() { │ │ │ │ - │ │ │ │ - //update the padding │ │ │ │ - this.padding = this.positionBlocks[this.relativePosition].padding; │ │ │ │ + control.activate(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - //update the position of our close box to new padding │ │ │ │ - if (this.closeDiv) { │ │ │ │ - // use the content div's css padding to determine if we should │ │ │ │ - // padd the close div │ │ │ │ - var contentDivPadding = this.getContentDivPadding(); │ │ │ │ + /** │ │ │ │ + * APIMethod: addControls │ │ │ │ + * To build a toolbar, you add a set of controls to it. addControls │ │ │ │ + * lets you add a single control or a list of controls to the │ │ │ │ + * Control Panel. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * controls - {<OpenLayers.Control>} Controls to add in the panel. │ │ │ │ + */ │ │ │ │ + addControls: function(controls) { │ │ │ │ + if (!(OpenLayers.Util.isArray(controls))) { │ │ │ │ + controls = [controls]; │ │ │ │ + } │ │ │ │ + this.controls = this.controls.concat(controls); │ │ │ │ │ │ │ │ - this.closeDiv.style.right = contentDivPadding.right + │ │ │ │ - this.padding.right + "px"; │ │ │ │ - this.closeDiv.style.top = contentDivPadding.top + │ │ │ │ - this.padding.top + "px"; │ │ │ │ + for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ + var control = controls[i], │ │ │ │ + element = this.createControlMarkup(control); │ │ │ │ + OpenLayers.Element.addClass(element, │ │ │ │ + control.displayClass + "ItemInactive"); │ │ │ │ + OpenLayers.Element.addClass(element, "olButton"); │ │ │ │ + if (control.title != "" && !element.title) { │ │ │ │ + element.title = control.title; │ │ │ │ } │ │ │ │ + control.panel_div = element; │ │ │ │ + } │ │ │ │ │ │ │ │ - this.updateBlocks(); │ │ │ │ - }, │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Method: calculateNewPx │ │ │ │ - * Besides the standard offset as determined by the Anchored class, our │ │ │ │ - * Framed popups have a special 'offset' property for each of their │ │ │ │ - * positions, which is used to offset the popup relative to its anchor. │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * px - {<OpenLayers.Pixel>} │ │ │ │ - * │ │ │ │ - * Returns: │ │ │ │ - * {<OpenLayers.Pixel>} The the new px position of the popup on the screen │ │ │ │ - * relative to the passed-in px. │ │ │ │ - */ │ │ │ │ - calculateNewPx: function(px) { │ │ │ │ - var newPx = OpenLayers.Popup.Anchored.prototype.calculateNewPx.apply( │ │ │ │ - this, arguments │ │ │ │ - ); │ │ │ │ - │ │ │ │ - newPx = newPx.offset(this.positionBlocks[this.relativePosition].offset); │ │ │ │ - │ │ │ │ - return newPx; │ │ │ │ - }, │ │ │ │ + if (this.map) { // map.addControl() has already been called on the panel │ │ │ │ + this.addControlsToMap(controls); │ │ │ │ + this.redraw(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: createBlocks │ │ │ │ - */ │ │ │ │ - createBlocks: function() { │ │ │ │ - this.blocks = []; │ │ │ │ + /** │ │ │ │ + * APIMethod: createControlMarkup │ │ │ │ + * This function just creates a div for the control. If specific HTML │ │ │ │ + * markup is needed this function can be overridden in specific classes, │ │ │ │ + * or at panel instantiation time: │ │ │ │ + * │ │ │ │ + * Example: │ │ │ │ + * (code) │ │ │ │ + * var panel = new OpenLayers.Control.Panel({ │ │ │ │ + * defaultControl: control, │ │ │ │ + * // ovverride createControlMarkup to create actual buttons │ │ │ │ + * // including texts wrapped into span elements. │ │ │ │ + * createControlMarkup: function(control) { │ │ │ │ + * var button = document.createElement('button'), │ │ │ │ + * span = document.createElement('span'); │ │ │ │ + * if (control.text) { │ │ │ │ + * span.innerHTML = control.text; │ │ │ │ + * } │ │ │ │ + * return button; │ │ │ │ + * } │ │ │ │ + * }); │ │ │ │ + * (end) │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * control - {<OpenLayers.Control>} The control to create the HTML │ │ │ │ + * markup for. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} The markup. │ │ │ │ + */ │ │ │ │ + createControlMarkup: function(control) { │ │ │ │ + return document.createElement("div"); │ │ │ │ + }, │ │ │ │ │ │ │ │ - //since all positions contain the same number of blocks, we can │ │ │ │ - // just pick the first position and use its blocks array to create │ │ │ │ - // our blocks array │ │ │ │ - var firstPosition = null; │ │ │ │ - for (var key in this.positionBlocks) { │ │ │ │ - firstPosition = key; │ │ │ │ - break; │ │ │ │ + /** │ │ │ │ + * Method: addControlsToMap │ │ │ │ + * Only for internal use in draw() and addControls() methods. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * controls - {Array(<OpenLayers.Control>)} Controls to add into map. │ │ │ │ + */ │ │ │ │ + addControlsToMap: function(controls) { │ │ │ │ + var control; │ │ │ │ + for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ + control = controls[i]; │ │ │ │ + if (control.autoActivate === true) { │ │ │ │ + control.autoActivate = false; │ │ │ │ + this.map.addControl(control); │ │ │ │ + control.autoActivate = true; │ │ │ │ + } else { │ │ │ │ + this.map.addControl(control); │ │ │ │ + control.deactivate(); │ │ │ │ } │ │ │ │ + control.events.on({ │ │ │ │ + activate: this.iconOn, │ │ │ │ + deactivate: this.iconOff │ │ │ │ + }); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - var position = this.positionBlocks[firstPosition]; │ │ │ │ - for (var i = 0; i < position.blocks.length; i++) { │ │ │ │ - │ │ │ │ - var block = {}; │ │ │ │ - this.blocks.push(block); │ │ │ │ - │ │ │ │ - var divId = this.id + '_FrameDecorationDiv_' + i; │ │ │ │ - block.div = OpenLayers.Util.createDiv(divId, │ │ │ │ - null, null, null, "absolute", null, "hidden", null │ │ │ │ - ); │ │ │ │ - │ │ │ │ - var imgId = this.id + '_FrameDecorationImg_' + i; │ │ │ │ - var imageCreator = │ │ │ │ - (this.isAlphaImage) ? OpenLayers.Util.createAlphaImageDiv : │ │ │ │ - OpenLayers.Util.createImage; │ │ │ │ - │ │ │ │ - block.image = imageCreator(imgId, │ │ │ │ - null, this.imageSize, this.imageSrc, │ │ │ │ - "absolute", null, null, null │ │ │ │ - ); │ │ │ │ + /** │ │ │ │ + * Method: iconOn │ │ │ │ + * Internal use, for use only with "controls[i].events.on/un". │ │ │ │ + */ │ │ │ │ + iconOn: function() { │ │ │ │ + var d = this.panel_div; // "this" refers to a control on panel! │ │ │ │ + var re = new RegExp("\\b(" + this.displayClass + "Item)Inactive\\b"); │ │ │ │ + d.className = d.className.replace(re, "$1Active"); │ │ │ │ + }, │ │ │ │ │ │ │ │ - block.div.appendChild(block.image); │ │ │ │ - this.groupDiv.appendChild(block.div); │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: iconOff │ │ │ │ + * Internal use, for use only with "controls[i].events.on/un". │ │ │ │ + */ │ │ │ │ + iconOff: function() { │ │ │ │ + var d = this.panel_div; // "this" refers to a control on panel! │ │ │ │ + var re = new RegExp("\\b(" + this.displayClass + "Item)Active\\b"); │ │ │ │ + d.className = d.className.replace(re, "$1Inactive"); │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Method: updateBlocks │ │ │ │ - * Internal method, called on initialize and when the popup's relative │ │ │ │ - * position has changed. This function takes care of re-positioning │ │ │ │ - * the popup's blocks in their appropropriate places. │ │ │ │ - */ │ │ │ │ - updateBlocks: function() { │ │ │ │ - if (!this.blocks) { │ │ │ │ - this.createBlocks(); │ │ │ │ + /** │ │ │ │ + * Method: onButtonClick │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * evt - {Event} │ │ │ │ + */ │ │ │ │ + onButtonClick: function(evt) { │ │ │ │ + var controls = this.controls, │ │ │ │ + button = evt.buttonElement; │ │ │ │ + for (var i = controls.length - 1; i >= 0; --i) { │ │ │ │ + if (controls[i].panel_div === button) { │ │ │ │ + this.activateControl(controls[i]); │ │ │ │ + break; │ │ │ │ } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - if (this.size && this.relativePosition) { │ │ │ │ - var position = this.positionBlocks[this.relativePosition]; │ │ │ │ - for (var i = 0; i < position.blocks.length; i++) { │ │ │ │ - │ │ │ │ - var positionBlock = position.blocks[i]; │ │ │ │ - var block = this.blocks[i]; │ │ │ │ - │ │ │ │ - // adjust sizes │ │ │ │ - var l = positionBlock.anchor.left; │ │ │ │ - var b = positionBlock.anchor.bottom; │ │ │ │ - var r = positionBlock.anchor.right; │ │ │ │ - var t = positionBlock.anchor.top; │ │ │ │ - │ │ │ │ - //note that we use the isNaN() test here because if the │ │ │ │ - // size object is initialized with a "auto" parameter, the │ │ │ │ - // size constructor calls parseFloat() on the string, │ │ │ │ - // which will turn it into NaN │ │ │ │ - // │ │ │ │ - var w = (isNaN(positionBlock.size.w)) ? this.size.w - (r + l) : │ │ │ │ - positionBlock.size.w; │ │ │ │ - │ │ │ │ - var h = (isNaN(positionBlock.size.h)) ? this.size.h - (b + t) : │ │ │ │ - positionBlock.size.h; │ │ │ │ - │ │ │ │ - block.div.style.width = (w < 0 ? 0 : w) + 'px'; │ │ │ │ - block.div.style.height = (h < 0 ? 0 : h) + 'px'; │ │ │ │ + /** │ │ │ │ + * APIMethod: getControlsBy │ │ │ │ + * Get a list of controls with properties matching the given criteria. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * property - {String} A control property to be matched. │ │ │ │ + * match - {String | Object} A string to match. Can also be a regular │ │ │ │ + * expression literal or object. In addition, it can be any object │ │ │ │ + * with a method named test. For reqular expressions or other, if │ │ │ │ + * match.test(control[property]) evaluates to true, the control will be │ │ │ │ + * included in the array returned. If no controls are found, an empty │ │ │ │ + * array is returned. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Control>)} A list of controls matching the given criteria. │ │ │ │ + * An empty array is returned if no matches are found. │ │ │ │ + */ │ │ │ │ + getControlsBy: function(property, match) { │ │ │ │ + var test = (typeof match.test == "function"); │ │ │ │ + var found = OpenLayers.Array.filter(this.controls, function(item) { │ │ │ │ + return item[property] == match || (test && match.test(item[property])); │ │ │ │ + }); │ │ │ │ + return found; │ │ │ │ + }, │ │ │ │ │ │ │ │ - block.div.style.left = (l != null) ? l + 'px' : ''; │ │ │ │ - block.div.style.bottom = (b != null) ? b + 'px' : ''; │ │ │ │ - block.div.style.right = (r != null) ? r + 'px' : ''; │ │ │ │ - block.div.style.top = (t != null) ? t + 'px' : ''; │ │ │ │ + /** │ │ │ │ + * APIMethod: getControlsByName │ │ │ │ + * Get a list of contorls with names matching the given name. │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * match - {String | Object} A control name. The name can also be a regular │ │ │ │ + * expression literal or object. In addition, it can be any object │ │ │ │ + * with a method named test. For reqular expressions or other, if │ │ │ │ + * name.test(control.name) evaluates to true, the control will be included │ │ │ │ + * in the list of controls returned. If no controls are found, an empty │ │ │ │ + * array is returned. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Control>)} A list of controls matching the given name. │ │ │ │ + * An empty array is returned if no matches are found. │ │ │ │ + */ │ │ │ │ + getControlsByName: function(match) { │ │ │ │ + return this.getControlsBy("name", match); │ │ │ │ + }, │ │ │ │ │ │ │ │ - block.image.style.left = positionBlock.position.x + 'px'; │ │ │ │ - block.image.style.top = positionBlock.position.y + 'px'; │ │ │ │ - } │ │ │ │ + /** │ │ │ │ + * APIMethod: getControlsByClass │ │ │ │ + * Get a list of controls of a given type (CLASS_NAME). │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * match - {String | Object} A control class name. The type can also be a │ │ │ │ + * regular expression literal or object. In addition, it can be any │ │ │ │ + * object with a method named test. For reqular expressions or other, │ │ │ │ + * if type.test(control.CLASS_NAME) evaluates to true, the control will │ │ │ │ + * be included in the list of controls returned. If no controls are │ │ │ │ + * found, an empty array is returned. │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {Array(<OpenLayers.Control>)} A list of controls matching the given type. │ │ │ │ + * An empty array is returned if no matches are found. │ │ │ │ + */ │ │ │ │ + getControlsByClass: function(match) { │ │ │ │ + return this.getControlsBy("CLASS_NAME", match); │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.contentDiv.style.left = this.padding.left + "px"; │ │ │ │ - this.contentDiv.style.top = this.padding.top + "px"; │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Panel" │ │ │ │ +}); │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Popup.Framed" │ │ │ │ - }); │ │ │ │ /* ====================================================================== │ │ │ │ - OpenLayers/Popup/FramedCloud.js │ │ │ │ + OpenLayers/Control/Zoom.js │ │ │ │ ====================================================================== */ │ │ │ │ │ │ │ │ /* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for │ │ │ │ * full list of contributors). Published under the 2-clause BSD license. │ │ │ │ * See license.txt in the OpenLayers distribution or repository for the │ │ │ │ * full text of the license. */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * @requires OpenLayers/Popup/Framed.js │ │ │ │ - * @requires OpenLayers/Util.js │ │ │ │ - * @requires OpenLayers/BaseTypes/Bounds.js │ │ │ │ - * @requires OpenLayers/BaseTypes/Pixel.js │ │ │ │ - * @requires OpenLayers/BaseTypes/Size.js │ │ │ │ + * @requires OpenLayers/Control.js │ │ │ │ + * @requires OpenLayers/Events/buttonclick.js │ │ │ │ */ │ │ │ │ │ │ │ │ /** │ │ │ │ - * Class: OpenLayers.Popup.FramedCloud │ │ │ │ - * │ │ │ │ - * Inherits from: │ │ │ │ - * - <OpenLayers.Popup.Framed> │ │ │ │ + * Class: OpenLayers.Control.Zoom │ │ │ │ + * The Zoom control is a pair of +/- links for zooming in and out. │ │ │ │ + * │ │ │ │ + * Inherits from: │ │ │ │ + * - <OpenLayers.Control> │ │ │ │ */ │ │ │ │ -OpenLayers.Popup.FramedCloud = │ │ │ │ - OpenLayers.Class(OpenLayers.Popup.Framed, { │ │ │ │ - │ │ │ │ - /** │ │ │ │ - * Property: contentDisplayClass │ │ │ │ - * {String} The CSS class of the popup content div. │ │ │ │ - */ │ │ │ │ - contentDisplayClass: "olFramedCloudPopupContent", │ │ │ │ +OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: autoSize │ │ │ │ - * {Boolean} Framed Cloud is autosizing by default. │ │ │ │ - */ │ │ │ │ - autoSize: true, │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomInText │ │ │ │ + * {String} │ │ │ │ + * Text for zoom-in link. Default is "+". │ │ │ │ + */ │ │ │ │ + zoomInText: "+", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: panMapIfOutOfView │ │ │ │ - * {Boolean} Framed Cloud does pan into view by default. │ │ │ │ - */ │ │ │ │ - panMapIfOutOfView: true, │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomInId │ │ │ │ + * {String} │ │ │ │ + * Instead of having the control create a zoom in link, you can provide │ │ │ │ + * the identifier for an anchor element already added to the document. │ │ │ │ + * By default, an element with id "olZoomInLink" will be searched for │ │ │ │ + * and used if it exists. │ │ │ │ + */ │ │ │ │ + zoomInId: "olZoomInLink", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: imageSize │ │ │ │ - * {<OpenLayers.Size>} │ │ │ │ - */ │ │ │ │ - imageSize: new OpenLayers.Size(1276, 736), │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomOutText │ │ │ │ + * {String} │ │ │ │ + * Text for zoom-out link. Default is "\u2212". │ │ │ │ + */ │ │ │ │ + zoomOutText: "\u2212", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: isAlphaImage │ │ │ │ - * {Boolean} The FramedCloud does not use an alpha image (in honor of the │ │ │ │ - * good ie6 folk out there) │ │ │ │ - */ │ │ │ │ - isAlphaImage: false, │ │ │ │ + /** │ │ │ │ + * APIProperty: zoomOutId │ │ │ │ + * {String} │ │ │ │ + * Instead of having the control create a zoom out link, you can provide │ │ │ │ + * the identifier for an anchor element already added to the document. │ │ │ │ + * By default, an element with id "olZoomOutLink" will be searched for │ │ │ │ + * and used if it exists. │ │ │ │ + */ │ │ │ │ + zoomOutId: "olZoomOutLink", │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: fixedRelativePosition │ │ │ │ - * {Boolean} The Framed Cloud popup works in just one fixed position. │ │ │ │ - */ │ │ │ │ - fixedRelativePosition: false, │ │ │ │ + /** │ │ │ │ + * Method: draw │ │ │ │ + * │ │ │ │ + * Returns: │ │ │ │ + * {DOMElement} A reference to the DOMElement containing the zoom links. │ │ │ │ + */ │ │ │ │ + draw: function() { │ │ │ │ + var div = OpenLayers.Control.prototype.draw.apply(this), │ │ │ │ + links = this.getOrCreateLinks(div), │ │ │ │ + zoomIn = links.zoomIn, │ │ │ │ + zoomOut = links.zoomOut, │ │ │ │ + eventsInstance = this.map.events; │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Property: positionBlocks │ │ │ │ - * {Object} Hash of differen position blocks, keyed by relativePosition │ │ │ │ - * two-character code string (ie "tl", "tr", "bl", "br") │ │ │ │ - */ │ │ │ │ - positionBlocks: { │ │ │ │ - "tl": { │ │ │ │ - 'offset': new OpenLayers.Pixel(44, 0), │ │ │ │ - 'padding': new OpenLayers.Bounds(8, 40, 8, 9), │ │ │ │ - 'blocks': [{ // top-left │ │ │ │ - size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 51, 22, 0), │ │ │ │ - position: new OpenLayers.Pixel(0, 0) │ │ │ │ - }, { //top-right │ │ │ │ - size: new OpenLayers.Size(22, 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 50, 0, 0), │ │ │ │ - position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ - }, { //bottom-left │ │ │ │ - size: new OpenLayers.Size('auto', 19), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 32, 22, null), │ │ │ │ - position: new OpenLayers.Pixel(0, -631) │ │ │ │ - }, { //bottom-right │ │ │ │ - size: new OpenLayers.Size(22, 18), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 32, 0, null), │ │ │ │ - position: new OpenLayers.Pixel(-1238, -632) │ │ │ │ - }, { // stem │ │ │ │ - size: new OpenLayers.Size(81, 35), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 0, 0, null), │ │ │ │ - position: new OpenLayers.Pixel(0, -688) │ │ │ │ - }] │ │ │ │ - }, │ │ │ │ - "tr": { │ │ │ │ - 'offset': new OpenLayers.Pixel(-45, 0), │ │ │ │ - 'padding': new OpenLayers.Bounds(8, 40, 8, 9), │ │ │ │ - 'blocks': [{ // top-left │ │ │ │ - size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 51, 22, 0), │ │ │ │ - position: new OpenLayers.Pixel(0, 0) │ │ │ │ - }, { //top-right │ │ │ │ - size: new OpenLayers.Size(22, 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 50, 0, 0), │ │ │ │ - position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ - }, { //bottom-left │ │ │ │ - size: new OpenLayers.Size('auto', 19), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 32, 22, null), │ │ │ │ - position: new OpenLayers.Pixel(0, -631) │ │ │ │ - }, { //bottom-right │ │ │ │ - size: new OpenLayers.Size(22, 19), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 32, 0, null), │ │ │ │ - position: new OpenLayers.Pixel(-1238, -631) │ │ │ │ - }, { // stem │ │ │ │ - size: new OpenLayers.Size(81, 35), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 0, null, null), │ │ │ │ - position: new OpenLayers.Pixel(-215, -687) │ │ │ │ - }] │ │ │ │ - }, │ │ │ │ - "bl": { │ │ │ │ - 'offset': new OpenLayers.Pixel(45, 0), │ │ │ │ - 'padding': new OpenLayers.Bounds(8, 9, 8, 40), │ │ │ │ - 'blocks': [{ // top-left │ │ │ │ - size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 21, 22, 32), │ │ │ │ - position: new OpenLayers.Pixel(0, 0) │ │ │ │ - }, { //top-right │ │ │ │ - size: new OpenLayers.Size(22, 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 21, 0, 32), │ │ │ │ - position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ - }, { //bottom-left │ │ │ │ - size: new OpenLayers.Size('auto', 21), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 0, 22, null), │ │ │ │ - position: new OpenLayers.Pixel(0, -629) │ │ │ │ - }, { //bottom-right │ │ │ │ - size: new OpenLayers.Size(22, 21), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 0, 0, null), │ │ │ │ - position: new OpenLayers.Pixel(-1238, -629) │ │ │ │ - }, { // stem │ │ │ │ - size: new OpenLayers.Size(81, 33), │ │ │ │ - anchor: new OpenLayers.Bounds(null, null, 0, 0), │ │ │ │ - position: new OpenLayers.Pixel(-101, -674) │ │ │ │ - }] │ │ │ │ - }, │ │ │ │ - "br": { │ │ │ │ - 'offset': new OpenLayers.Pixel(-44, 0), │ │ │ │ - 'padding': new OpenLayers.Bounds(8, 9, 8, 40), │ │ │ │ - 'blocks': [{ // top-left │ │ │ │ - size: new OpenLayers.Size('auto', 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 21, 22, 32), │ │ │ │ - position: new OpenLayers.Pixel(0, 0) │ │ │ │ - }, { //top-right │ │ │ │ - size: new OpenLayers.Size(22, 'auto'), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 21, 0, 32), │ │ │ │ - position: new OpenLayers.Pixel(-1238, 0) │ │ │ │ - }, { //bottom-left │ │ │ │ - size: new OpenLayers.Size('auto', 21), │ │ │ │ - anchor: new OpenLayers.Bounds(0, 0, 22, null), │ │ │ │ - position: new OpenLayers.Pixel(0, -629) │ │ │ │ - }, { //bottom-right │ │ │ │ - size: new OpenLayers.Size(22, 21), │ │ │ │ - anchor: new OpenLayers.Bounds(null, 0, 0, null), │ │ │ │ - position: new OpenLayers.Pixel(-1238, -629) │ │ │ │ - }, { // stem │ │ │ │ - size: new OpenLayers.Size(81, 33), │ │ │ │ - anchor: new OpenLayers.Bounds(0, null, null, 0), │ │ │ │ - position: new OpenLayers.Pixel(-311, -674) │ │ │ │ - }] │ │ │ │ - } │ │ │ │ - }, │ │ │ │ + if (zoomOut.parentNode !== div) { │ │ │ │ + eventsInstance = this.events; │ │ │ │ + eventsInstance.attachToElement(zoomOut.parentNode); │ │ │ │ + } │ │ │ │ + eventsInstance.register("buttonclick", this, this.onZoomClick); │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: minSize │ │ │ │ - * {<OpenLayers.Size>} │ │ │ │ - */ │ │ │ │ - minSize: new OpenLayers.Size(105, 10), │ │ │ │ + this.zoomInLink = zoomIn; │ │ │ │ + this.zoomOutLink = zoomOut; │ │ │ │ + return div; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * APIProperty: maxSize │ │ │ │ - * {<OpenLayers.Size>} │ │ │ │ - */ │ │ │ │ - maxSize: new OpenLayers.Size(1200, 660), │ │ │ │ + /** │ │ │ │ + * Method: getOrCreateLinks │ │ │ │ + * │ │ │ │ + * Parameters: │ │ │ │ + * el - {DOMElement} │ │ │ │ + * │ │ │ │ + * Return: │ │ │ │ + * {Object} Object with zoomIn and zoomOut properties referencing links. │ │ │ │ + */ │ │ │ │ + getOrCreateLinks: function(el) { │ │ │ │ + var zoomIn = document.getElementById(this.zoomInId), │ │ │ │ + zoomOut = document.getElementById(this.zoomOutId); │ │ │ │ + if (!zoomIn) { │ │ │ │ + zoomIn = document.createElement("a"); │ │ │ │ + zoomIn.href = "#zoomIn"; │ │ │ │ + zoomIn.appendChild(document.createTextNode(this.zoomInText)); │ │ │ │ + zoomIn.className = "olControlZoomIn"; │ │ │ │ + el.appendChild(zoomIn); │ │ │ │ + } │ │ │ │ + OpenLayers.Element.addClass(zoomIn, "olButton"); │ │ │ │ + if (!zoomOut) { │ │ │ │ + zoomOut = document.createElement("a"); │ │ │ │ + zoomOut.href = "#zoomOut"; │ │ │ │ + zoomOut.appendChild(document.createTextNode(this.zoomOutText)); │ │ │ │ + zoomOut.className = "olControlZoomOut"; │ │ │ │ + el.appendChild(zoomOut); │ │ │ │ + } │ │ │ │ + OpenLayers.Element.addClass(zoomOut, "olButton"); │ │ │ │ + return { │ │ │ │ + zoomIn: zoomIn, │ │ │ │ + zoomOut: zoomOut │ │ │ │ + }; │ │ │ │ + }, │ │ │ │ │ │ │ │ - /** │ │ │ │ - * Constructor: OpenLayers.Popup.FramedCloud │ │ │ │ - * │ │ │ │ - * Parameters: │ │ │ │ - * id - {String} │ │ │ │ - * lonlat - {<OpenLayers.LonLat>} │ │ │ │ - * contentSize - {<OpenLayers.Size>} │ │ │ │ - * contentHTML - {String} │ │ │ │ - * anchor - {Object} Object to which we'll anchor the popup. Must expose │ │ │ │ - * a 'size' (<OpenLayers.Size>) and 'offset' (<OpenLayers.Pixel>) │ │ │ │ - * (Note that this is generally an <OpenLayers.Icon>). │ │ │ │ - * closeBox - {Boolean} │ │ │ │ - * closeBoxCallback - {Function} Function to be called on closeBox click. │ │ │ │ - */ │ │ │ │ - initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, │ │ │ │ - closeBoxCallback) { │ │ │ │ + /** │ │ │ │ + * Method: onZoomClick │ │ │ │ + * Called when zoomin/out link is clicked. │ │ │ │ + */ │ │ │ │ + onZoomClick: function(evt) { │ │ │ │ + var button = evt.buttonElement; │ │ │ │ + if (button === this.zoomInLink) { │ │ │ │ + this.map.zoomIn(); │ │ │ │ + } else if (button === this.zoomOutLink) { │ │ │ │ + this.map.zoomOut(); │ │ │ │ + } │ │ │ │ + }, │ │ │ │ │ │ │ │ - this.imageSrc = OpenLayers.Util.getImageLocation('cloud-popup-relative.png'); │ │ │ │ - OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments); │ │ │ │ - this.contentDiv.className = this.contentDisplayClass; │ │ │ │ - }, │ │ │ │ + /** │ │ │ │ + * Method: destroy │ │ │ │ + * Clean up. │ │ │ │ + */ │ │ │ │ + destroy: function() { │ │ │ │ + if (this.map) { │ │ │ │ + this.map.events.unregister("buttonclick", this, this.onZoomClick); │ │ │ │ + } │ │ │ │ + delete this.zoomInLink; │ │ │ │ + delete this.zoomOutLink; │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this); │ │ │ │ + }, │ │ │ │ │ │ │ │ - CLASS_NAME: "OpenLayers.Popup.FramedCloud" │ │ │ │ - }); │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Zoom" │ │ │ │ +}); │ │ ├── ./usr/share/javascript/openlayers/OpenLayers.light.min.js │ │ │ ├── js-beautify {} │ │ │ │ @@ -2053,99 +2053,219 @@ │ │ │ │ if (typeof value == "string" && value.indexOf("${") != -1) { │ │ │ │ value = OpenLayers.String.format(value, context, [feature, property]); │ │ │ │ value = isNaN(value) || !value ? value : parseFloat(value) │ │ │ │ } │ │ │ │ return value │ │ │ │ }; │ │ │ │ OpenLayers.Style.SYMBOLIZER_PREFIXES = ["Point", "Line", "Polygon", "Text", "Raster"]; │ │ │ │ -OpenLayers.Rule = OpenLayers.Class({ │ │ │ │ - id: null, │ │ │ │ - name: null, │ │ │ │ - title: null, │ │ │ │ - description: null, │ │ │ │ - context: null, │ │ │ │ - filter: null, │ │ │ │ - elseFilter: false, │ │ │ │ - symbolizer: null, │ │ │ │ - symbolizers: null, │ │ │ │ - minScaleDenominator: null, │ │ │ │ - maxScaleDenominator: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - this.symbolizer = {}; │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - if (this.symbolizers) { │ │ │ │ - delete this.symbolizer │ │ │ │ +OpenLayers.StyleMap = OpenLayers.Class({ │ │ │ │ + styles: null, │ │ │ │ + extendDefault: true, │ │ │ │ + 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"]) │ │ │ │ + }; │ │ │ │ + if (style instanceof OpenLayers.Style) { │ │ │ │ + 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) { │ │ │ │ + this.styles[key] = style[key] │ │ │ │ + } else if (typeof style[key] == "object") { │ │ │ │ + this.styles[key] = new OpenLayers.Style(style[key]) │ │ │ │ + } else { │ │ │ │ + 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.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_") │ │ │ │ + OpenLayers.Util.extend(this, options) │ │ │ │ }, │ │ │ │ destroy: function() { │ │ │ │ - for (var i in this.symbolizer) { │ │ │ │ - this.symbolizer[i] = null │ │ │ │ + for (var key in this.styles) { │ │ │ │ + this.styles[key].destroy() │ │ │ │ } │ │ │ │ - this.symbolizer = null; │ │ │ │ - delete this.symbolizers │ │ │ │ + this.styles = null │ │ │ │ }, │ │ │ │ - evaluate: function(feature) { │ │ │ │ - var context = this.getContext(feature); │ │ │ │ - var applies = true; │ │ │ │ - if (this.minScaleDenominator || this.maxScaleDenominator) { │ │ │ │ - var scale = feature.layer.map.getScale() │ │ │ │ - } │ │ │ │ - if (this.minScaleDenominator) { │ │ │ │ - applies = scale >= OpenLayers.Style.createLiteral(this.minScaleDenominator, context) │ │ │ │ + createSymbolizer: function(feature, intent) { │ │ │ │ + if (!feature) { │ │ │ │ + feature = new OpenLayers.Feature.Vector │ │ │ │ } │ │ │ │ - if (applies && this.maxScaleDenominator) { │ │ │ │ - applies = scale < OpenLayers.Style.createLiteral(this.maxScaleDenominator, context) │ │ │ │ + if (!this.styles[intent]) { │ │ │ │ + intent = "default" │ │ │ │ } │ │ │ │ - if (applies && this.filter) { │ │ │ │ - if (this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") { │ │ │ │ - applies = this.filter.evaluate(feature) │ │ │ │ - } else { │ │ │ │ - applies = this.filter.evaluate(context) │ │ │ │ - } │ │ │ │ + feature.renderIntent = intent; │ │ │ │ + var defaultSymbolizer = {}; │ │ │ │ + if (this.extendDefault && intent != "default") { │ │ │ │ + defaultSymbolizer = this.styles["default"].createSymbolizer(feature) │ │ │ │ } │ │ │ │ - return applies │ │ │ │ + return OpenLayers.Util.extend(defaultSymbolizer, this.styles[intent].createSymbolizer(feature)) │ │ │ │ }, │ │ │ │ - getContext: function(feature) { │ │ │ │ - var context = this.context; │ │ │ │ - if (!context) { │ │ │ │ - context = feature.attributes || feature.data │ │ │ │ + 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 │ │ │ │ + }) │ │ │ │ + })) │ │ │ │ } │ │ │ │ - if (typeof this.context == "function") { │ │ │ │ - context = this.context(feature) │ │ │ │ + this.styles[renderIntent].addRules(rules) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.StyleMap" │ │ │ │ +}); │ │ │ │ +OpenLayers.Projection = OpenLayers.Class({ │ │ │ │ + proj: null, │ │ │ │ + projCode: null, │ │ │ │ + titleRegEx: /\+title=[^\+]*/, │ │ │ │ + initialize: function(projCode, options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.projCode = projCode; │ │ │ │ + if (typeof Proj4js == "object") { │ │ │ │ + this.proj = new Proj4js.Proj(projCode) │ │ │ │ } │ │ │ │ - return context │ │ │ │ }, │ │ │ │ - clone: function() { │ │ │ │ - var options = OpenLayers.Util.extend({}, this); │ │ │ │ - if (this.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() │ │ │ │ + getCode: function() { │ │ │ │ + return this.proj ? this.proj.srsCode : this.projCode │ │ │ │ + }, │ │ │ │ + getUnits: function() { │ │ │ │ + return this.proj ? this.proj.units : null │ │ │ │ + }, │ │ │ │ + toString: function() { │ │ │ │ + return this.getCode() │ │ │ │ + }, │ │ │ │ + equals: function(projection) { │ │ │ │ + var p = projection, │ │ │ │ + equals = false; │ │ │ │ + if (p) { │ │ │ │ + if (!(p instanceof OpenLayers.Projection)) { │ │ │ │ + p = new OpenLayers.Projection(p) │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ + 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 │ │ │ │ } │ │ │ │ } │ │ │ │ - options.filter = this.filter && this.filter.clone(); │ │ │ │ - options.context = this.context && OpenLayers.Util.extend({}, this.context); │ │ │ │ - return new OpenLayers.Rule(options) │ │ │ │ + return equals │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Rule" │ │ │ │ + destroy: function() { │ │ │ │ + delete this.proj; │ │ │ │ + delete this.projCode │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Projection" │ │ │ │ }); │ │ │ │ +OpenLayers.Projection.transforms = {}; │ │ │ │ +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] │ │ │ │ + } │ │ │ │ +}; │ │ │ │ +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 │ │ │ │ +}; │ │ │ │ +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 │ │ │ │ +}; │ │ │ │ +OpenLayers.Projection.nullTransform = function(point) { │ │ │ │ + return point │ │ │ │ +}; │ │ │ │ +(function() { │ │ │ │ + var pole = 20037508.34; │ │ │ │ + │ │ │ │ + function inverseMercator(xy) { │ │ │ │ + xy.x = 180 * xy.x / pole; │ │ │ │ + xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp(xy.y / pole * Math.PI)) - Math.PI / 2); │ │ │ │ + return xy │ │ │ │ + } │ │ │ │ + │ │ │ │ + function forwardMercator(xy) { │ │ │ │ + xy.x = xy.x * pole / 180; │ │ │ │ + var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole; │ │ │ │ + xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34)); │ │ │ │ + return xy │ │ │ │ + } │ │ │ │ + │ │ │ │ + function map(base, codes) { │ │ │ │ + var add = OpenLayers.Projection.addTransform; │ │ │ │ + var same = OpenLayers.Projection.nullTransform; │ │ │ │ + var i, len, code, other, j; │ │ │ │ + for (i = 0, len = codes.length; i < len; ++i) { │ │ │ │ + code = codes[i]; │ │ │ │ + add(base, code, forwardMercator); │ │ │ │ + add(code, base, inverseMercator); │ │ │ │ + for (j = i + 1; j < len; ++j) { │ │ │ │ + other = codes[j]; │ │ │ │ + add(code, other, same); │ │ │ │ + add(other, code, same) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + 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.Util = OpenLayers.Util || {}; │ │ │ │ OpenLayers.Util.vendorPrefix = function() { │ │ │ │ "use strict"; │ │ │ │ var VENDOR_PREFIXES = ["", "O", "ms", "Moz", "Webkit"], │ │ │ │ divStyle = document.createElement("div").style, │ │ │ │ cssCache = {}, │ │ │ │ jsCache = {}; │ │ │ │ @@ -2809,150 +2929,14 @@ │ │ │ │ }, │ │ │ │ easeInOut: function(t, b, c, d) { │ │ │ │ if ((t /= d / 2) < 1) return c / 2 * t * t + b; │ │ │ │ return -c / 2 * (--t * (t - 2) - 1) + b │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.Easing.Quad" │ │ │ │ }; │ │ │ │ -OpenLayers.Projection = OpenLayers.Class({ │ │ │ │ - proj: null, │ │ │ │ - projCode: null, │ │ │ │ - titleRegEx: /\+title=[^\+]*/, │ │ │ │ - initialize: function(projCode, options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.projCode = projCode; │ │ │ │ - if (typeof Proj4js == "object") { │ │ │ │ - this.proj = new Proj4js.Proj(projCode) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getCode: function() { │ │ │ │ - return this.proj ? this.proj.srsCode : this.projCode │ │ │ │ - }, │ │ │ │ - getUnits: function() { │ │ │ │ - return this.proj ? this.proj.units : null │ │ │ │ - }, │ │ │ │ - toString: function() { │ │ │ │ - return this.getCode() │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - delete this.proj; │ │ │ │ - delete this.projCode │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Projection" │ │ │ │ -}); │ │ │ │ -OpenLayers.Projection.transforms = {}; │ │ │ │ -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] │ │ │ │ - } │ │ │ │ -}; │ │ │ │ -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 │ │ │ │ -}; │ │ │ │ -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 │ │ │ │ -}; │ │ │ │ -OpenLayers.Projection.nullTransform = function(point) { │ │ │ │ - return point │ │ │ │ -}; │ │ │ │ -(function() { │ │ │ │ - var pole = 20037508.34; │ │ │ │ - │ │ │ │ - function inverseMercator(xy) { │ │ │ │ - xy.x = 180 * xy.x / pole; │ │ │ │ - xy.y = 180 / Math.PI * (2 * Math.atan(Math.exp(xy.y / pole * Math.PI)) - Math.PI / 2); │ │ │ │ - return xy │ │ │ │ - } │ │ │ │ - │ │ │ │ - function forwardMercator(xy) { │ │ │ │ - xy.x = xy.x * pole / 180; │ │ │ │ - var y = Math.log(Math.tan((90 + xy.y) * Math.PI / 360)) / Math.PI * pole; │ │ │ │ - xy.y = Math.max(-20037508.34, Math.min(y, 20037508.34)); │ │ │ │ - return xy │ │ │ │ - } │ │ │ │ - │ │ │ │ - function map(base, codes) { │ │ │ │ - var add = OpenLayers.Projection.addTransform; │ │ │ │ - var same = OpenLayers.Projection.nullTransform; │ │ │ │ - var i, len, code, other, j; │ │ │ │ - for (i = 0, len = codes.length; i < len; ++i) { │ │ │ │ - code = codes[i]; │ │ │ │ - add(base, code, forwardMercator); │ │ │ │ - add(code, base, inverseMercator); │ │ │ │ - for (j = i + 1; j < len; ++j) { │ │ │ │ - other = codes[j]; │ │ │ │ - add(code, other, same); │ │ │ │ - add(other, code, same) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var mercator = ["EPSG:900913", "EPSG:3857", "EPSG:102113", "EPSG:102100"], │ │ │ │ - geographic = ["CRS:84", "urn:ogc:def:crs:EPSG:6.6:4326", "EPSG:4326"], │ │ │ │ - i; │ │ │ │ - for (i = mercator.length - 1; i >= 0; --i) { │ │ │ │ - map(mercator[i], geographic) │ │ │ │ - } │ │ │ │ - for (i = geographic.length - 1; i >= 0; --i) { │ │ │ │ - map(geographic[i], mercator) │ │ │ │ - } │ │ │ │ -})(); │ │ │ │ OpenLayers.Map = OpenLayers.Class({ │ │ │ │ Z_INDEX_BASE: { │ │ │ │ BaseLayer: 100, │ │ │ │ Overlay: 325, │ │ │ │ Feature: 725, │ │ │ │ Popup: 750, │ │ │ │ Control: 1e3 │ │ │ │ @@ -4130,83 +4114,14 @@ │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.Map" │ │ │ │ }); │ │ │ │ OpenLayers.Map.TILE_WIDTH = 256; │ │ │ │ OpenLayers.Map.TILE_HEIGHT = 256; │ │ │ │ -OpenLayers.StyleMap = OpenLayers.Class({ │ │ │ │ - styles: null, │ │ │ │ - extendDefault: true, │ │ │ │ - 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"]) │ │ │ │ - }; │ │ │ │ - if (style instanceof OpenLayers.Style) { │ │ │ │ - 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) { │ │ │ │ - this.styles[key] = style[key] │ │ │ │ - } else if (typeof style[key] == "object") { │ │ │ │ - this.styles[key] = new OpenLayers.Style(style[key]) │ │ │ │ - } else { │ │ │ │ - this.styles["default"] = new OpenLayers.Style(style); │ │ │ │ - this.styles["select"] = new OpenLayers.Style(style); │ │ │ │ - this.styles["temporary"] = new OpenLayers.Style(style); │ │ │ │ - this.styles["delete"] = new OpenLayers.Style(style); │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Util.extend(this, options) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - for (var key in this.styles) { │ │ │ │ - this.styles[key].destroy() │ │ │ │ - } │ │ │ │ - this.styles = null │ │ │ │ - }, │ │ │ │ - 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)) │ │ │ │ - }, │ │ │ │ - addUniqueValueRules: function(renderIntent, property, symbolizers, context) { │ │ │ │ - var rules = []; │ │ │ │ - for (var value in symbolizers) { │ │ │ │ - rules.push(new OpenLayers.Rule({ │ │ │ │ - symbolizer: symbolizers[value], │ │ │ │ - context: context, │ │ │ │ - filter: new OpenLayers.Filter.Comparison({ │ │ │ │ - type: OpenLayers.Filter.Comparison.EQUAL_TO, │ │ │ │ - property: property, │ │ │ │ - value: value │ │ │ │ - }) │ │ │ │ - })) │ │ │ │ - } │ │ │ │ - this.styles[renderIntent].addRules(rules) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.StyleMap" │ │ │ │ -}); │ │ │ │ OpenLayers.Kinetic = OpenLayers.Class({ │ │ │ │ threshold: 0, │ │ │ │ deceleration: .0035, │ │ │ │ nbPoints: 100, │ │ │ │ delay: 200, │ │ │ │ points: undefined, │ │ │ │ timerId: undefined, │ │ │ │ @@ -4283,1734 +4198,391 @@ │ │ │ │ lastY = y; │ │ │ │ callback(args.x, args.y, args.end) │ │ │ │ }; │ │ │ │ this.timerId = OpenLayers.Animation.start(OpenLayers.Function.bind(timerCallback, this)) │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.Kinetic" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer = OpenLayers.Class({ │ │ │ │ +OpenLayers.Rule = OpenLayers.Class({ │ │ │ │ id: null, │ │ │ │ name: null, │ │ │ │ - div: null, │ │ │ │ - opacity: 1, │ │ │ │ - alwaysInRange: null, │ │ │ │ - RESOLUTION_PROPERTIES: ["scales", "resolutions", "maxScale", "minScale", "maxResolution", "minResolution", "numZoomLevels", "maxZoomLevel"], │ │ │ │ - events: null, │ │ │ │ - map: null, │ │ │ │ - isBaseLayer: false, │ │ │ │ - alpha: false, │ │ │ │ - displayInLayerSwitcher: true, │ │ │ │ - visibility: true, │ │ │ │ - attribution: null, │ │ │ │ - inRange: false, │ │ │ │ - imageSize: null, │ │ │ │ - options: null, │ │ │ │ - eventListeners: null, │ │ │ │ - gutter: 0, │ │ │ │ - projection: null, │ │ │ │ - units: null, │ │ │ │ - scales: null, │ │ │ │ - resolutions: null, │ │ │ │ - maxExtent: null, │ │ │ │ - minExtent: null, │ │ │ │ - maxResolution: null, │ │ │ │ - minResolution: null, │ │ │ │ - numZoomLevels: null, │ │ │ │ - minScale: null, │ │ │ │ - maxScale: null, │ │ │ │ - displayOutsideMaxExtent: false, │ │ │ │ - wrapDateLine: false, │ │ │ │ - metadata: null, │ │ │ │ - initialize: function(name, options) { │ │ │ │ - this.metadata = {}; │ │ │ │ - options = OpenLayers.Util.extend({}, options); │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - destroy: function(setNewBaseLayer) { │ │ │ │ - if (setNewBaseLayer == null) { │ │ │ │ - setNewBaseLayer = true │ │ │ │ - } │ │ │ │ - if (this.map != null) { │ │ │ │ - this.map.removeLayer(this, setNewBaseLayer) │ │ │ │ - } │ │ │ │ - this.projection = null; │ │ │ │ - this.map = null; │ │ │ │ - this.name = null; │ │ │ │ - this.div = null; │ │ │ │ - this.options = null; │ │ │ │ - if (this.events) { │ │ │ │ - if (this.eventListeners) { │ │ │ │ - this.events.un(this.eventListeners) │ │ │ │ - } │ │ │ │ - this.events.destroy() │ │ │ │ - } │ │ │ │ - this.eventListeners = null; │ │ │ │ - this.events = null │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer(this.name, this.getOptions()) │ │ │ │ - } │ │ │ │ - OpenLayers.Util.applyDefaults(obj, this); │ │ │ │ - obj.map = null; │ │ │ │ - return obj │ │ │ │ - }, │ │ │ │ - getOptions: function() { │ │ │ │ - var options = {}; │ │ │ │ - for (var o in this.options) { │ │ │ │ - options[o] = this[o] │ │ │ │ - } │ │ │ │ - return options │ │ │ │ - }, │ │ │ │ - setName: function(newName) { │ │ │ │ - if (newName != this.name) { │ │ │ │ - this.name = newName; │ │ │ │ - if (this.map != null) { │ │ │ │ - this.map.events.triggerEvent("changelayer", { │ │ │ │ - layer: this, │ │ │ │ - property: "name" │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - addOptions: function(newOptions, reinitialize) { │ │ │ │ - if (this.options == null) { │ │ │ │ - this.options = {} │ │ │ │ - } │ │ │ │ - if (newOptions) { │ │ │ │ - if (typeof newOptions.projection == "string") { │ │ │ │ - newOptions.projection = new OpenLayers.Projection(newOptions.projection) │ │ │ │ - } │ │ │ │ - if (newOptions.projection) { │ │ │ │ - OpenLayers.Util.applyDefaults(newOptions, OpenLayers.Projection.defaults[newOptions.projection.getCode()]) │ │ │ │ - } │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Util.extend(this.options, newOptions); │ │ │ │ - OpenLayers.Util.extend(this, newOptions); │ │ │ │ - if (this.projection && this.projection.getUnits()) { │ │ │ │ - this.units = this.projection.getUnits() │ │ │ │ - } │ │ │ │ - if (this.map) { │ │ │ │ - var resolution = this.map.getResolution(); │ │ │ │ - var properties = this.RESOLUTION_PROPERTIES.concat(["projection", "units", "minExtent", "maxExtent"]); │ │ │ │ - for (var o in newOptions) { │ │ │ │ - if (newOptions.hasOwnProperty(o) && OpenLayers.Util.indexOf(properties, o) >= 0) { │ │ │ │ - this.initResolutions(); │ │ │ │ - if (reinitialize && this.map.baseLayer === this) { │ │ │ │ - this.map.setCenter(this.map.getCenter(), this.map.getZoomForResolution(resolution), false, true); │ │ │ │ - this.map.events.triggerEvent("changebaselayer", { │ │ │ │ - layer: this │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - onMapResize: function() {}, │ │ │ │ - redraw: function() { │ │ │ │ - var redrawn = false; │ │ │ │ - if (this.map) { │ │ │ │ - this.inRange = this.calculateInRange(); │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - var display = this.visibility; │ │ │ │ - if (!this.isBaseLayer) { │ │ │ │ - display = display && this.inRange │ │ │ │ - } │ │ │ │ - this.display(display) │ │ │ │ - }, │ │ │ │ - moveByPx: function(dx, dy) {}, │ │ │ │ - setMap: function(map) { │ │ │ │ - if (this.map == null) { │ │ │ │ - this.map = map; │ │ │ │ - this.maxExtent = this.maxExtent || this.map.maxExtent; │ │ │ │ - this.minExtent = this.minExtent || this.map.minExtent; │ │ │ │ - this.projection = this.projection || this.map.projection; │ │ │ │ - if (typeof this.projection == "string") { │ │ │ │ - this.projection = new OpenLayers.Projection(this.projection) │ │ │ │ - } │ │ │ │ - this.units = this.projection.getUnits() || this.units || this.map.units; │ │ │ │ - this.initResolutions(); │ │ │ │ - if (!this.isBaseLayer) { │ │ │ │ - this.inRange = this.calculateInRange(); │ │ │ │ - var show = this.visibility && this.inRange; │ │ │ │ - this.div.style.display = show ? "" : "none" │ │ │ │ - } │ │ │ │ - this.setTileSize() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - afterAdd: function() {}, │ │ │ │ - removeMap: function(map) {}, │ │ │ │ - getImageSize: function(bounds) { │ │ │ │ - return this.imageSize || this.tileSize │ │ │ │ - }, │ │ │ │ - setTileSize: function(size) { │ │ │ │ - var tileSize = size ? size : this.tileSize ? this.tileSize : this.map.getTileSize(); │ │ │ │ - this.tileSize = tileSize; │ │ │ │ - if (this.gutter) { │ │ │ │ - this.imageSize = new OpenLayers.Size(tileSize.w + 2 * this.gutter, tileSize.h + 2 * this.gutter) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getVisibility: function() { │ │ │ │ - return this.visibility │ │ │ │ - }, │ │ │ │ - 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") │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - display: function(display) { │ │ │ │ - if (display != (this.div.style.display != "none")) { │ │ │ │ - this.div.style.display = display && this.calculateInRange() ? "block" : "none" │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - calculateInRange: function() { │ │ │ │ - var inRange = false; │ │ │ │ - if (this.alwaysInRange) { │ │ │ │ - inRange = true │ │ │ │ - } else { │ │ │ │ - if (this.map) { │ │ │ │ - var resolution = this.map.getResolution(); │ │ │ │ - inRange = resolution >= this.minResolution && resolution <= this.maxResolution │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return inRange │ │ │ │ - }, │ │ │ │ - setIsBaseLayer: function(isBaseLayer) { │ │ │ │ - if (isBaseLayer != this.isBaseLayer) { │ │ │ │ - this.isBaseLayer = isBaseLayer; │ │ │ │ - if (this.map != null) { │ │ │ │ - this.map.events.triggerEvent("changebaselayer", { │ │ │ │ - layer: this │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - initResolutions: function() { │ │ │ │ - var i, len, p; │ │ │ │ - var props = {}, │ │ │ │ - alwaysInRange = true; │ │ │ │ - for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) { │ │ │ │ - p = this.RESOLUTION_PROPERTIES[i]; │ │ │ │ - props[p] = this.options[p]; │ │ │ │ - if (alwaysInRange && this.options[p]) { │ │ │ │ - alwaysInRange = false │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.options.alwaysInRange == null) { │ │ │ │ - this.alwaysInRange = alwaysInRange │ │ │ │ - } │ │ │ │ - if (props.resolutions == null) { │ │ │ │ - props.resolutions = this.resolutionsFromScales(props.scales) │ │ │ │ - } │ │ │ │ - if (props.resolutions == null) { │ │ │ │ - props.resolutions = this.calculateResolutions(props) │ │ │ │ - } │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - var minResolution; │ │ │ │ - if (this.options.minResolution && this.options.minResolution !== "auto") { │ │ │ │ - minResolution = this.options.minResolution │ │ │ │ - } │ │ │ │ - if (this.options.maxScale) { │ │ │ │ - minResolution = OpenLayers.Util.getResolutionFromScale(this.options.maxScale, this.units) │ │ │ │ - } │ │ │ │ - if (props.resolutions) { │ │ │ │ - props.resolutions.sort(function(a, b) { │ │ │ │ - return b - a │ │ │ │ - }); │ │ │ │ - if (!maxResolution) { │ │ │ │ - maxResolution = props.resolutions[0] │ │ │ │ - } │ │ │ │ - if (!minResolution) { │ │ │ │ - var lastIdx = props.resolutions.length - 1; │ │ │ │ - minResolution = props.resolutions[lastIdx] │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.resolutions = props.resolutions; │ │ │ │ - if (this.resolutions) { │ │ │ │ - len = this.resolutions.length; │ │ │ │ - this.scales = new Array(len); │ │ │ │ - for (i = 0; i < len; i++) { │ │ │ │ - this.scales[i] = OpenLayers.Util.getScaleFromResolution(this.resolutions[i], this.units) │ │ │ │ - } │ │ │ │ - this.numZoomLevels = len │ │ │ │ - } │ │ │ │ - this.minResolution = minResolution; │ │ │ │ - if (minResolution) { │ │ │ │ - this.maxScale = OpenLayers.Util.getScaleFromResolution(minResolution, this.units) │ │ │ │ - } │ │ │ │ - this.maxResolution = maxResolution; │ │ │ │ - if (maxResolution) { │ │ │ │ - this.minScale = OpenLayers.Util.getScaleFromResolution(maxResolution, this.units) │ │ │ │ + title: null, │ │ │ │ + description: null, │ │ │ │ + context: null, │ │ │ │ + filter: null, │ │ │ │ + elseFilter: false, │ │ │ │ + symbolizer: null, │ │ │ │ + symbolizers: null, │ │ │ │ + minScaleDenominator: null, │ │ │ │ + maxScaleDenominator: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + this.symbolizer = {}; │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + if (this.symbolizers) { │ │ │ │ + delete this.symbolizer │ │ │ │ } │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_") │ │ │ │ }, │ │ │ │ - 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) │ │ │ │ + destroy: function() { │ │ │ │ + for (var i in this.symbolizer) { │ │ │ │ + this.symbolizer[i] = null │ │ │ │ } │ │ │ │ - return resolutions │ │ │ │ + this.symbolizer = null; │ │ │ │ + delete this.symbolizers │ │ │ │ }, │ │ │ │ - calculateResolutions: function(props) { │ │ │ │ - var viewSize, wRes, hRes; │ │ │ │ - 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 minResolution = props.minResolution; │ │ │ │ - if (props.maxScale != null) { │ │ │ │ - minResolution = OpenLayers.Util.getResolutionFromScale(props.maxScale, this.units) │ │ │ │ - } else if (props.minResolution == "auto" && this.minExtent != null) { │ │ │ │ - viewSize = this.map.getSize(); │ │ │ │ - wRes = this.minExtent.getWidth() / viewSize.w; │ │ │ │ - hRes = this.minExtent.getHeight() / viewSize.h; │ │ │ │ - minResolution = Math.max(wRes, hRes) │ │ │ │ - } │ │ │ │ - if (typeof maxResolution !== "number" && typeof minResolution !== "number" && this.maxExtent != null) { │ │ │ │ - var tileSize = this.map.getTileSize(); │ │ │ │ - maxResolution = Math.max(this.maxExtent.getWidth() / tileSize.w, this.maxExtent.getHeight() / tileSize.h) │ │ │ │ - } │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ - if (typeof numZoomLevels !== "number" || numZoomLevels <= 0 || typeof maxResolution !== "number" && typeof minResolution !== "number") { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var resolutions = new Array(numZoomLevels); │ │ │ │ - var base = 2; │ │ │ │ - if (typeof minResolution == "number" && typeof maxResolution == "number") { │ │ │ │ - base = Math.pow(maxResolution / minResolution, 1 / (numZoomLevels - 1)) │ │ │ │ + evaluate: function(feature) { │ │ │ │ + var context = this.getContext(feature); │ │ │ │ + var applies = true; │ │ │ │ + if (this.minScaleDenominator || this.maxScaleDenominator) { │ │ │ │ + var scale = feature.layer.map.getScale() │ │ │ │ } │ │ │ │ - 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 (this.minScaleDenominator) { │ │ │ │ + applies = scale >= OpenLayers.Style.createLiteral(this.minScaleDenominator, context) │ │ │ │ } │ │ │ │ - return resolutions │ │ │ │ - }, │ │ │ │ - getResolution: function() { │ │ │ │ - var zoom = this.map.getZoom(); │ │ │ │ - return this.getResolutionForZoom(zoom) │ │ │ │ - }, │ │ │ │ - getExtent: function() { │ │ │ │ - return this.map.calculateBounds() │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - getDataExtent: function() {}, │ │ │ │ - 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)] │ │ │ │ + if (applies && this.maxScaleDenominator) { │ │ │ │ + applies = scale < OpenLayers.Style.createLiteral(this.maxScaleDenominator, context) │ │ │ │ } │ │ │ │ - return resolution │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ + if (applies && this.filter) { │ │ │ │ + if (this.filter.CLASS_NAME == "OpenLayers.Filter.FeatureId") { │ │ │ │ + applies = this.filter.evaluate(feature) │ │ │ │ } 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 │ │ │ │ - } │ │ │ │ - } │ │ │ │ + applies = this.filter.evaluate(context) │ │ │ │ } │ │ │ │ - zoom = Math.max(0, i - 1) │ │ │ │ } │ │ │ │ - return zoom │ │ │ │ + return applies │ │ │ │ }, │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ + getContext: function(feature) { │ │ │ │ + var context = this.context; │ │ │ │ + if (!context) { │ │ │ │ + context = feature.attributes || feature.data │ │ │ │ } │ │ │ │ - return lonlat │ │ │ │ - }, │ │ │ │ - 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)) │ │ │ │ + if (typeof this.context == "function") { │ │ │ │ + context = this.context(feature) │ │ │ │ } │ │ │ │ - return px │ │ │ │ + return context │ │ │ │ }, │ │ │ │ - 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; │ │ │ │ - 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" │ │ │ │ - }) │ │ │ │ + clone: function() { │ │ │ │ + var options = OpenLayers.Util.extend({}, this); │ │ │ │ + if (this.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() │ │ │ │ } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getZIndex: function() { │ │ │ │ - return this.div.style.zIndex │ │ │ │ - }, │ │ │ │ - setZIndex: function(zIndex) { │ │ │ │ - this.div.style.zIndex = zIndex │ │ │ │ - }, │ │ │ │ - adjustBounds: function(bounds) { │ │ │ │ - if (this.gutter) { │ │ │ │ - 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) { │ │ │ │ - var wrappingOptions = { │ │ │ │ - rightTolerance: this.getResolution(), │ │ │ │ - leftTolerance: this.getResolution() │ │ │ │ - }; │ │ │ │ - bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions) │ │ │ │ - } │ │ │ │ - return bounds │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ - URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2, │ │ │ │ - url: null, │ │ │ │ - params: null, │ │ │ │ - reproject: false, │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.url = null; │ │ │ │ - this.params = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.HTTPRequest(this.name, this.url, this.params, this.getOptions()) │ │ │ │ - } │ │ │ │ - obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ - }, │ │ │ │ - setUrl: function(newUrl) { │ │ │ │ - this.url = newUrl │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - redraw: function(force) { │ │ │ │ - if (force) { │ │ │ │ - return this.mergeNewParams({ │ │ │ │ - _olSalt: Math.random() │ │ │ │ - }) │ │ │ │ } else { │ │ │ │ - return OpenLayers.Layer.prototype.redraw.apply(this, []) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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)] │ │ │ │ - }, │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ - var url = altUrl || this.url; │ │ │ │ - var allParams = OpenLayers.Util.extend({}, this.params); │ │ │ │ - allParams = OpenLayers.Util.extend(allParams, newParams); │ │ │ │ - var paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(paramsString, url) │ │ │ │ - } │ │ │ │ - var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url)); │ │ │ │ - for (var key in allParams) { │ │ │ │ - if (key.toUpperCase() in urlParams) { │ │ │ │ - delete allParams[key] │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ - paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ - return OpenLayers.Util.urlAppend(url, paramsString) │ │ │ │ + options.filter = this.filter && this.filter.clone(); │ │ │ │ + options.context = this.context && OpenLayers.Util.extend({}, this.context); │ │ │ │ + return new OpenLayers.Rule(options) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.HTTPRequest" │ │ │ │ + CLASS_NAME: "OpenLayers.Rule" │ │ │ │ }); │ │ │ │ -OpenLayers.Tile = OpenLayers.Class({ │ │ │ │ - events: null, │ │ │ │ - eventListeners: null, │ │ │ │ - id: null, │ │ │ │ +OpenLayers.Strategy = OpenLayers.Class({ │ │ │ │ layer: null, │ │ │ │ - url: null, │ │ │ │ - bounds: null, │ │ │ │ - size: null, │ │ │ │ - position: null, │ │ │ │ - isLoading: false, │ │ │ │ - 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() │ │ │ │ - } │ │ │ │ - this.id = OpenLayers.Util.createUniqueID("Tile_"); │ │ │ │ + options: null, │ │ │ │ + active: null, │ │ │ │ + autoActivate: true, │ │ │ │ + autoDestroy: true, │ │ │ │ + initialize: function(options) { │ │ │ │ OpenLayers.Util.extend(this, options); │ │ │ │ - this.events = new OpenLayers.Events(this); │ │ │ │ - if (this.eventListeners instanceof Object) { │ │ │ │ - this.events.on(this.eventListeners) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - unload: function() { │ │ │ │ - if (this.isLoading) { │ │ │ │ - this.isLoading = false; │ │ │ │ - this.events.triggerEvent("unload") │ │ │ │ - } │ │ │ │ + this.options = options; │ │ │ │ + this.active = false │ │ │ │ }, │ │ │ │ destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ this.layer = null; │ │ │ │ - this.bounds = null; │ │ │ │ - this.size = null; │ │ │ │ - this.position = null; │ │ │ │ - if (this.eventListeners) { │ │ │ │ - this.events.un(this.eventListeners) │ │ │ │ - } │ │ │ │ - this.events.destroy(); │ │ │ │ - this.eventListeners = null; │ │ │ │ - this.events = null │ │ │ │ - }, │ │ │ │ - draw: function(force) { │ │ │ │ - if (!force) { │ │ │ │ - this.clear() │ │ │ │ - } │ │ │ │ - var draw = this.shouldDraw(); │ │ │ │ - if (draw && !force && this.events.triggerEvent("beforedraw") === false) { │ │ │ │ - draw = null │ │ │ │ - } │ │ │ │ - return draw │ │ │ │ + this.options = null │ │ │ │ }, │ │ │ │ - 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 │ │ │ │ + setLayer: function(layer) { │ │ │ │ + this.layer = layer │ │ │ │ }, │ │ │ │ - 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 │ │ │ │ - }) │ │ │ │ + activate: function() { │ │ │ │ + if (!this.active) { │ │ │ │ + this.active = true; │ │ │ │ + return true │ │ │ │ } │ │ │ │ - this.bounds = bounds │ │ │ │ + return false │ │ │ │ }, │ │ │ │ - moveTo: function(bounds, position, redraw) { │ │ │ │ - if (redraw == null) { │ │ │ │ - redraw = true │ │ │ │ - } │ │ │ │ - this.setBounds(bounds); │ │ │ │ - this.position = position.clone(); │ │ │ │ - if (redraw) { │ │ │ │ - this.draw() │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + this.active = false; │ │ │ │ + return true │ │ │ │ } │ │ │ │ + return false │ │ │ │ }, │ │ │ │ - clear: function(draw) {}, │ │ │ │ - CLASS_NAME: "OpenLayers.Tile" │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy" │ │ │ │ }); │ │ │ │ -OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { │ │ │ │ - url: null, │ │ │ │ - imgDiv: null, │ │ │ │ - frame: null, │ │ │ │ - imageReloadAttempts: null, │ │ │ │ - layerAlphaHack: null, │ │ │ │ - asyncRequestId: null, │ │ │ │ - maxGetUrlLength: null, │ │ │ │ - canvasContext: null, │ │ │ │ - crossOriginKeyword: null, │ │ │ │ - initialize: function(layer, position, bounds, url, size, options) { │ │ │ │ - OpenLayers.Tile.prototype.initialize.apply(this, arguments); │ │ │ │ - this.url = url; │ │ │ │ - this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack(); │ │ │ │ - if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) { │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.imgDiv) { │ │ │ │ - this.clear(); │ │ │ │ - this.imgDiv = null; │ │ │ │ - this.frame = null │ │ │ │ - } │ │ │ │ - this.asyncRequestId = null; │ │ │ │ - OpenLayers.Tile.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - draw: function() { │ │ │ │ - var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments); │ │ │ │ - if (shouldDraw) { │ │ │ │ - if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { │ │ │ │ - this.bounds = this.getBoundsFromBaseLayer(this.position) │ │ │ │ - } │ │ │ │ - if (this.isLoading) { │ │ │ │ - this._loadEvent = "reload" │ │ │ │ - } else { │ │ │ │ - this.isLoading = true; │ │ │ │ - this._loadEvent = "loadstart" │ │ │ │ - } │ │ │ │ - this.renderTile(); │ │ │ │ - this.positionTile() │ │ │ │ - } else if (shouldDraw === false) { │ │ │ │ - this.unload() │ │ │ │ - } │ │ │ │ - return shouldDraw │ │ │ │ - }, │ │ │ │ - renderTile: function() { │ │ │ │ - if (this.layer.async) { │ │ │ │ - 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 { │ │ │ │ - this.url = this.layer.getURL(this.bounds); │ │ │ │ - this.initImage() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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" │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - getImage: function() { │ │ │ │ - if (!this.imgDiv) { │ │ │ │ - this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false); │ │ │ │ - var style = this.imgDiv.style; │ │ │ │ - if (this.frame) { │ │ │ │ - var left = 0, │ │ │ │ - top = 0; │ │ │ │ - if (this.layer.gutter) { │ │ │ │ - left = this.layer.gutter / this.layer.tileSize.w * 100; │ │ │ │ - top = this.layer.gutter / this.layer.tileSize.h * 100 │ │ │ │ - } │ │ │ │ - style.left = -left + "%"; │ │ │ │ - style.top = -top + "%"; │ │ │ │ - style.width = 2 * left + 100 + "%"; │ │ │ │ - style.height = 2 * top + 100 + "%" │ │ │ │ - } │ │ │ │ - style.visibility = "hidden"; │ │ │ │ - style.opacity = 0; │ │ │ │ - if (this.layer.opacity < 1) { │ │ │ │ - style.filter = "alpha(opacity=" + this.layer.opacity * 100 + ")" │ │ │ │ - } │ │ │ │ - style.position = "absolute"; │ │ │ │ - if (this.layerAlphaHack) { │ │ │ │ - style.paddingTop = style.height; │ │ │ │ - style.height = "0"; │ │ │ │ - style.width = "100%" │ │ │ │ - } │ │ │ │ - if (this.frame) { │ │ │ │ - this.frame.appendChild(this.imgDiv) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return this.imgDiv │ │ │ │ - }, │ │ │ │ - setImage: function(img) { │ │ │ │ - this.imgDiv = img │ │ │ │ - }, │ │ │ │ - initImage: function() { │ │ │ │ - if (!this.url && !this.imgDiv) { │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ +OpenLayers.Filter = OpenLayers.Class({ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options) │ │ │ │ }, │ │ │ │ - setImgSrc: function(url) { │ │ │ │ - var img = this.imgDiv; │ │ │ │ - if (url) { │ │ │ │ - img.style.visibility = "hidden"; │ │ │ │ - img.style.opacity = 0; │ │ │ │ - if (this.crossOriginKeyword) { │ │ │ │ - if (url.substr(0, 5) !== "data:") { │ │ │ │ - img.setAttribute("crossorigin", this.crossOriginKeyword) │ │ │ │ - } else { │ │ │ │ - img.removeAttribute("crossorigin") │ │ │ │ - } │ │ │ │ - } │ │ │ │ - img.src = url │ │ │ │ - } else { │ │ │ │ - this.stopLoading(); │ │ │ │ - this.imgDiv = null; │ │ │ │ - if (img.parentNode) { │ │ │ │ - img.parentNode.removeChild(img) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + destroy: function() {}, │ │ │ │ + evaluate: function(context) { │ │ │ │ + return true │ │ │ │ }, │ │ │ │ - getTile: function() { │ │ │ │ - return this.frame ? this.frame : this.getImage() │ │ │ │ + clone: function() { │ │ │ │ + return null │ │ │ │ }, │ │ │ │ - createBackBuffer: function() { │ │ │ │ - if (!this.imgDiv || this.isLoading) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var backBuffer; │ │ │ │ - if (this.frame) { │ │ │ │ - backBuffer = this.frame.cloneNode(false); │ │ │ │ - backBuffer.appendChild(this.imgDiv) │ │ │ │ + toString: function() { │ │ │ │ + var string; │ │ │ │ + if (OpenLayers.Format && OpenLayers.Format.CQL) { │ │ │ │ + string = OpenLayers.Format.CQL.prototype.write(this) │ │ │ │ } else { │ │ │ │ - backBuffer = this.imgDiv │ │ │ │ - } │ │ │ │ - this.imgDiv = null; │ │ │ │ - return backBuffer │ │ │ │ - }, │ │ │ │ - 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')" │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - stopLoading: function() { │ │ │ │ - OpenLayers.Event.stopObservingElement(this.imgDiv); │ │ │ │ - window.clearTimeout(this._loadTimeout); │ │ │ │ - delete this._loadTimeout │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ + string = Object.prototype.toString.call(this) │ │ │ │ } │ │ │ │ + return string │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Tile.Image" │ │ │ │ + CLASS_NAME: "OpenLayers.Filter" │ │ │ │ }); │ │ │ │ -OpenLayers.Tile.Image.IMAGE = function() { │ │ │ │ - var img = new Image; │ │ │ │ - img.className = "olTileImage"; │ │ │ │ - img.galleryImg = "no"; │ │ │ │ - return img │ │ │ │ -}(); │ │ │ │ -OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { │ │ │ │ - tileSize: null, │ │ │ │ - tileOriginCorner: "bl", │ │ │ │ - tileOrigin: null, │ │ │ │ - tileOptions: null, │ │ │ │ - tileClass: OpenLayers.Tile.Image, │ │ │ │ - grid: null, │ │ │ │ - singleTile: false, │ │ │ │ - ratio: 1.5, │ │ │ │ - buffer: 0, │ │ │ │ - transitionEffect: "resize", │ │ │ │ - numLoadingTiles: 0, │ │ │ │ - serverResolutions: null, │ │ │ │ - loading: false, │ │ │ │ - backBuffer: null, │ │ │ │ - gridResolution: null, │ │ │ │ - backBufferResolution: null, │ │ │ │ - backBufferLonLat: null, │ │ │ │ - backBufferTimerId: null, │ │ │ │ - removeBackBufferDelay: null, │ │ │ │ - className: null, │ │ │ │ - gridLayout: null, │ │ │ │ - rowSign: null, │ │ │ │ - transitionendEvents: ["transitionend", "webkitTransitionEnd", "otransitionend", "oTransitionEnd"], │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - initProperties: function() { │ │ │ │ - if (this.options.removeBackBufferDelay === undefined) { │ │ │ │ - this.removeBackBufferDelay = this.singleTile ? 0 : 2500 │ │ │ │ - } │ │ │ │ - if (this.options.className === undefined) { │ │ │ │ - this.className = this.singleTile ? "olLayerGridSingleTile" : "olLayerGrid" │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map); │ │ │ │ - OpenLayers.Element.addClass(this.div, this.className) │ │ │ │ - }, │ │ │ │ - removeMap: function(map) { │ │ │ │ - this.removeBackBuffer() │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.removeBackBuffer(); │ │ │ │ - this.clearGrid(); │ │ │ │ - this.grid = null; │ │ │ │ - this.tileSize = null; │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Grid(this.name, this.url, this.params, this.getOptions()) │ │ │ │ - } │ │ │ │ - obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]); │ │ │ │ - if (this.tileSize != null) { │ │ │ │ - obj.tileSize = this.tileSize.clone() │ │ │ │ - } │ │ │ │ - obj.grid = []; │ │ │ │ - obj.gridResolution = null; │ │ │ │ - obj.backBuffer = null; │ │ │ │ - obj.backBufferTimerId = null; │ │ │ │ - obj.loading = false; │ │ │ │ - obj.numLoadingTiles = 0; │ │ │ │ - return obj │ │ │ │ - }, │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments); │ │ │ │ - bounds = bounds || this.map.getExtent(); │ │ │ │ - if (bounds != null) { │ │ │ │ - var forceReTile = !this.grid.length || zoomChanged; │ │ │ │ - var tilesBounds = this.getTilesBounds(); │ │ │ │ - var resolution = this.map.getResolution(); │ │ │ │ - var serverResolution = this.getServerResolution(resolution); │ │ │ │ - if (this.singleTile) { │ │ │ │ - if (forceReTile || !dragging && !tilesBounds.containsBounds(bounds)) { │ │ │ │ - if (zoomChanged && this.transitionEffect !== "resize") { │ │ │ │ - this.removeBackBuffer() │ │ │ │ - } │ │ │ │ - if (!zoomChanged || this.transitionEffect === "resize") { │ │ │ │ - this.applyBackBuffer(resolution) │ │ │ │ - } │ │ │ │ - this.initSingleTile(bounds) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - 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) │ │ │ │ +OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ + type: null, │ │ │ │ + property: null, │ │ │ │ + value: null, │ │ │ │ + distance: null, │ │ │ │ + distanceUnits: null, │ │ │ │ + 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() │ │ │ │ } │ │ │ │ - this.initGriddedTiles(bounds) │ │ │ │ - } else { │ │ │ │ - this.moveGriddedTiles() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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) { │ │ │ │ - if (this.map.baseLayer.wrapDateLine) { │ │ │ │ - var worldWidth = this.map.getMaxExtent().getWidth(); │ │ │ │ - var worldsAway = Math.ceil((left - x) / worldWidth); │ │ │ │ - x += worldWidth * worldsAway │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var dtx = (x - left) / (res * tileWidth); │ │ │ │ - var dty = (top - y) / (res * tileHeight); │ │ │ │ - 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, │ │ │ │ - i: Math.floor((dtx - col) * tileWidth), │ │ │ │ - j: Math.floor((dty - row) * tileHeight) │ │ │ │ + if (feature.geometry.intersects(geom)) { │ │ │ │ + intersect = true │ │ │ │ } │ │ │ │ } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return data │ │ │ │ - }, │ │ │ │ - destroyTile: function(tile) { │ │ │ │ - this.removeTileMonitoringHooks(tile); │ │ │ │ - tile.destroy() │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + throw new Error("evaluate is not implemented for this filter type.") │ │ │ │ } │ │ │ │ - return resolution │ │ │ │ - }, │ │ │ │ - getServerZoom: function() { │ │ │ │ - var resolution = this.getServerResolution(); │ │ │ │ - return this.serverResolutions ? OpenLayers.Util.indexOf(this.serverResolutions, resolution) : this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0) │ │ │ │ + return intersect │ │ │ │ }, │ │ │ │ - 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; │ │ │ │ - var topLeftTileBounds = this.grid[0][0].bounds; │ │ │ │ - this.backBufferLonLat = { │ │ │ │ - lon: topLeftTileBounds.left, │ │ │ │ - lat: topLeftTileBounds.top │ │ │ │ - }; │ │ │ │ - this.backBufferResolution = this.gridResolution │ │ │ │ - } │ │ │ │ - var ratio = this.backBufferResolution / resolution; │ │ │ │ - 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" │ │ │ │ - } │ │ │ │ - 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" │ │ │ │ + clone: function() { │ │ │ │ + var options = OpenLayers.Util.applyDefaults({ │ │ │ │ + value: this.value && this.value.clone && this.value.clone() │ │ │ │ + }, this); │ │ │ │ + return new OpenLayers.Filter.Spatial(options) │ │ │ │ }, │ │ │ │ - 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.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) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Filter.Spatial" │ │ │ │ +}); │ │ │ │ +OpenLayers.Filter.Spatial.BBOX = "BBOX"; │ │ │ │ +OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS"; │ │ │ │ +OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN"; │ │ │ │ +OpenLayers.Filter.Spatial.WITHIN = "WITHIN"; │ │ │ │ +OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS"; │ │ │ │ +OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ + bounds: null, │ │ │ │ + resolution: null, │ │ │ │ + ratio: 2, │ │ │ │ + resFactor: null, │ │ │ │ + response: null, │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + moveend: this.update, │ │ │ │ + refresh: this.update, │ │ │ │ + visibilitychanged: this.update, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.update() │ │ │ │ } │ │ │ │ - return backBuffer │ │ │ │ + return activated │ │ │ │ }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.layer.events.un({ │ │ │ │ + moveend: this.update, │ │ │ │ + refresh: this.update, │ │ │ │ + visibilitychanged: this.update, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ } │ │ │ │ + return deactivated │ │ │ │ }, │ │ │ │ - moveByPx: function(dx, dy) { │ │ │ │ - if (!this.singleTile) { │ │ │ │ - this.moveGriddedTiles() │ │ │ │ + update: function(options) { │ │ │ │ + var mapBounds = this.getMapBounds(); │ │ │ │ + if (mapBounds !== null && (options && options.force || this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds))) { │ │ │ │ + this.calculateBounds(mapBounds); │ │ │ │ + this.resolution = this.layer.map.getResolution(); │ │ │ │ + this.triggerRead(options) │ │ │ │ } │ │ │ │ }, │ │ │ │ - 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) │ │ │ │ + getMapBounds: function() { │ │ │ │ + if (this.layer.map === null) { │ │ │ │ + return null │ │ │ │ } │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]) │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ + var bounds = this.layer.map.getExtent(); │ │ │ │ + if (bounds && !this.layer.projection.equals(this.layer.map.getProjectionObject())) { │ │ │ │ + bounds = bounds.clone().transform(this.layer.map.getProjectionObject(), this.layer.projection) │ │ │ │ } │ │ │ │ return bounds │ │ │ │ }, │ │ │ │ - initSingleTile: function(bounds) { │ │ │ │ - this.events.triggerEvent("retile"); │ │ │ │ - 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] = [] │ │ │ │ + invalidBounds: function(mapBounds) { │ │ │ │ + if (!mapBounds) { │ │ │ │ + mapBounds = this.getMapBounds() │ │ │ │ } │ │ │ │ - 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) │ │ │ │ + var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds); │ │ │ │ + if (!invalid && this.resFactor) { │ │ │ │ + var ratio = this.resolution / this.layer.map.getResolution(); │ │ │ │ + invalid = ratio >= this.resFactor || ratio <= 1 / this.resFactor │ │ │ │ } │ │ │ │ - this.removeExcessTiles(1, 1); │ │ │ │ - this.gridResolution = this.getServerResolution() │ │ │ │ + return invalid │ │ │ │ }, │ │ │ │ - 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 │ │ │ │ + calculateBounds: function(mapBounds) { │ │ │ │ + if (!mapBounds) { │ │ │ │ + mapBounds = this.getMapBounds() │ │ │ │ } │ │ │ │ + var center = mapBounds.getCenterLonLat(); │ │ │ │ + var dataWidth = mapBounds.getWidth() * this.ratio; │ │ │ │ + var dataHeight = mapBounds.getHeight() * this.ratio; │ │ │ │ + this.bounds = new OpenLayers.Bounds(center.lon - dataWidth / 2, center.lat - dataHeight / 2, center.lon + dataWidth / 2, center.lat + dataHeight / 2) │ │ │ │ }, │ │ │ │ - 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]]) │ │ │ │ + triggerRead: function(options) { │ │ │ │ + if (this.response && !(options && options.noAbort === true)) { │ │ │ │ + this.layer.protocol.abort(this.response); │ │ │ │ + this.layer.events.triggerEvent("loadend") │ │ │ │ } │ │ │ │ - return origin │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ + var evt = { │ │ │ │ + filter: this.createFilter() │ │ │ │ + }; │ │ │ │ + this.layer.events.triggerEvent("loadstart", evt); │ │ │ │ + this.response = this.layer.protocol.read(OpenLayers.Util.applyDefaults({ │ │ │ │ + filter: evt.filter, │ │ │ │ + callback: this.merge, │ │ │ │ + scope: this │ │ │ │ + }, options)) │ │ │ │ }, │ │ │ │ - initGriddedTiles: function(bounds) { │ │ │ │ - this.events.triggerEvent("retile"); │ │ │ │ - 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); │ │ │ │ - this.removeExcessTiles(rowidx, colidx); │ │ │ │ - var resolution = this.getServerResolution(); │ │ │ │ - this.gridResolution = resolution; │ │ │ │ - tileData.sort(function(a, b) { │ │ │ │ - return a.distance - b.distance │ │ │ │ + createFilter: function() { │ │ │ │ + var filter = new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ + value: this.bounds, │ │ │ │ + projection: this.layer.projection │ │ │ │ }); │ │ │ │ - for (var i = 0, ii = tileData.length; i < ii; ++i) { │ │ │ │ - tileData[i].tile.draw() │ │ │ │ + if (this.layer.filter) { │ │ │ │ + filter = new OpenLayers.Filter.Logical({ │ │ │ │ + type: OpenLayers.Filter.Logical.AND, │ │ │ │ + filters: [this.layer.filter, filter] │ │ │ │ + }) │ │ │ │ } │ │ │ │ + return filter │ │ │ │ }, │ │ │ │ - getMaxExtent: function() { │ │ │ │ - return this.maxExtent │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - addTileMonitoringHooks: function(tile) { │ │ │ │ - var replacingCls = "olTileReplacing"; │ │ │ │ - tile.onLoadStart = function() { │ │ │ │ - if (this.loading === false) { │ │ │ │ - this.loading = true; │ │ │ │ - this.events.triggerEvent("loadstart") │ │ │ │ - } │ │ │ │ - this.events.triggerEvent("tileloadstart", { │ │ │ │ - tile: tile │ │ │ │ - }); │ │ │ │ - this.numLoadingTiles++; │ │ │ │ - if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ - OpenLayers.Element.addClass(tile.getTile(), replacingCls) │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - tile.onLoadEnd = function(evt) { │ │ │ │ - this.numLoadingTiles--; │ │ │ │ - var aborted = evt.type === "unload"; │ │ │ │ - this.events.triggerEvent("tileloaded", { │ │ │ │ - tile: tile, │ │ │ │ - aborted: aborted │ │ │ │ - }); │ │ │ │ - if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ - var tileDiv = tile.getTile(); │ │ │ │ - if (OpenLayers.Element.getStyle(tileDiv, "display") === "none") { │ │ │ │ - var bufferTile = document.getElementById(tile.id + "_bb"); │ │ │ │ - if (bufferTile) { │ │ │ │ - bufferTile.parentNode.removeChild(bufferTile) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Element.removeClass(tileDiv, replacingCls) │ │ │ │ - } │ │ │ │ - if (this.numLoadingTiles === 0) { │ │ │ │ - if (this.backBuffer) { │ │ │ │ - if (this.backBuffer.childNodes.length === 0) { │ │ │ │ - this.removeBackBuffer() │ │ │ │ - } else { │ │ │ │ - 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) │ │ │ │ + merge: function(resp) { │ │ │ │ + this.layer.destroyFeatures(); │ │ │ │ + if (resp.success()) { │ │ │ │ + var features = resp.features; │ │ │ │ + if (features && features.length > 0) { │ │ │ │ + var remote = this.layer.projection; │ │ │ │ + var local = this.layer.map.getProjectionObject(); │ │ │ │ + if (!local.equals(remote)) { │ │ │ │ + var geom; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + geom = features[i].geometry; │ │ │ │ + if (geom) { │ │ │ │ + geom.transform(remote, local) │ │ │ │ } │ │ │ │ - this.backBufferTimerId = window.setTimeout(this._removeBackBuffer, this.removeBackBufferDelay) │ │ │ │ } │ │ │ │ } │ │ │ │ - this.loading = false; │ │ │ │ - this.events.triggerEvent("loadend") │ │ │ │ + this.layer.addFeatures(features) │ │ │ │ } │ │ │ │ - }; │ │ │ │ - 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 │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - removeTileMonitoringHooks: function(tile) { │ │ │ │ - tile.unload(); │ │ │ │ - tile.events.un({ │ │ │ │ - loadstart: tile.onLoadStart, │ │ │ │ - loadend: tile.onLoadEnd, │ │ │ │ - unload: tile.onLoadEnd, │ │ │ │ - loaderror: tile.onLoadError, │ │ │ │ - scope: this │ │ │ │ + } else { │ │ │ │ + this.bounds = null │ │ │ │ + } │ │ │ │ + this.response = null; │ │ │ │ + this.layer.events.triggerEvent("loadend", { │ │ │ │ + response: resp │ │ │ │ }) │ │ │ │ }, │ │ │ │ - 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) │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.BBOX" │ │ │ │ +}); │ │ │ │ +OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ + preload: false, │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); │ │ │ │ + if (activated) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + refresh: this.load, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + if (this.layer.visibility == true || this.preload) { │ │ │ │ + this.load() │ │ │ │ } else { │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - removeExcessTiles: function(rows, columns) { │ │ │ │ - var i, l; │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - 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) │ │ │ │ + this.layer.events.on({ │ │ │ │ + visibilitychanged: this.load, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ } │ │ │ │ } │ │ │ │ + return activated │ │ │ │ }, │ │ │ │ - onMapResize: function() { │ │ │ │ - if (this.singleTile) { │ │ │ │ - this.clearGrid(); │ │ │ │ - this.setTileSize() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getTileBounds: function(viewPortPx) { │ │ │ │ - var maxExtent = this.maxExtent; │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var tileMapWidth = resolution * this.tileSize.w; │ │ │ │ - var tileMapHeight = resolution * this.tileSize.h; │ │ │ │ - var mapPoint = this.getLonLatFromViewPortPx(viewPortPx); │ │ │ │ - var tileLeft = maxExtent.left + tileMapWidth * Math.floor((mapPoint.lon - maxExtent.left) / tileMapWidth); │ │ │ │ - var tileBottom = maxExtent.bottom + tileMapHeight * Math.floor((mapPoint.lat - maxExtent.bottom) / tileMapHeight); │ │ │ │ - return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Grid" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - isBaseLayer: true, │ │ │ │ - sphericalMercator: false, │ │ │ │ - zoomOffset: 0, │ │ │ │ - serverResolutions: null, │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - if (options && options.sphericalMercator || this.sphericalMercator) { │ │ │ │ - options = OpenLayers.Util.extend({ │ │ │ │ - projection: "EPSG:900913", │ │ │ │ - numZoomLevels: 19 │ │ │ │ - }, options) │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name || this.name, url || this.url, {}, options]) │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.XYZ(this.name, this.url, this.getOptions()) │ │ │ │ - } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ - }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - var xyz = this.getXYZ(bounds); │ │ │ │ - var url = this.url; │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - var s = "" + xyz.x + xyz.y + xyz.z; │ │ │ │ - url = this.selectUrl(s, url) │ │ │ │ - } │ │ │ │ - return OpenLayers.String.format(url, xyz) │ │ │ │ - }, │ │ │ │ - getXYZ: function(bounds) { │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w)); │ │ │ │ - var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h)); │ │ │ │ - var z = this.getServerZoom(); │ │ │ │ - if (this.wrapDateLine) { │ │ │ │ - var limit = Math.pow(2, z); │ │ │ │ - x = (x % limit + limit) % limit │ │ │ │ - } │ │ │ │ - return { │ │ │ │ - x: x, │ │ │ │ - y: y, │ │ │ │ - z: z │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ - if (!this.tileOrigin) { │ │ │ │ - this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.bottom) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.XYZ" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ - key: null, │ │ │ │ - serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625, 4891.9698095703125, 2445.9849047851562, 1222.9924523925781, 611.4962261962891, 305.74811309814453, 152.87405654907226, 76.43702827453613, 38.218514137268066, 19.109257068634033, 9.554628534317017, 4.777314267158508, 2.388657133579254, 1.194328566789627, .5971642833948135, .29858214169740677, .14929107084870338, .07464553542435169], │ │ │ │ - attributionTemplate: '<span class="olBingAttribution ${type}">' + '<div><a target="_blank" href="http://www.bing.com/maps/">' + '<img src="${logo}" /></a></div>${copyrights}' + '<a style="white-space: nowrap" target="_blank" ' + 'href="http://www.microsoft.com/maps/product/terms.html">' + "Terms of Use</a></span>", │ │ │ │ - metadata: null, │ │ │ │ - protocolRegex: /^http:/i, │ │ │ │ - type: "Road", │ │ │ │ - culture: "en-US", │ │ │ │ - metadataParams: null, │ │ │ │ - tileOptions: null, │ │ │ │ - protocol: ~window.location.href.indexOf("http") ? "" : "http:", │ │ │ │ - initialize: function(options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults({ │ │ │ │ - sphericalMercator: true │ │ │ │ - }, options); │ │ │ │ - var name = options.name || "Bing " + (options.type || this.type); │ │ │ │ - var newArgs = [name, null, options]; │ │ │ │ - OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs); │ │ │ │ - this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ - crossOriginKeyword: "anonymous" │ │ │ │ - }, this.options.tileOptions); │ │ │ │ - this.loadMetadata() │ │ │ │ - }, │ │ │ │ - loadMetadata: function() { │ │ │ │ - this._callbackId = "_callback_" + this.id.replace(/\./g, "_"); │ │ │ │ - window[this._callbackId] = OpenLayers.Function.bind(OpenLayers.Layer.Bing.processMetadata, this); │ │ │ │ - var params = OpenLayers.Util.applyDefaults({ │ │ │ │ - key: this.key, │ │ │ │ - jsonp: this._callbackId, │ │ │ │ - include: "ImageryProviders" │ │ │ │ - }, this.metadataParams); │ │ │ │ - var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" + this.type + "?" + OpenLayers.Util.getParameterString(params); │ │ │ │ - var script = document.createElement("script"); │ │ │ │ - script.type = "text/javascript"; │ │ │ │ - script.src = url; │ │ │ │ - script.id = this._callbackId; │ │ │ │ - document.getElementsByTagName("head")[0].appendChild(script) │ │ │ │ - }, │ │ │ │ - initLayer: function() { │ │ │ │ - var res = this.metadata.resourceSets[0].resources[0]; │ │ │ │ - var url = res.imageUrl.replace("{quadkey}", "${quadkey}"); │ │ │ │ - url = url.replace("{culture}", this.culture); │ │ │ │ - url = url.replace(this.protocolRegex, this.protocol); │ │ │ │ - this.url = []; │ │ │ │ - for (var i = 0; i < res.imageUrlSubdomains.length; ++i) { │ │ │ │ - this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i])) │ │ │ │ - } │ │ │ │ - this.addOptions({ │ │ │ │ - maxResolution: Math.min(this.serverResolutions[res.zoomMin], this.maxResolution || Number.POSITIVE_INFINITY), │ │ │ │ - numZoomLevels: Math.min(res.zoomMax + 1 - res.zoomMin, this.numZoomLevels) │ │ │ │ - }, true); │ │ │ │ - if (!this.isBaseLayer) { │ │ │ │ - this.redraw() │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.layer.events.un({ │ │ │ │ + refresh: this.load, │ │ │ │ + visibilitychanged: this.load, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ } │ │ │ │ - this.updateAttribution() │ │ │ │ + return deactivated │ │ │ │ }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - if (!this.url) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var xyz = this.getXYZ(bounds), │ │ │ │ - x = xyz.x, │ │ │ │ - y = xyz.y, │ │ │ │ - z = xyz.z; │ │ │ │ - var quadDigits = []; │ │ │ │ - for (var i = z; i > 0; --i) { │ │ │ │ - var digit = "0"; │ │ │ │ - var mask = 1 << i - 1; │ │ │ │ - if ((x & mask) != 0) { │ │ │ │ - digit++ │ │ │ │ - } │ │ │ │ - if ((y & mask) != 0) { │ │ │ │ - digit++; │ │ │ │ - digit++ │ │ │ │ - } │ │ │ │ - quadDigits.push(digit) │ │ │ │ - } │ │ │ │ - var quadKey = quadDigits.join(""); │ │ │ │ - var url = this.selectUrl("" + x + y + z, this.url); │ │ │ │ - return OpenLayers.String.format(url, { │ │ │ │ - quadkey: quadKey │ │ │ │ + load: function(options) { │ │ │ │ + var layer = this.layer; │ │ │ │ + layer.events.triggerEvent("loadstart", { │ │ │ │ + filter: layer.filter │ │ │ │ + }); │ │ │ │ + layer.protocol.read(OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: this.merge, │ │ │ │ + filter: layer.filter, │ │ │ │ + scope: this │ │ │ │ + }, options)); │ │ │ │ + layer.events.un({ │ │ │ │ + visibilitychanged: this.load, │ │ │ │ + scope: this │ │ │ │ }) │ │ │ │ }, │ │ │ │ - updateAttribution: function() { │ │ │ │ - var metadata = this.metadata; │ │ │ │ - if (!metadata.resourceSets || !this.map || !this.map.center) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var res = metadata.resourceSets[0].resources[0]; │ │ │ │ - var extent = this.map.getExtent().transform(this.map.getProjectionObject(), new OpenLayers.Projection("EPSG:4326")); │ │ │ │ - var providers = res.imageryProviders || [], │ │ │ │ - zoom = OpenLayers.Util.indexOf(this.serverResolutions, this.getServerResolution()), │ │ │ │ - copyrights = "", │ │ │ │ - provider, i, ii, j, jj, bbox, coverage; │ │ │ │ - for (i = 0, ii = providers.length; i < ii; ++i) { │ │ │ │ - provider = providers[i]; │ │ │ │ - for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) { │ │ │ │ - coverage = provider.coverageAreas[j]; │ │ │ │ - bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true); │ │ │ │ - if (extent.intersectsBounds(bbox) && zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) { │ │ │ │ - copyrights += provider.attribution + " " │ │ │ │ + merge: function(resp) { │ │ │ │ + var layer = this.layer; │ │ │ │ + layer.destroyFeatures(); │ │ │ │ + var features = resp.features; │ │ │ │ + if (features && features.length > 0) { │ │ │ │ + var remote = layer.projection; │ │ │ │ + var local = layer.map.getProjectionObject(); │ │ │ │ + if (!local.equals(remote)) { │ │ │ │ + var geom; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + geom = features[i].geometry; │ │ │ │ + if (geom) { │ │ │ │ + geom.transform(remote, local) │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ + layer.addFeatures(features) │ │ │ │ } │ │ │ │ - var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol); │ │ │ │ - this.attribution = OpenLayers.String.format(this.attributionTemplate, { │ │ │ │ - type: this.type.toLowerCase(), │ │ │ │ - logo: logo, │ │ │ │ - copyrights: copyrights │ │ │ │ - }); │ │ │ │ - this.map && this.map.events.triggerEvent("changelayer", { │ │ │ │ - layer: this, │ │ │ │ - property: "attribution" │ │ │ │ + layer.events.triggerEvent("loadend", { │ │ │ │ + response: resp │ │ │ │ }) │ │ │ │ }, │ │ │ │ - setMap: function() { │ │ │ │ - OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments); │ │ │ │ - this.map.events.register("moveend", this, this.updateAttribution) │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Bing(this.options) │ │ │ │ - } │ │ │ │ - obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.map && this.map.events.unregister("moveend", this, this.updateAttribution); │ │ │ │ - OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Bing" │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Fixed" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.Bing.processMetadata = function(metadata) { │ │ │ │ - this.metadata = metadata; │ │ │ │ - this.initLayer(); │ │ │ │ - var script = document.getElementById(this._callbackId); │ │ │ │ - script.parentNode.removeChild(script); │ │ │ │ - window[this._callbackId] = undefined; │ │ │ │ - delete this._callbackId │ │ │ │ -}; │ │ │ │ OpenLayers.Renderer = OpenLayers.Class({ │ │ │ │ container: null, │ │ │ │ root: null, │ │ │ │ extent: null, │ │ │ │ locked: false, │ │ │ │ size: null, │ │ │ │ resolution: null, │ │ │ │ @@ -6143,4354 +4715,1062 @@ │ │ │ │ OpenLayers.Renderer.symbol = { │ │ │ │ star: [350, 75, 379, 161, 469, 161, 397, 215, 423, 301, 350, 250, 277, 301, 303, 215, 231, 161, 321, 161, 350, 75], │ │ │ │ cross: [4, 0, 6, 0, 6, 4, 10, 4, 10, 6, 6, 6, 6, 10, 4, 10, 4, 6, 0, 6, 0, 4, 4, 4, 4, 0], │ │ │ │ x: [0, 0, 25, 0, 50, 35, 75, 0, 100, 0, 65, 50, 100, 100, 75, 100, 50, 65, 25, 100, 0, 100, 35, 50, 0, 0], │ │ │ │ square: [0, 0, 0, 1, 1, 1, 1, 0, 0, 0], │ │ │ │ triangle: [0, 10, 10, 10, 5, 0, 0, 10] │ │ │ │ }; │ │ │ │ -OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ - isBaseLayer: false, │ │ │ │ - isFixed: false, │ │ │ │ - features: null, │ │ │ │ - filter: null, │ │ │ │ - selectedFeatures: null, │ │ │ │ - unrenderedFeatures: null, │ │ │ │ - reportError: true, │ │ │ │ - style: null, │ │ │ │ - styleMap: null, │ │ │ │ - strategies: null, │ │ │ │ - protocol: null, │ │ │ │ - renderers: ["SVG", "VML", "Canvas"], │ │ │ │ - renderer: null, │ │ │ │ - rendererOptions: null, │ │ │ │ - geometryType: null, │ │ │ │ - drawn: false, │ │ │ │ - ratio: 1, │ │ │ │ - initialize: function(name, options) { │ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ - if (!this.renderer || !this.renderer.supported()) { │ │ │ │ - this.assignRenderer() │ │ │ │ - } │ │ │ │ - if (!this.renderer || !this.renderer.supported()) { │ │ │ │ - this.renderer = null; │ │ │ │ - this.displayError() │ │ │ │ - } │ │ │ │ - if (!this.styleMap) { │ │ │ │ - this.styleMap = new OpenLayers.StyleMap │ │ │ │ +OpenLayers.ElementsIndexer = OpenLayers.Class({ │ │ │ │ + maxZIndex: null, │ │ │ │ + order: null, │ │ │ │ + indices: null, │ │ │ │ + compare: null, │ │ │ │ + initialize: function(yOrdering) { │ │ │ │ + this.compare = yOrdering ? OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER; │ │ │ │ + this.clear() │ │ │ │ + }, │ │ │ │ + insert: function(newNode) { │ │ │ │ + if (this.exists(newNode)) { │ │ │ │ + this.remove(newNode) │ │ │ │ } │ │ │ │ - this.features = []; │ │ │ │ - this.selectedFeatures = []; │ │ │ │ - this.unrenderedFeatures = {}; │ │ │ │ - if (this.strategies) { │ │ │ │ - for (var i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - this.strategies[i].setLayer(this) │ │ │ │ + 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); │ │ │ │ + return this.getNextElement(rightIndex) │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.strategies) { │ │ │ │ - var strategy, i, len; │ │ │ │ - for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - strategy = this.strategies[i]; │ │ │ │ - if (strategy.autoDestroy) { │ │ │ │ - strategy.destroy() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.strategies = null │ │ │ │ - } │ │ │ │ - if (this.protocol) { │ │ │ │ - if (this.protocol.autoDestroy) { │ │ │ │ - this.protocol.destroy() │ │ │ │ + remove: function(node) { │ │ │ │ + var nodeId = node.id; │ │ │ │ + var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId); │ │ │ │ + if (arrayIndex >= 0) { │ │ │ │ + this.order.splice(arrayIndex, 1); │ │ │ │ + delete this.indices[nodeId]; │ │ │ │ + if (this.order.length > 0) { │ │ │ │ + var lastId = this.order[this.order.length - 1]; │ │ │ │ + this.maxZIndex = this.indices[lastId] │ │ │ │ + } else { │ │ │ │ + this.maxZIndex = 0 │ │ │ │ } │ │ │ │ - this.protocol = null │ │ │ │ } │ │ │ │ - this.destroyFeatures(); │ │ │ │ - this.features = null; │ │ │ │ - this.selectedFeatures = null; │ │ │ │ - this.unrenderedFeatures = null; │ │ │ │ - if (this.renderer) { │ │ │ │ - this.renderer.destroy() │ │ │ │ - } │ │ │ │ - this.renderer = null; │ │ │ │ - this.geometryType = null; │ │ │ │ - this.drawn = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments) │ │ │ │ }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Vector(this.name, this.getOptions()) │ │ │ │ - } │ │ │ │ - obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ - var features = this.features; │ │ │ │ - var len = features.length; │ │ │ │ - var clonedFeatures = new Array(len); │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ - clonedFeatures[i] = features[i].clone() │ │ │ │ - } │ │ │ │ - obj.features = clonedFeatures; │ │ │ │ - return obj │ │ │ │ + clear: function() { │ │ │ │ + this.order = []; │ │ │ │ + this.indices = {}; │ │ │ │ + this.maxZIndex = 0 │ │ │ │ }, │ │ │ │ - refresh: function(obj) { │ │ │ │ - if (this.calculateInRange() && this.visibility) { │ │ │ │ - this.events.triggerEvent("refresh", obj) │ │ │ │ + exists: function(node) { │ │ │ │ + return this.indices[node.id] != null │ │ │ │ + }, │ │ │ │ + getZIndex: function(node) { │ │ │ │ + return node._style.graphicZIndex │ │ │ │ + }, │ │ │ │ + determineZIndex: function(node) { │ │ │ │ + var zIndex = node._style.graphicZIndex; │ │ │ │ + if (zIndex == null) { │ │ │ │ + zIndex = this.maxZIndex; │ │ │ │ + node._style.graphicZIndex = zIndex │ │ │ │ + } else if (zIndex > this.maxZIndex) { │ │ │ │ + this.maxZIndex = zIndex │ │ │ │ } │ │ │ │ }, │ │ │ │ - assignRenderer: function() { │ │ │ │ - for (var i = 0, len = this.renderers.length; i < len; i++) { │ │ │ │ - var rendererClass = this.renderers[i]; │ │ │ │ - var renderer = typeof rendererClass == "function" ? rendererClass : OpenLayers.Renderer[rendererClass]; │ │ │ │ - if (renderer && renderer.prototype.supported()) { │ │ │ │ - this.renderer = new renderer(this.div, this.rendererOptions); │ │ │ │ - break │ │ │ │ + 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 │ │ │ │ } │ │ │ │ }, │ │ │ │ - displayError: function() { │ │ │ │ - if (this.reportError) { │ │ │ │ - OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", { │ │ │ │ - renderers: this.renderers.join("\n") │ │ │ │ - })) │ │ │ │ + CLASS_NAME: "OpenLayers.ElementsIndexer" │ │ │ │ +}); │ │ │ │ +OpenLayers.ElementsIndexer.IndexingMethods = { │ │ │ │ + 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 │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ - if (!this.renderer) { │ │ │ │ - this.map.removeLayer(this) │ │ │ │ - } else { │ │ │ │ - this.renderer.map = this.map; │ │ │ │ - var newSize = this.map.getSize(); │ │ │ │ - newSize.w = newSize.w * this.ratio; │ │ │ │ - newSize.h = newSize.h * this.ratio; │ │ │ │ - this.renderer.setSize(newSize) │ │ │ │ + Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ + var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, nextNode); │ │ │ │ + if (nextNode && returnVal == 0) { │ │ │ │ + returnVal = 1 │ │ │ │ } │ │ │ │ + return returnVal │ │ │ │ }, │ │ │ │ - afterAdd: function() { │ │ │ │ - if (this.strategies) { │ │ │ │ - var strategy, i, len; │ │ │ │ - for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - strategy = this.strategies[i]; │ │ │ │ - if (strategy.autoActivate) { │ │ │ │ - strategy.activate() │ │ │ │ - } │ │ │ │ - } │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - }, │ │ │ │ - removeMap: function(map) { │ │ │ │ - this.drawn = false; │ │ │ │ - if (this.strategies) { │ │ │ │ - var strategy, i, len; │ │ │ │ - for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - strategy = this.strategies[i]; │ │ │ │ - if (strategy.autoActivate) { │ │ │ │ - strategy.deactivate() │ │ │ │ - } │ │ │ │ - } │ │ │ │ + return returnVal │ │ │ │ + } │ │ │ │ +}; │ │ │ │ +OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, { │ │ │ │ + rendererRoot: null, │ │ │ │ + root: null, │ │ │ │ + vectorRoot: null, │ │ │ │ + textRoot: null, │ │ │ │ + xmlns: null, │ │ │ │ + xOffset: 0, │ │ │ │ + indexer: null, │ │ │ │ + BACKGROUND_ID_SUFFIX: "_background", │ │ │ │ + LABEL_ID_SUFFIX: "_label", │ │ │ │ + LABEL_OUTLINE_SUFFIX: "_outline", │ │ │ │ + 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) │ │ │ │ } │ │ │ │ }, │ │ │ │ - onMapResize: function() { │ │ │ │ - OpenLayers.Layer.prototype.onMapResize.apply(this, arguments); │ │ │ │ - var newSize = this.map.getSize(); │ │ │ │ - newSize.w = newSize.w * this.ratio; │ │ │ │ - newSize.h = newSize.h * this.ratio; │ │ │ │ - this.renderer.setSize(newSize) │ │ │ │ + destroy: function() { │ │ │ │ + this.clear(); │ │ │ │ + this.rendererRoot = null; │ │ │ │ + this.root = null; │ │ │ │ + this.xmlns = null; │ │ │ │ + OpenLayers.Renderer.prototype.destroy.apply(this, arguments) │ │ │ │ }, │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ - var coordSysUnchanged = true; │ │ │ │ - if (!dragging) { │ │ │ │ - this.renderer.root.style.visibility = "hidden"; │ │ │ │ - var viewSize = this.map.getSize(), │ │ │ │ - viewWidth = viewSize.w, │ │ │ │ - viewHeight = viewSize.h, │ │ │ │ - offsetLeft = viewWidth / 2 * this.ratio - viewWidth / 2, │ │ │ │ - offsetTop = viewHeight / 2 * this.ratio - viewHeight / 2; │ │ │ │ - offsetLeft += this.map.layerContainerOriginPx.x; │ │ │ │ - offsetLeft = -Math.round(offsetLeft); │ │ │ │ - offsetTop += this.map.layerContainerOriginPx.y; │ │ │ │ - offsetTop = -Math.round(offsetTop); │ │ │ │ - this.div.style.left = offsetLeft + "px"; │ │ │ │ - this.div.style.top = offsetTop + "px"; │ │ │ │ - var extent = this.map.getExtent().scale(this.ratio); │ │ │ │ - coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged); │ │ │ │ - this.renderer.root.style.visibility = "visible"; │ │ │ │ - if (OpenLayers.IS_GECKO === true) { │ │ │ │ - this.div.scrollLeft = this.div.scrollLeft │ │ │ │ - } │ │ │ │ - if (!zoomChanged && coordSysUnchanged) { │ │ │ │ - for (var i in this.unrenderedFeatures) { │ │ │ │ - var feature = this.unrenderedFeatures[i]; │ │ │ │ - this.drawFeature(feature) │ │ │ │ - } │ │ │ │ + clear: function() { │ │ │ │ + var child; │ │ │ │ + var root = this.vectorRoot; │ │ │ │ + if (root) { │ │ │ │ + while (child = root.firstChild) { │ │ │ │ + root.removeChild(child) │ │ │ │ } │ │ │ │ } │ │ │ │ - if (!this.drawn || zoomChanged || !coordSysUnchanged) { │ │ │ │ - this.drawn = true; │ │ │ │ - var feature; │ │ │ │ - for (var i = 0, len = this.features.length; i < len; i++) { │ │ │ │ - this.renderer.locked = i !== len - 1; │ │ │ │ - feature = this.features[i]; │ │ │ │ - this.drawFeature(feature) │ │ │ │ + root = this.textRoot; │ │ │ │ + if (root) { │ │ │ │ + while (child = root.firstChild) { │ │ │ │ + root.removeChild(child) │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - display: function(display) { │ │ │ │ - OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ - var currentDisplay = this.div.style.display; │ │ │ │ - if (currentDisplay != this.renderer.root.style.display) { │ │ │ │ - this.renderer.root.style.display = currentDisplay │ │ │ │ + if (this.indexer) { │ │ │ │ + this.indexer.clear() │ │ │ │ } │ │ │ │ }, │ │ │ │ - addFeatures: function(features, options) { │ │ │ │ - if (!OpenLayers.Util.isArray(features)) { │ │ │ │ - features = [features] │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - var notify = !options || !options.silent; │ │ │ │ - if (notify) { │ │ │ │ - var event = { │ │ │ │ - features: features │ │ │ │ - }; │ │ │ │ - var ret = this.events.triggerEvent("beforefeaturesadded", event); │ │ │ │ - if (ret === false) { │ │ │ │ - return │ │ │ │ + return coordSysUnchanged │ │ │ │ + }, │ │ │ │ + getNodeType: function(geometry, style) {}, │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - features = event.features │ │ │ │ + return rendered │ │ │ │ } │ │ │ │ - var featuresAdded = []; │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - if (i != features.length - 1) { │ │ │ │ - this.renderer.locked = true │ │ │ │ + rendered = false; │ │ │ │ + var removeBackground = false; │ │ │ │ + if (style.display != "none") { │ │ │ │ + if (style.backgroundGraphic) { │ │ │ │ + this.redrawBackgroundNode(geometry.id, geometry, style, featureId) │ │ │ │ } else { │ │ │ │ - this.renderer.locked = false │ │ │ │ - } │ │ │ │ - var feature = features[i]; │ │ │ │ - if (this.geometryType && !(feature.geometry instanceof this.geometryType)) { │ │ │ │ - throw new TypeError("addFeatures: component should be an " + this.geometryType.prototype.CLASS_NAME) │ │ │ │ - } │ │ │ │ - feature.layer = this; │ │ │ │ - if (!feature.style && this.style) { │ │ │ │ - feature.style = OpenLayers.Util.extend({}, this.style) │ │ │ │ + removeBackground = true │ │ │ │ } │ │ │ │ - if (notify) { │ │ │ │ - if (this.events.triggerEvent("beforefeatureadded", { │ │ │ │ - feature: feature │ │ │ │ - }) === false) { │ │ │ │ - continue │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - this.preFeatureInsert(feature) │ │ │ │ - } │ │ │ │ - featuresAdded.push(feature); │ │ │ │ - this.features.push(feature); │ │ │ │ - this.drawFeature(feature); │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featureadded", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.onFeatureInsert(feature) │ │ │ │ + node.parentNode.removeChild(node) │ │ │ │ } │ │ │ │ } │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featuresadded", { │ │ │ │ - features: featuresAdded │ │ │ │ - }) │ │ │ │ + if (removeBackground) { │ │ │ │ + var node = document.getElementById(geometry.id + this.BACKGROUND_ID_SUFFIX); │ │ │ │ + if (node) { │ │ │ │ + node.parentNode.removeChild(node) │ │ │ │ + } │ │ │ │ } │ │ │ │ + return rendered │ │ │ │ }, │ │ │ │ - removeFeatures: function(features, options) { │ │ │ │ - if (!features || features.length === 0) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - if (features === this.features) { │ │ │ │ - return this.removeAllFeatures(options) │ │ │ │ - } │ │ │ │ - if (!OpenLayers.Util.isArray(features)) { │ │ │ │ - features = [features] │ │ │ │ - } │ │ │ │ - if (features === this.selectedFeatures) { │ │ │ │ - features = features.slice() │ │ │ │ - } │ │ │ │ - var notify = !options || !options.silent; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("beforefeaturesremoved", { │ │ │ │ - features: features │ │ │ │ - }) │ │ │ │ + redrawNode: function(id, geometry, style, featureId) { │ │ │ │ + style = this.applyDefaultSymbolizer(style); │ │ │ │ + var node = this.nodeFactory(id, this.getNodeType(geometry, style)); │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - if (i != 0 && features[i - 1].geometry) { │ │ │ │ - this.renderer.locked = true │ │ │ │ + node = drawResult.node; │ │ │ │ + if (this.indexer) { │ │ │ │ + var insert = this.indexer.insert(node); │ │ │ │ + if (insert) { │ │ │ │ + this.vectorRoot.insertBefore(node, insert) │ │ │ │ } else { │ │ │ │ - this.renderer.locked = false │ │ │ │ - } │ │ │ │ - var feature = features[i]; │ │ │ │ - delete this.unrenderedFeatures[feature.id]; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - this.features = OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ - feature.layer = null; │ │ │ │ - if (feature.geometry) { │ │ │ │ - this.renderer.eraseFeatures(feature) │ │ │ │ - } │ │ │ │ - if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) { │ │ │ │ - OpenLayers.Util.removeItem(this.selectedFeatures, feature) │ │ │ │ + this.vectorRoot.appendChild(node) │ │ │ │ } │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ + } else { │ │ │ │ + if (node.parentNode !== this.vectorRoot) { │ │ │ │ + this.vectorRoot.appendChild(node) │ │ │ │ } │ │ │ │ } │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featuresremoved", { │ │ │ │ - features: features │ │ │ │ - }) │ │ │ │ - } │ │ │ │ + this.postDraw(node); │ │ │ │ + return drawResult.complete │ │ │ │ }, │ │ │ │ - removeAllFeatures: function(options) { │ │ │ │ - var notify = !options || !options.silent; │ │ │ │ - var features = this.features; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("beforefeaturesremoved", { │ │ │ │ - features: features │ │ │ │ - }) │ │ │ │ + redrawBackgroundNode: function(id, geometry, style, featureId) { │ │ │ │ + var backgroundStyle = OpenLayers.Util.extend({}, style); │ │ │ │ + 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; │ │ │ │ + backgroundStyle.backgroundGraphic = null; │ │ │ │ + backgroundStyle.backgroundXOffset = null; │ │ │ │ + backgroundStyle.backgroundYOffset = null; │ │ │ │ + backgroundStyle.backgroundGraphicZIndex = null; │ │ │ │ + return this.redrawNode(id + this.BACKGROUND_ID_SUFFIX, geometry, backgroundStyle, null) │ │ │ │ + }, │ │ │ │ + drawGeometryNode: function(node, geometry, style) { │ │ │ │ + style = style || node._style; │ │ │ │ + var options = { │ │ │ │ + isFilled: style.fill === undefined ? true : style.fill, │ │ │ │ + isStroked: style.stroke === undefined ? !!style.strokeWidth : style.stroke │ │ │ │ + }; │ │ │ │ + var drawn; │ │ │ │ + switch (geometry.CLASS_NAME) { │ │ │ │ + case "OpenLayers.Geometry.Point": │ │ │ │ + if (style.graphic === false) { │ │ │ │ + options.isFilled = false; │ │ │ │ + options.isStroked = false │ │ │ │ + } │ │ │ │ + drawn = this.drawPoint(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LineString": │ │ │ │ + options.isFilled = false; │ │ │ │ + drawn = this.drawLineString(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LinearRing": │ │ │ │ + drawn = this.drawLinearRing(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Polygon": │ │ │ │ + drawn = this.drawPolygon(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Rectangle": │ │ │ │ + drawn = this.drawRectangle(node, geometry); │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + break │ │ │ │ } │ │ │ │ - var feature; │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - feature = features[i]; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - feature.layer = null; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ + node._options = options; │ │ │ │ + if (drawn != false) { │ │ │ │ + return { │ │ │ │ + node: this.setStyle(node, style, options, geometry), │ │ │ │ + complete: drawn │ │ │ │ } │ │ │ │ - } │ │ │ │ - this.renderer.clear(); │ │ │ │ - this.features = []; │ │ │ │ - this.unrenderedFeatures = {}; │ │ │ │ - this.selectedFeatures = []; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featuresremoved", { │ │ │ │ - features: features │ │ │ │ - }) │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ } │ │ │ │ }, │ │ │ │ - destroyFeatures: function(features, options) { │ │ │ │ - var all = features == undefined; │ │ │ │ - if (all) { │ │ │ │ - features = this.features │ │ │ │ + postDraw: function(node) {}, │ │ │ │ + drawPoint: function(node, geometry) {}, │ │ │ │ + drawLineString: function(node, geometry) {}, │ │ │ │ + drawLinearRing: function(node, geometry) {}, │ │ │ │ + drawPolygon: function(node, geometry) {}, │ │ │ │ + drawRectangle: function(node, geometry) {}, │ │ │ │ + drawCircle: function(node, geometry) {}, │ │ │ │ + removeText: function(featureId) { │ │ │ │ + var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX); │ │ │ │ + if (label) { │ │ │ │ + this.textRoot.removeChild(label) │ │ │ │ } │ │ │ │ - if (features) { │ │ │ │ - this.removeFeatures(features, options); │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - features[i].destroy() │ │ │ │ - } │ │ │ │ + var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX); │ │ │ │ + if (outline) { │ │ │ │ + this.textRoot.removeChild(outline) │ │ │ │ } │ │ │ │ }, │ │ │ │ - drawFeature: function(feature, style) { │ │ │ │ - if (!this.drawn) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - if (typeof style != "object") { │ │ │ │ - if (!style && feature.state === OpenLayers.State.DELETE) { │ │ │ │ - style = "delete" │ │ │ │ + getFeatureIdFromEvent: function(evt) { │ │ │ │ + var target = evt.target; │ │ │ │ + var useElement = target && target.correspondingUseElement; │ │ │ │ + var node = useElement ? useElement : target || evt.srcElement; │ │ │ │ + return node._featureId │ │ │ │ + }, │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - var renderIntent = style || feature.renderIntent; │ │ │ │ - style = feature.style || this.style; │ │ │ │ - if (!style) { │ │ │ │ - style = this.styleMap.createSymbolizer(feature, renderIntent) │ │ │ │ + } 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) { │ │ │ │ + bElem.parentNode.removeChild(bElem) │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ - var drawn = this.renderer.drawFeature(feature, style); │ │ │ │ - if (drawn === false || drawn === null) { │ │ │ │ - this.unrenderedFeatures[feature.id] = feature │ │ │ │ + }, │ │ │ │ + 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 { │ │ │ │ - delete this.unrenderedFeatures[feature.id] │ │ │ │ + node = this.createNode(type, id) │ │ │ │ } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - eraseFeatures: function(features) { │ │ │ │ - this.renderer.eraseFeatures(features) │ │ │ │ + nodeTypeCompare: function(node, type) {}, │ │ │ │ + createNode: function(type, id) {}, │ │ │ │ + moveRoot: function(renderer) { │ │ │ │ + var root = this.root; │ │ │ │ + if (renderer.root.parentNode == this.rendererRoot) { │ │ │ │ + root = renderer.root │ │ │ │ + } │ │ │ │ + root.parentNode.removeChild(root); │ │ │ │ + renderer.rendererRoot.appendChild(root) │ │ │ │ }, │ │ │ │ - getFeatureFromEvent: function(evt) { │ │ │ │ - if (!this.renderer) { │ │ │ │ - throw new Error("getFeatureFromEvent called on layer with no " + "renderer. This usually means you destroyed a " + "layer, but not some handler which is associated " + "with it.") │ │ │ │ + getRenderLayerId: function() { │ │ │ │ + return this.root.parentNode.parentNode.id │ │ │ │ + }, │ │ │ │ + isComplexSymbol: function(graphicName) { │ │ │ │ + return graphicName != "circle" && !!graphicName │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.Elements" │ │ │ │ +}); │ │ │ │ +OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ + xmlns: "urn:schemas-microsoft-com:vml", │ │ │ │ + symbolCache: {}, │ │ │ │ + offset: null, │ │ │ │ + initialize: function(containerID) { │ │ │ │ + if (!this.supported()) { │ │ │ │ + return │ │ │ │ } │ │ │ │ - var feature = null; │ │ │ │ - var featureId = this.renderer.getFeatureIdFromEvent(evt); │ │ │ │ - if (featureId) { │ │ │ │ - if (typeof featureId === "string") { │ │ │ │ - feature = this.getFeatureById(featureId) │ │ │ │ - } else { │ │ │ │ - feature = featureId │ │ │ │ + if (!document.namespaces.olv) { │ │ │ │ + document.namespaces.add("olv", this.xmlns); │ │ │ │ + var style = document.createStyleSheet(); │ │ │ │ + var shapes = ["shape", "rect", "oval", "fill", "stroke", "imagedata", "group", "textbox"]; │ │ │ │ + for (var i = 0, len = shapes.length; i < len; i++) { │ │ │ │ + style.addRule("olv\\:" + shapes[i], "behavior: url(#default#VML); " + "position: absolute; display: inline-block;") │ │ │ │ } │ │ │ │ } │ │ │ │ - return feature │ │ │ │ + OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments) │ │ │ │ }, │ │ │ │ - getFeatureBy: function(property, value) { │ │ │ │ - var feature = null; │ │ │ │ - for (var i = 0, len = this.features.length; i < len; ++i) { │ │ │ │ - if (this.features[i][property] == value) { │ │ │ │ - feature = this.features[i]; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return feature │ │ │ │ + supported: function() { │ │ │ │ + return !!document.namespaces │ │ │ │ }, │ │ │ │ - getFeatureById: function(featureId) { │ │ │ │ - return this.getFeatureBy("id", featureId) │ │ │ │ + setExtent: function(extent, resolutionChanged) { │ │ │ │ + var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var left = extent.left / resolution | 0; │ │ │ │ + var top = extent.top / resolution - this.size.h | 0; │ │ │ │ + if (resolutionChanged || !this.offset) { │ │ │ │ + this.offset = { │ │ │ │ + x: left, │ │ │ │ + y: top │ │ │ │ + }; │ │ │ │ + left = 0; │ │ │ │ + top = 0 │ │ │ │ + } else { │ │ │ │ + left = left - this.offset.x; │ │ │ │ + top = top - this.offset.y │ │ │ │ + } │ │ │ │ + var org = left - this.xOffset + " " + top; │ │ │ │ + this.root.coordorigin = org; │ │ │ │ + var roots = [this.root, this.vectorRoot, this.textRoot]; │ │ │ │ + var root; │ │ │ │ + for (var i = 0, len = roots.length; i < len; ++i) { │ │ │ │ + root = roots[i]; │ │ │ │ + var size = this.size.w + " " + this.size.h; │ │ │ │ + root.coordsize = size │ │ │ │ + } │ │ │ │ + this.root.style.flip = "y"; │ │ │ │ + return coordSysUnchanged │ │ │ │ }, │ │ │ │ - getFeatureByFid: function(featureFid) { │ │ │ │ - return this.getFeatureBy("fid", featureFid) │ │ │ │ + setSize: function(size) { │ │ │ │ + OpenLayers.Renderer.prototype.setSize.apply(this, arguments); │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ }, │ │ │ │ - getFeaturesByAttribute: function(attrName, attrValue) { │ │ │ │ - var i, feature, len = this.features.length, │ │ │ │ - foundFeatures = []; │ │ │ │ - for (i = 0; i < len; i++) { │ │ │ │ - feature = this.features[i]; │ │ │ │ - if (feature && feature.attributes) { │ │ │ │ - if (feature.attributes[attrName] === attrValue) { │ │ │ │ - foundFeatures.push(feature) │ │ │ │ + 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 foundFeatures │ │ │ │ + return nodeType │ │ │ │ }, │ │ │ │ - onFeatureInsert: function(feature) {}, │ │ │ │ - preFeatureInsert: function(feature) {}, │ │ │ │ - getDataExtent: function() { │ │ │ │ - var maxExtent = null; │ │ │ │ - var features = this.features; │ │ │ │ - if (features && features.length > 0) { │ │ │ │ - var geometry = null; │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - geometry = features[i].geometry; │ │ │ │ - if (geometry) { │ │ │ │ - if (maxExtent === null) { │ │ │ │ - maxExtent = new OpenLayers.Bounds │ │ │ │ - } │ │ │ │ - maxExtent.extend(geometry.getBounds()) │ │ │ │ - } │ │ │ │ + setStyle: function(node, style, options, geometry) { │ │ │ │ + style = style || node._style; │ │ │ │ + options = options || node._options; │ │ │ │ + var fillColor = style.fillColor; │ │ │ │ + var title = style.title || style.graphicTitle; │ │ │ │ + if (title) { │ │ │ │ + node.title = title │ │ │ │ + } │ │ │ │ + if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ + if (style.externalGraphic) { │ │ │ │ + options.isFilled = true; │ │ │ │ + var width = style.graphicWidth || style.graphicHeight; │ │ │ │ + var height = style.graphicHeight || style.graphicWidth; │ │ │ │ + width = width ? width : style.pointRadius * 2; │ │ │ │ + height = height ? height : style.pointRadius * 2; │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var xOffset = style.graphicXOffset != undefined ? style.graphicXOffset : -(.5 * width); │ │ │ │ + var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.5 * height); │ │ │ │ + node.style.left = ((geometry.x - this.featureDx) / resolution - this.offset.x + xOffset | 0) + "px"; │ │ │ │ + node.style.top = (geometry.y / resolution - this.offset.y - (yOffset + height) | 0) + "px"; │ │ │ │ + node.style.width = width + "px"; │ │ │ │ + node.style.height = height + "px"; │ │ │ │ + node.style.flip = "y"; │ │ │ │ + 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) │ │ │ │ } │ │ │ │ } │ │ │ │ - return maxExtent │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Vector" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - DEFAULT_PARAMS: { │ │ │ │ - service: "WMS", │ │ │ │ - version: "1.1.1", │ │ │ │ - request: "GetMap", │ │ │ │ - styles: "", │ │ │ │ - format: "image/jpeg" │ │ │ │ - }, │ │ │ │ - isBaseLayer: true, │ │ │ │ - encodeBBOX: false, │ │ │ │ - noMagic: false, │ │ │ │ - yx: {}, │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - var newArguments = []; │ │ │ │ - params = OpenLayers.Util.upperCaseObject(params); │ │ │ │ - if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) { │ │ │ │ - params.EXCEPTIONS = "INIMAGE" │ │ │ │ + if (options.isFilled) { │ │ │ │ + node.fillcolor = fillColor │ │ │ │ + } else { │ │ │ │ + node.filled = "false" │ │ │ │ } │ │ │ │ - newArguments.push(name, url, params, options); │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ - OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)); │ │ │ │ - if (!this.noMagic && this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == "true") { │ │ │ │ - if (options == null || !options.isBaseLayer) { │ │ │ │ - this.isBaseLayer = false │ │ │ │ + var fills = node.getElementsByTagName("fill"); │ │ │ │ + var fill = fills.length == 0 ? null : fills[0]; │ │ │ │ + if (!options.isFilled) { │ │ │ │ + if (fill) { │ │ │ │ + node.removeChild(fill) │ │ │ │ } │ │ │ │ - if (this.params.FORMAT == "image/jpeg") { │ │ │ │ - this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif" : "image/png" │ │ │ │ + } else { │ │ │ │ + if (!fill) { │ │ │ │ + fill = this.createNode("olv:fill", node.id + "_fill") │ │ │ │ + } │ │ │ │ + fill.opacity = style.fillOpacity; │ │ │ │ + if (node._geometryClass === "OpenLayers.Geometry.Point" && style.externalGraphic) { │ │ │ │ + if (style.graphicOpacity) { │ │ │ │ + fill.opacity = style.graphicOpacity │ │ │ │ + } │ │ │ │ + fill.src = style.externalGraphic; │ │ │ │ + fill.type = "frame"; │ │ │ │ + if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ + fill.aspect = "atmost" │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (fill.parentNode != node) { │ │ │ │ + node.appendChild(fill) │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.WMS(this.name, this.url, this.params, this.getOptions()) │ │ │ │ + var rotation = style.rotation; │ │ │ │ + if (rotation !== undefined || node._rotation !== undefined) { │ │ │ │ + node._rotation = rotation; │ │ │ │ + if (style.externalGraphic) { │ │ │ │ + this.graphicRotate(node, xOffset, yOffset, style); │ │ │ │ + fill.opacity = 0 │ │ │ │ + } else if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ + node.style.rotation = rotation || 0 │ │ │ │ + } │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ - }, │ │ │ │ - reverseAxisOrder: function() { │ │ │ │ - var projCode = this.projection.getCode(); │ │ │ │ - return parseFloat(this.params.VERSION) >= 1.3 && !!(this.yx[projCode] || OpenLayers.Projection.defaults[projCode] && OpenLayers.Projection.defaults[projCode].yx) │ │ │ │ - }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var imageSize = this.getImageSize(); │ │ │ │ - var newParams = {}; │ │ │ │ - var reverseAxisOrder = this.reverseAxisOrder(); │ │ │ │ - newParams.BBOX = this.encodeBBOX ? bounds.toBBOX(null, reverseAxisOrder) : bounds.toArray(reverseAxisOrder); │ │ │ │ - newParams.WIDTH = imageSize.w; │ │ │ │ - newParams.HEIGHT = imageSize.h; │ │ │ │ - var requestString = this.getFullRequestString(newParams); │ │ │ │ - return requestString │ │ │ │ - }, │ │ │ │ - mergeNewParams: function(newParams) { │ │ │ │ - var upperParams = OpenLayers.Util.upperCaseObject(newParams); │ │ │ │ - var newArguments = [upperParams]; │ │ │ │ - return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments) │ │ │ │ - }, │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ - var mapProjection = this.map.getProjectionObject(); │ │ │ │ - var projectionCode = this.projection && this.projection.equals(mapProjection) ? this.projection.getCode() : mapProjection.getCode(); │ │ │ │ - var value = projectionCode == "none" ? null : projectionCode; │ │ │ │ - if (parseFloat(this.params.VERSION) >= 1.3) { │ │ │ │ - this.params.CRS = value │ │ │ │ + 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 { │ │ │ │ - this.params.SRS = value │ │ │ │ - } │ │ │ │ - if (typeof this.params.TRANSPARENT == "boolean") { │ │ │ │ - newParams.TRANSPARENT = this.params.TRANSPARENT ? "TRUE" : "FALSE" │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ } │ │ │ │ - return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.WMS" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ - name: "OpenStreetMap", │ │ │ │ - url: ["http://a.tile.openstreetmap.org/${z}/${x}/${y}.png", "http://b.tile.openstreetmap.org/${z}/${x}/${y}.png", "http://c.tile.openstreetmap.org/${z}/${x}/${y}.png"], │ │ │ │ - attribution: "© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors", │ │ │ │ - sphericalMercator: true, │ │ │ │ - wrapDateLine: true, │ │ │ │ - tileOptions: null, │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); │ │ │ │ - this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ - crossOriginKeyword: "anonymous" │ │ │ │ - }, this.options && this.options.tileOptions) │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.OSM(this.name, this.url, this.getOptions()) │ │ │ │ + if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ + node.style.cursor = style.cursor │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.OSM" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.SphericalMercator = { │ │ │ │ - getExtent: function() { │ │ │ │ - var extent = null; │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - extent = this.map.calculateBounds() │ │ │ │ + graphicRotate: function(node, xOffset, yOffset, style) { │ │ │ │ + var style = style || node._style; │ │ │ │ + var rotation = style.rotation || 0; │ │ │ │ + var aspectRatio, size; │ │ │ │ + if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ + 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; │ │ │ │ + return │ │ │ │ } else { │ │ │ │ - extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this) │ │ │ │ + size = Math.max(style.graphicWidth, style.graphicHeight); │ │ │ │ + aspectRatio = style.graphicWidth / style.graphicHeight │ │ │ │ } │ │ │ │ - return extent │ │ │ │ - }, │ │ │ │ - getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ - return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ - return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - initMercatorParameters: function() { │ │ │ │ - this.RESOLUTIONS = []; │ │ │ │ - var maxResolution = 156543.03390625; │ │ │ │ - for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) { │ │ │ │ - this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom) │ │ │ │ + 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"; │ │ │ │ + var image = document.getElementById(node.id + "_image"); │ │ │ │ + if (!image) { │ │ │ │ + image = this.createNode("olv:imagedata", node.id + "_image"); │ │ │ │ + node.appendChild(image) │ │ │ │ } │ │ │ │ - this.units = "m"; │ │ │ │ - this.projection = this.projection || "EPSG:900913" │ │ │ │ + image.style.width = width + "px"; │ │ │ │ + image.style.height = height + "px"; │ │ │ │ + image.src = style.externalGraphic; │ │ │ │ + image.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + "src='', sizingMethod='scale')"; │ │ │ │ + var rot = rotation * Math.PI / 180; │ │ │ │ + var sintheta = Math.sin(rot); │ │ │ │ + var costheta = Math.cos(rot); │ │ │ │ + var filter = "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta + ",M12=" + -sintheta + ",M21=" + sintheta + ",M22=" + costheta + ",SizingMethod='auto expand')\n"; │ │ │ │ + var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ + if (opacity && opacity != 1) { │ │ │ │ + filter += "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + opacity + ")\n" │ │ │ │ + } │ │ │ │ + node.style.filter = filter; │ │ │ │ + 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" │ │ │ │ }, │ │ │ │ - forwardMercator: function() { │ │ │ │ - var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ - var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ - return function(lon, lat) { │ │ │ │ - var point = OpenLayers.Projection.transform({ │ │ │ │ - x: lon, │ │ │ │ - y: lat │ │ │ │ - }, gg, sm); │ │ │ │ - return new OpenLayers.LonLat(point.x, point.y) │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - }(), │ │ │ │ - inverseMercator: function() { │ │ │ │ - var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ - var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ - return function(x, y) { │ │ │ │ - var point = OpenLayers.Projection.transform({ │ │ │ │ - x: x, │ │ │ │ - y: y │ │ │ │ - }, sm, gg); │ │ │ │ - return new OpenLayers.LonLat(point.x, point.y) │ │ │ │ + if (strokeColor == "none" && node.strokecolor != strokeColor) { │ │ │ │ + node.strokecolor = strokeColor │ │ │ │ } │ │ │ │ - }() │ │ │ │ -}; │ │ │ │ -OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ - smoothDragPan: true, │ │ │ │ - isBaseLayer: true, │ │ │ │ - isFixed: true, │ │ │ │ - pane: null, │ │ │ │ - mapObject: null, │ │ │ │ - initialize: function(name, options) { │ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ - if (this.pane == null) { │ │ │ │ - this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane") │ │ │ │ + }, │ │ │ │ + setNodeDimension: function(node, geometry) { │ │ │ │ + var bbox = geometry.getBounds(); │ │ │ │ + if (bbox) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var scaledBox = new OpenLayers.Bounds((bbox.left - this.featureDx) / resolution - this.offset.x | 0, bbox.bottom / resolution - this.offset.y | 0, (bbox.right - this.featureDx) / resolution - this.offset.x | 0, bbox.top / resolution - this.offset.y | 0); │ │ │ │ + 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() │ │ │ │ } │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - this.mapObject = null; │ │ │ │ - this.pane = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments) │ │ │ │ + dashStyle: function(style) { │ │ │ │ + var dash = style.strokeDashstyle; │ │ │ │ + switch (dash) { │ │ │ │ + case "solid": │ │ │ │ + case "dot": │ │ │ │ + case "dash": │ │ │ │ + case "dashdot": │ │ │ │ + case "longdash": │ │ │ │ + case "longdashdot": │ │ │ │ + return dash; │ │ │ │ + default: │ │ │ │ + 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" │ │ │ │ + } │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ - this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; │ │ │ │ - this.pane.style.display = this.div.style.display; │ │ │ │ - this.pane.style.width = "100%"; │ │ │ │ - this.pane.style.height = "100%"; │ │ │ │ - if (OpenLayers.BROWSER_NAME == "msie") { │ │ │ │ - this.pane.style.background = "url(" + OpenLayers.Util.getImageLocation("blank.gif") + ")" │ │ │ │ + createNode: function(type, id) { │ │ │ │ + var node = document.createElement(type); │ │ │ │ + if (id) { │ │ │ │ + node.id = id │ │ │ │ } │ │ │ │ - if (this.isFixed) { │ │ │ │ - this.map.viewPortDiv.appendChild(this.pane) │ │ │ │ - } else { │ │ │ │ - this.map.layerContainerDiv.appendChild(this.pane) │ │ │ │ + node.unselectable = "on"; │ │ │ │ + node.onselectstart = OpenLayers.Function.False; │ │ │ │ + return node │ │ │ │ + }, │ │ │ │ + nodeTypeCompare: function(node, type) { │ │ │ │ + var subType = type; │ │ │ │ + var splitIndex = subType.indexOf(":"); │ │ │ │ + if (splitIndex != -1) { │ │ │ │ + subType = subType.substr(splitIndex + 1) │ │ │ │ } │ │ │ │ - this.loadMapObject(); │ │ │ │ - if (this.mapObject == null) { │ │ │ │ - this.loadWarningMessage() │ │ │ │ + var nodeName = node.nodeName; │ │ │ │ + splitIndex = nodeName.indexOf(":"); │ │ │ │ + if (splitIndex != -1) { │ │ │ │ + nodeName = nodeName.substr(splitIndex + 1) │ │ │ │ } │ │ │ │ + return subType == nodeName │ │ │ │ }, │ │ │ │ - removeMap: function(map) { │ │ │ │ - if (this.pane && this.pane.parentNode) { │ │ │ │ - this.pane.parentNode.removeChild(this.pane) │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.prototype.removeMap.apply(this, arguments) │ │ │ │ + createRenderRoot: function() { │ │ │ │ + return this.nodeFactory(this.container.id + "_vmlRoot", "div") │ │ │ │ }, │ │ │ │ - loadWarningMessage: function() { │ │ │ │ - this.div.style.backgroundColor = "darkblue"; │ │ │ │ - var viewSize = this.map.getSize(); │ │ │ │ - var msgW = Math.min(viewSize.w, 300); │ │ │ │ - var msgH = Math.min(viewSize.h, 200); │ │ │ │ - var size = new OpenLayers.Size(msgW, msgH); │ │ │ │ - var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2); │ │ │ │ - var topLeft = centerPx.add(-size.w / 2, -size.h / 2); │ │ │ │ - var div = OpenLayers.Util.createDiv(this.name + "_warning", topLeft, size, null, null, null, "auto"); │ │ │ │ - div.style.padding = "7px"; │ │ │ │ - div.style.backgroundColor = "yellow"; │ │ │ │ - div.innerHTML = this.getWarningHTML(); │ │ │ │ - this.div.appendChild(div) │ │ │ │ + createRoot: function(suffix) { │ │ │ │ + return this.nodeFactory(this.container.id + suffix, "olv:group") │ │ │ │ }, │ │ │ │ - getWarningHTML: function() { │ │ │ │ - return "" │ │ │ │ + drawPoint: function(node, geometry) { │ │ │ │ + return this.drawCircle(node, geometry, 1) │ │ │ │ }, │ │ │ │ - display: function(display) { │ │ │ │ - OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ - this.pane.style.display = this.div.style.display │ │ │ │ + drawCircle: function(node, geometry, radius) { │ │ │ │ + if (!isNaN(geometry.x) && !isNaN(geometry.y)) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + node.style.left = ((geometry.x - this.featureDx) / resolution - this.offset.x | 0) - radius + "px"; │ │ │ │ + node.style.top = (geometry.y / resolution - this.offset.y | 0) - radius + "px"; │ │ │ │ + var diameter = radius * 2; │ │ │ │ + node.style.width = diameter + "px"; │ │ │ │ + node.style.height = diameter + "px"; │ │ │ │ + return node │ │ │ │ + } │ │ │ │ + return false │ │ │ │ }, │ │ │ │ - setZIndex: function(zIndex) { │ │ │ │ - OpenLayers.Layer.prototype.setZIndex.apply(this, arguments); │ │ │ │ - this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1 │ │ │ │ + drawLineString: function(node, geometry) { │ │ │ │ + return this.drawLine(node, geometry, false) │ │ │ │ }, │ │ │ │ - moveByPx: function(dx, dy) { │ │ │ │ - OpenLayers.Layer.prototype.moveByPx.apply(this, arguments); │ │ │ │ - if (this.dragPanMapObject) { │ │ │ │ - this.dragPanMapObject(dx, -dy) │ │ │ │ - } else { │ │ │ │ - this.moveTo(this.map.getCachedCenter()) │ │ │ │ + drawLinearRing: function(node, geometry) { │ │ │ │ + return this.drawLine(node, geometry, true) │ │ │ │ + }, │ │ │ │ + drawLine: function(node, geometry, closeLine) { │ │ │ │ + this.setNodeDimension(node, geometry); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var numComponents = geometry.components.length; │ │ │ │ + var parts = new Array(numComponents); │ │ │ │ + var comp, x, y; │ │ │ │ + for (var i = 0; i < numComponents; i++) { │ │ │ │ + comp = geometry.components[i]; │ │ │ │ + x = (comp.x - this.featureDx) / resolution - this.offset.x | 0; │ │ │ │ + y = comp.y / resolution - this.offset.y | 0; │ │ │ │ + parts[i] = " " + x + "," + y + " l " │ │ │ │ } │ │ │ │ + var end = closeLine ? " x e" : " e"; │ │ │ │ + node.path = "m" + parts.join("") + end; │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ - if (this.mapObject != null) { │ │ │ │ - var newCenter = this.map.getCenter(); │ │ │ │ - var newZoom = this.map.getZoom(); │ │ │ │ - if (newCenter != null) { │ │ │ │ - var moOldCenter = this.getMapObjectCenter(); │ │ │ │ - var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter); │ │ │ │ - var moOldZoom = this.getMapObjectZoom(); │ │ │ │ - var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom); │ │ │ │ - if (!newCenter.equals(oldCenter) || newZoom != oldZoom) { │ │ │ │ - if (!zoomChanged && oldCenter && this.dragPanMapObject && this.smoothDragPan) { │ │ │ │ - var oldPx = this.map.getViewPortPxFromLonLat(oldCenter); │ │ │ │ - var newPx = this.map.getViewPortPxFromLonLat(newCenter); │ │ │ │ - this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y) │ │ │ │ - } else { │ │ │ │ - var center = this.getMapObjectLonLatFromOLLonLat(newCenter); │ │ │ │ - var zoom = this.getMapObjectZoomFromOLZoom(newZoom); │ │ │ │ - this.setMapObjectCenter(center, zoom, dragging) │ │ │ │ + drawPolygon: function(node, geometry) { │ │ │ │ + this.setNodeDimension(node, geometry); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var path = []; │ │ │ │ + var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y; │ │ │ │ + for (j = 0, jj = geometry.components.length; j < jj; j++) { │ │ │ │ + path.push("m"); │ │ │ │ + points = geometry.components[j].components; │ │ │ │ + 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) { │ │ │ │ + if (!first) { │ │ │ │ + first = pathComp │ │ │ │ + } else if (first != pathComp) { │ │ │ │ + if (!second) { │ │ │ │ + second = pathComp │ │ │ │ + } else if (second != pathComp) { │ │ │ │ + area = true │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ + path.push(area ? " x " : " ") │ │ │ │ } │ │ │ │ + path.push("e"); │ │ │ │ + node.path = path.join(""); │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ - var lonlat = null; │ │ │ │ - if (this.mapObject != null && this.getMapObjectCenter() != null) { │ │ │ │ - var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx); │ │ │ │ - var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel); │ │ │ │ - lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat) │ │ │ │ - } │ │ │ │ - return lonlat │ │ │ │ + drawRectangle: function(node, geometry) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + node.style.left = ((geometry.x - this.featureDx) / resolution - this.offset.x | 0) + "px"; │ │ │ │ + node.style.top = (geometry.y / resolution - this.offset.y | 0) + "px"; │ │ │ │ + node.style.width = (geometry.width / resolution | 0) + "px"; │ │ │ │ + node.style.height = (geometry.height / resolution | 0) + "px"; │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ - var viewPortPx = null; │ │ │ │ - if (this.mapObject != null && this.getMapObjectCenter() != null) { │ │ │ │ - var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat); │ │ │ │ - var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat); │ │ │ │ - viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel) │ │ │ │ + drawText: function(featureId, style, location) { │ │ │ │ + var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect"); │ │ │ │ + var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox"); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + label.style.left = ((location.x - this.featureDx) / resolution - this.offset.x | 0) + "px"; │ │ │ │ + label.style.top = (location.y / resolution - this.offset.y | 0) + "px"; │ │ │ │ + label.style.flip = "y"; │ │ │ │ + textbox.innerText = style.label; │ │ │ │ + if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ + textbox.style.cursor = style.cursor │ │ │ │ } │ │ │ │ - return viewPortPx │ │ │ │ - }, │ │ │ │ - getOLLonLatFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - var olLonLat = null; │ │ │ │ - if (moLonLat != null) { │ │ │ │ - var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - olLonLat = new OpenLayers.LonLat(lon, lat) │ │ │ │ + if (style.fontColor) { │ │ │ │ + textbox.style.color = style.fontColor │ │ │ │ } │ │ │ │ - return olLonLat │ │ │ │ - }, │ │ │ │ - getMapObjectLonLatFromOLLonLat: function(olLonLat) { │ │ │ │ - var moLatLng = null; │ │ │ │ - if (olLonLat != null) { │ │ │ │ - moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, olLonLat.lat) │ │ │ │ + if (style.fontOpacity) { │ │ │ │ + textbox.style.filter = "alpha(opacity=" + style.fontOpacity * 100 + ")" │ │ │ │ } │ │ │ │ - return moLatLng │ │ │ │ - }, │ │ │ │ - getOLPixelFromMapObjectPixel: function(moPixel) { │ │ │ │ - var olPixel = null; │ │ │ │ - if (moPixel != null) { │ │ │ │ - var x = this.getXFromMapObjectPixel(moPixel); │ │ │ │ - var y = this.getYFromMapObjectPixel(moPixel); │ │ │ │ - olPixel = new OpenLayers.Pixel(x, y) │ │ │ │ + if (style.fontFamily) { │ │ │ │ + textbox.style.fontFamily = style.fontFamily │ │ │ │ } │ │ │ │ - return olPixel │ │ │ │ - }, │ │ │ │ - getMapObjectPixelFromOLPixel: function(olPixel) { │ │ │ │ - var moPixel = null; │ │ │ │ - if (olPixel != null) { │ │ │ │ - moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y) │ │ │ │ + if (style.fontSize) { │ │ │ │ + textbox.style.fontSize = style.fontSize │ │ │ │ } │ │ │ │ - return moPixel │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.EventPane" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({ │ │ │ │ - initialize: function() {}, │ │ │ │ - initResolutions: function() { │ │ │ │ - var props = ["minZoomLevel", "maxZoomLevel", "numZoomLevels"]; │ │ │ │ - for (var i = 0, len = props.length; i < len; i++) { │ │ │ │ - var property = props[i]; │ │ │ │ - this[property] = this.options[property] != null ? this.options[property] : this.map[property] │ │ │ │ + if (style.fontWeight) { │ │ │ │ + textbox.style.fontWeight = style.fontWeight │ │ │ │ } │ │ │ │ - if (this.minZoomLevel == null || this.minZoomLevel < this.MIN_ZOOM_LEVEL) { │ │ │ │ - this.minZoomLevel = this.MIN_ZOOM_LEVEL │ │ │ │ + if (style.fontStyle) { │ │ │ │ + textbox.style.fontStyle = style.fontStyle │ │ │ │ } │ │ │ │ - var desiredZoomLevels; │ │ │ │ - var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1; │ │ │ │ - if (this.options.numZoomLevels == null && this.options.maxZoomLevel != null || this.numZoomLevels == null && this.maxZoomLevel != null) { │ │ │ │ - desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1 │ │ │ │ - } else { │ │ │ │ - desiredZoomLevels = this.numZoomLevels │ │ │ │ + if (style.labelSelect === true) { │ │ │ │ + label._featureId = featureId; │ │ │ │ + textbox._featureId = featureId; │ │ │ │ + textbox._geometry = location; │ │ │ │ + textbox._geometryClass = location.CLASS_NAME │ │ │ │ } │ │ │ │ - if (desiredZoomLevels != null) { │ │ │ │ - this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels) │ │ │ │ - } else { │ │ │ │ - this.numZoomLevels = limitZoomLevels │ │ │ │ + textbox.style.whiteSpace = "nowrap"; │ │ │ │ + textbox.inset = "1px,0px,0px,0px"; │ │ │ │ + if (!label.parentNode) { │ │ │ │ + label.appendChild(textbox); │ │ │ │ + this.textRoot.appendChild(label) │ │ │ │ } │ │ │ │ - this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1; │ │ │ │ - if (this.RESOLUTIONS != null) { │ │ │ │ - var resolutionsIndex = 0; │ │ │ │ - this.resolutions = []; │ │ │ │ - for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) { │ │ │ │ - this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i] │ │ │ │ - } │ │ │ │ - this.maxResolution = this.resolutions[0]; │ │ │ │ - this.minResolution = this.resolutions[this.resolutions.length - 1] │ │ │ │ + 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" │ │ │ │ }, │ │ │ │ - getResolution: function() { │ │ │ │ - if (this.resolutions != null) { │ │ │ │ - return OpenLayers.Layer.prototype.getResolution.apply(this, arguments) │ │ │ │ - } else { │ │ │ │ - var resolution = null; │ │ │ │ - var viewSize = this.map.getSize(); │ │ │ │ - var extent = this.getExtent(); │ │ │ │ - if (viewSize != null && extent != null) { │ │ │ │ - resolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h) │ │ │ │ - } │ │ │ │ - return resolution │ │ │ │ + moveRoot: function(renderer) { │ │ │ │ + var layer = this.map.getLayer(renderer.container.id); │ │ │ │ + if (layer instanceof OpenLayers.Layer.Vector.RootContainer) { │ │ │ │ + layer = this.map.getLayer(this.container.id) │ │ │ │ } │ │ │ │ + layer && layer.renderer.clear(); │ │ │ │ + OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments); │ │ │ │ + layer && layer.redraw() │ │ │ │ }, │ │ │ │ - getExtent: function() { │ │ │ │ - var size = this.map.getSize(); │ │ │ │ - var tl = this.getLonLatFromViewPortPx({ │ │ │ │ - x: 0, │ │ │ │ - y: 0 │ │ │ │ - }); │ │ │ │ - var br = this.getLonLatFromViewPortPx({ │ │ │ │ - x: size.w, │ │ │ │ - y: size.h │ │ │ │ - }); │ │ │ │ - if (tl != null && br != null) { │ │ │ │ - return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat) │ │ │ │ - } else { │ │ │ │ - return null │ │ │ │ + importSymbol: function(graphicName) { │ │ │ │ + var id = this.container.id + "-" + graphicName; │ │ │ │ + var cache = this.symbolCache[id]; │ │ │ │ + if (cache) { │ │ │ │ + return cache │ │ │ │ } │ │ │ │ - }, │ │ │ │ - getZoomForResolution: function(resolution) { │ │ │ │ - if (this.resolutions != null) { │ │ │ │ - return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments) │ │ │ │ - } else { │ │ │ │ - var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []); │ │ │ │ - return this.getZoomForExtent(extent) │ │ │ │ + var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ + if (!symbol) { │ │ │ │ + throw new Error(graphicName + " is not a valid symbol name") │ │ │ │ } │ │ │ │ - }, │ │ │ │ - getOLZoomFromMapObjectZoom: function(moZoom) { │ │ │ │ - var zoom = null; │ │ │ │ - if (moZoom != null) { │ │ │ │ - zoom = moZoom - this.minZoomLevel; │ │ │ │ - if (this.map.baseLayer !== this) { │ │ │ │ - zoom = this.map.baseLayer.getZoomForResolution(this.getResolutionForZoom(zoom)) │ │ │ │ + var symbolExtent = new OpenLayers.Bounds(Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ + var pathitems = ["m"]; │ │ │ │ + for (var i = 0; i < symbol.length; i = i + 2) { │ │ │ │ + var x = symbol[i]; │ │ │ │ + var y = symbol[i + 1]; │ │ │ │ + symbolExtent.left = Math.min(symbolExtent.left, x); │ │ │ │ + symbolExtent.bottom = Math.min(symbolExtent.bottom, y); │ │ │ │ + symbolExtent.right = Math.max(symbolExtent.right, x); │ │ │ │ + symbolExtent.top = Math.max(symbolExtent.top, y); │ │ │ │ + pathitems.push(x); │ │ │ │ + pathitems.push(y); │ │ │ │ + if (i == 0) { │ │ │ │ + pathitems.push("l") │ │ │ │ } │ │ │ │ } │ │ │ │ - return zoom │ │ │ │ - }, │ │ │ │ - getMapObjectZoomFromOLZoom: function(olZoom) { │ │ │ │ - var zoom = null; │ │ │ │ - if (olZoom != null) { │ │ │ │ - zoom = olZoom + this.minZoomLevel; │ │ │ │ - if (this.map.baseLayer !== this) { │ │ │ │ - zoom = this.getZoomForResolution(this.map.baseLayer.getResolutionForZoom(zoom)) │ │ │ │ - } │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - return zoom │ │ │ │ + cache = { │ │ │ │ + path: path, │ │ │ │ + size: symbolExtent.getWidth(), │ │ │ │ + left: symbolExtent.left, │ │ │ │ + bottom: symbolExtent.bottom │ │ │ │ + }; │ │ │ │ + this.symbolCache[id] = cache; │ │ │ │ + return cache │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels" │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.VML" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, { │ │ │ │ - MIN_ZOOM_LEVEL: 0, │ │ │ │ - MAX_ZOOM_LEVEL: 21, │ │ │ │ - RESOLUTIONS: [1.40625, .703125, .3515625, .17578125, .087890625, .0439453125, .02197265625, .010986328125, .0054931640625, .00274658203125, .001373291015625, .0006866455078125, .00034332275390625, .000171661376953125, 858306884765625e-19, 4291534423828125e-20, 2145767211914062e-20, 1072883605957031e-20, 536441802978515e-20, 268220901489257e-20, 1341104507446289e-21, 6.705522537231445e-7], │ │ │ │ - type: null, │ │ │ │ - wrapDateLine: true, │ │ │ │ - sphericalMercator: false, │ │ │ │ - version: null, │ │ │ │ - initialize: function(name, options) { │ │ │ │ - options = options || {}; │ │ │ │ - if (!options.version) { │ │ │ │ - options.version = typeof GMap2 === "function" ? "2" : "3" │ │ │ │ - } │ │ │ │ - var mixin = OpenLayers.Layer.Google["v" + options.version.replace(/\./g, "_")]; │ │ │ │ - if (mixin) { │ │ │ │ - OpenLayers.Util.applyDefaults(options, mixin) │ │ │ │ - } else { │ │ │ │ - throw "Unsupported Google Maps API version: " + options.version │ │ │ │ - } │ │ │ │ - OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS); │ │ │ │ - if (options.maxExtent) { │ │ │ │ - options.maxExtent = options.maxExtent.clone() │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.EventPane.prototype.initialize.apply(this, [name, options]); │ │ │ │ - OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, [name, options]); │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator); │ │ │ │ - this.initMercatorParameters() │ │ │ │ +OpenLayers.Renderer.VML.LABEL_SHIFT = { │ │ │ │ + l: 0, │ │ │ │ + c: .5, │ │ │ │ + r: 1, │ │ │ │ + t: 0, │ │ │ │ + m: .5, │ │ │ │ + b: 1 │ │ │ │ +}; │ │ │ │ +OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ + xmlns: "http://www.w3.org/2000/svg", │ │ │ │ + xlinkns: "http://www.w3.org/1999/xlink", │ │ │ │ + MAX_PIXEL: 15e3, │ │ │ │ + translationParameters: null, │ │ │ │ + symbolMetrics: null, │ │ │ │ + initialize: function(containerID) { │ │ │ │ + if (!this.supported()) { │ │ │ │ + return │ │ │ │ } │ │ │ │ + OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments); │ │ │ │ + this.translationParameters = { │ │ │ │ + x: 0, │ │ │ │ + y: 0 │ │ │ │ + }; │ │ │ │ + this.symbolMetrics = {} │ │ │ │ }, │ │ │ │ - clone: function() { │ │ │ │ - return new OpenLayers.Layer.Google(this.name, this.getOptions()) │ │ │ │ - }, │ │ │ │ - setVisibility: function(visible) { │ │ │ │ - var opacity = this.opacity == null ? 1 : this.opacity; │ │ │ │ - OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments); │ │ │ │ - this.setOpacity(opacity) │ │ │ │ - }, │ │ │ │ - display: function(visible) { │ │ │ │ - if (!this._dragging) { │ │ │ │ - this.setGMapVisibility(visible) │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments) │ │ │ │ + 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")) │ │ │ │ }, │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - this._dragging = dragging; │ │ │ │ - OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments); │ │ │ │ - delete this._dragging │ │ │ │ + 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 │ │ │ │ }, │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ - if (opacity !== this.opacity) { │ │ │ │ - if (this.map != null) { │ │ │ │ - this.map.events.triggerEvent("changelayer", { │ │ │ │ - layer: this, │ │ │ │ - property: "opacity" │ │ │ │ - }) │ │ │ │ + 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 (resolutionChanged) { │ │ │ │ + this.left = left; │ │ │ │ + this.top = top; │ │ │ │ + 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) { │ │ │ │ + this.setExtent(extent, true) │ │ │ │ } │ │ │ │ - this.opacity = opacity │ │ │ │ - } │ │ │ │ - if (this.getVisibility()) { │ │ │ │ - var container = this.getMapContainer(); │ │ │ │ - OpenLayers.Util.modifyDOMElement(container, null, null, null, null, null, null, opacity) │ │ │ │ + return coordSysUnchanged && inRange │ │ │ │ } │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.setGMapVisibility(false); │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache && cache.count <= 1) { │ │ │ │ - this.removeGMapElements() │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments) │ │ │ │ }, │ │ │ │ - removeGMapElements: function() { │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - var container = this.mapObject && this.getMapContainer(); │ │ │ │ - if (container && container.parentNode) { │ │ │ │ - container.parentNode.removeChild(container) │ │ │ │ - } │ │ │ │ - var termsOfUse = cache.termsOfUse; │ │ │ │ - if (termsOfUse && termsOfUse.parentNode) { │ │ │ │ - termsOfUse.parentNode.removeChild(termsOfUse) │ │ │ │ - } │ │ │ │ - var poweredBy = cache.poweredBy; │ │ │ │ - if (poweredBy && poweredBy.parentNode) { │ │ │ │ - poweredBy.parentNode.removeChild(poweredBy) │ │ │ │ - } │ │ │ │ - if (this.mapObject && window.google && google.maps && google.maps.event && google.maps.event.clearListeners) { │ │ │ │ - google.maps.event.clearListeners(this.mapObject, "tilesloaded") │ │ │ │ - } │ │ │ │ - } │ │ │ │ + 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) │ │ │ │ }, │ │ │ │ - removeMap: function(map) { │ │ │ │ - if (this.visibility && this.mapObject) { │ │ │ │ - this.setGMapVisibility(false) │ │ │ │ + getNodeType: function(geometry, style) { │ │ │ │ + var nodeType = null; │ │ │ │ + switch (geometry.CLASS_NAME) { │ │ │ │ + case "OpenLayers.Geometry.Point": │ │ │ │ + if (style.externalGraphic) { │ │ │ │ + nodeType = "image" │ │ │ │ + } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ + nodeType = "svg" │ │ │ │ + } else { │ │ │ │ + nodeType = "circle" │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Rectangle": │ │ │ │ + nodeType = "rect"; │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LineString": │ │ │ │ + nodeType = "polyline"; │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LinearRing": │ │ │ │ + nodeType = "polygon"; │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Polygon": │ │ │ │ + case "OpenLayers.Geometry.Curve": │ │ │ │ + nodeType = "path"; │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + break │ │ │ │ } │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[map.id]; │ │ │ │ - if (cache) { │ │ │ │ - if (cache.count <= 1) { │ │ │ │ - this.removeGMapElements(); │ │ │ │ - delete OpenLayers.Layer.Google.cache[map.id] │ │ │ │ + return nodeType │ │ │ │ + }, │ │ │ │ + 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); │ │ │ │ + var titleNode = node.getElementsByTagName("title"); │ │ │ │ + if (titleNode.length > 0) { │ │ │ │ + titleNode[0].firstChild.textContent = title │ │ │ │ } else { │ │ │ │ - --cache.count │ │ │ │ + var label = this.nodeFactory(null, "title"); │ │ │ │ + label.textContent = title; │ │ │ │ + node.appendChild(label) │ │ │ │ } │ │ │ │ } │ │ │ │ - delete this.termsOfUse; │ │ │ │ - delete this.poweredBy; │ │ │ │ - delete this.mapObject; │ │ │ │ - delete this.dragObject; │ │ │ │ - OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - getOLBoundsFromMapObjectBounds: function(moBounds) { │ │ │ │ - var olBounds = null; │ │ │ │ - if (moBounds != null) { │ │ │ │ - var sw = moBounds.getSouthWest(); │ │ │ │ - var ne = moBounds.getNorthEast(); │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - sw = this.forwardMercator(sw.lng(), sw.lat()); │ │ │ │ - ne = this.forwardMercator(ne.lng(), ne.lat()) │ │ │ │ + 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 : -(.5 * width); │ │ │ │ + var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.5 * height); │ │ │ │ + var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ + node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed()); │ │ │ │ + node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed()); │ │ │ │ + node.setAttributeNS(null, "width", width); │ │ │ │ + node.setAttributeNS(null, "height", height); │ │ │ │ + node.setAttributeNS(this.xlinkns, "xlink:href", style.externalGraphic); │ │ │ │ + node.setAttributeNS(null, "style", "opacity: " + opacity); │ │ │ │ + node.onclick = OpenLayers.Event.preventDefault │ │ │ │ + } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ + 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; │ │ │ │ + var parent = node.parentNode; │ │ │ │ + var nextSibling = node.nextSibling; │ │ │ │ + if (parent) { │ │ │ │ + parent.removeChild(node) │ │ │ │ + } │ │ │ │ + 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); │ │ │ │ + if (nextSibling) { │ │ │ │ + parent.insertBefore(node, nextSibling) │ │ │ │ + } else if (parent) { │ │ │ │ + parent.appendChild(node) │ │ │ │ + } │ │ │ │ } else { │ │ │ │ - sw = new OpenLayers.LonLat(sw.lng(), sw.lat()); │ │ │ │ - ne = new OpenLayers.LonLat(ne.lng(), ne.lat()) │ │ │ │ - } │ │ │ │ - olBounds = new OpenLayers.Bounds(sw.lon, sw.lat, ne.lon, ne.lat) │ │ │ │ - } │ │ │ │ - return olBounds │ │ │ │ - }, │ │ │ │ - getWarningHTML: function() { │ │ │ │ - return OpenLayers.i18n("googleWarning") │ │ │ │ - }, │ │ │ │ - getMapObjectCenter: function() { │ │ │ │ - return this.mapObject.getCenter() │ │ │ │ - }, │ │ │ │ - getMapObjectZoom: function() { │ │ │ │ - return this.mapObject.getZoom() │ │ │ │ - }, │ │ │ │ - getLongitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - return this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : moLonLat.lng() │ │ │ │ - }, │ │ │ │ - getLatitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - var lat = this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : moLonLat.lat(); │ │ │ │ - return lat │ │ │ │ - }, │ │ │ │ - getXFromMapObjectPixel: function(moPixel) { │ │ │ │ - return moPixel.x │ │ │ │ - }, │ │ │ │ - getYFromMapObjectPixel: function(moPixel) { │ │ │ │ - return moPixel.y │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Google" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.Google.cache = {}; │ │ │ │ -OpenLayers.Layer.Google.v2 = { │ │ │ │ - termsOfUse: null, │ │ │ │ - poweredBy: null, │ │ │ │ - dragObject: null, │ │ │ │ - loadMapObject: function() { │ │ │ │ - if (!this.type) { │ │ │ │ - this.type = G_NORMAL_MAP │ │ │ │ - } │ │ │ │ - var mapObject, termsOfUse, poweredBy; │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - mapObject = cache.mapObject; │ │ │ │ - termsOfUse = cache.termsOfUse; │ │ │ │ - poweredBy = cache.poweredBy; │ │ │ │ - ++cache.count │ │ │ │ - } else { │ │ │ │ - var container = this.map.viewPortDiv; │ │ │ │ - var div = document.createElement("div"); │ │ │ │ - div.id = this.map.id + "_GMap2Container"; │ │ │ │ - div.style.position = "absolute"; │ │ │ │ - div.style.width = "100%"; │ │ │ │ - div.style.height = "100%"; │ │ │ │ - container.appendChild(div); │ │ │ │ - try { │ │ │ │ - mapObject = new GMap2(div); │ │ │ │ - termsOfUse = div.lastChild; │ │ │ │ - container.appendChild(termsOfUse); │ │ │ │ - termsOfUse.style.zIndex = "1100"; │ │ │ │ - termsOfUse.style.right = ""; │ │ │ │ - termsOfUse.style.bottom = ""; │ │ │ │ - termsOfUse.className = "olLayerGoogleCopyright"; │ │ │ │ - poweredBy = div.lastChild; │ │ │ │ - container.appendChild(poweredBy); │ │ │ │ - poweredBy.style.zIndex = "1100"; │ │ │ │ - poweredBy.style.right = ""; │ │ │ │ - poweredBy.style.bottom = ""; │ │ │ │ - poweredBy.className = "olLayerGooglePoweredBy gmnoprint" │ │ │ │ - } catch (e) { │ │ │ │ - throw e │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.Google.cache[this.map.id] = { │ │ │ │ - mapObject: mapObject, │ │ │ │ - termsOfUse: termsOfUse, │ │ │ │ - poweredBy: poweredBy, │ │ │ │ - count: 1 │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.mapObject = mapObject; │ │ │ │ - this.termsOfUse = termsOfUse; │ │ │ │ - this.poweredBy = poweredBy; │ │ │ │ - if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), this.type) === -1) { │ │ │ │ - this.mapObject.addMapType(this.type) │ │ │ │ - } │ │ │ │ - if (typeof mapObject.getDragObject == "function") { │ │ │ │ - this.dragObject = mapObject.getDragObject() │ │ │ │ - } else { │ │ │ │ - this.dragPanMapObject = null │ │ │ │ - } │ │ │ │ - if (this.isBaseLayer === false) { │ │ │ │ - this.setGMapVisibility(this.div.style.display !== "none") │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - onMapResize: function() { │ │ │ │ - if (this.visibility && this.mapObject.isLoaded()) { │ │ │ │ - this.mapObject.checkResize() │ │ │ │ - } else { │ │ │ │ - if (!this._resized) { │ │ │ │ - var layer = this; │ │ │ │ - var handle = GEvent.addListener(this.mapObject, "load", function() { │ │ │ │ - GEvent.removeListener(handle); │ │ │ │ - delete layer._resized; │ │ │ │ - layer.mapObject.checkResize(); │ │ │ │ - layer.moveTo(layer.map.getCenter(), layer.map.getZoom()) │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - this._resized = true │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - setGMapVisibility: function(visible) { │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - var container = this.mapObject.getContainer(); │ │ │ │ - if (visible === true) { │ │ │ │ - this.mapObject.setMapType(this.type); │ │ │ │ - container.style.display = ""; │ │ │ │ - this.termsOfUse.style.left = ""; │ │ │ │ - this.termsOfUse.style.display = ""; │ │ │ │ - this.poweredBy.style.display = ""; │ │ │ │ - cache.displayed = this.id │ │ │ │ - } else { │ │ │ │ - if (cache.displayed === this.id) { │ │ │ │ - delete cache.displayed │ │ │ │ - } │ │ │ │ - if (!cache.displayed) { │ │ │ │ - container.style.display = "none"; │ │ │ │ - this.termsOfUse.style.display = "none"; │ │ │ │ - this.termsOfUse.style.left = "-9999px"; │ │ │ │ - this.poweredBy.style.display = "none" │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getMapContainer: function() { │ │ │ │ - return this.mapObject.getContainer() │ │ │ │ - }, │ │ │ │ - getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ - var moBounds = null; │ │ │ │ - if (olBounds != null) { │ │ │ │ - var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ - var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ - moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), new GLatLng(ne.lat, ne.lon)) │ │ │ │ - } │ │ │ │ - return moBounds │ │ │ │ - }, │ │ │ │ - setMapObjectCenter: function(center, zoom) { │ │ │ │ - this.mapObject.setCenter(center, zoom) │ │ │ │ - }, │ │ │ │ - dragPanMapObject: function(dX, dY) { │ │ │ │ - this.dragObject.moveBy(new GSize(-dX, dY)) │ │ │ │ - }, │ │ │ │ - getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ - return this.mapObject.fromContainerPixelToLatLng(moPixel) │ │ │ │ - }, │ │ │ │ - getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - return this.mapObject.fromLatLngToContainerPixel(moLonLat) │ │ │ │ - }, │ │ │ │ - getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ - return this.mapObject.getBoundsZoomLevel(moBounds) │ │ │ │ - }, │ │ │ │ - getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ - var gLatLng; │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - var lonlat = this.inverseMercator(lon, lat); │ │ │ │ - gLatLng = new GLatLng(lonlat.lat, lonlat.lon) │ │ │ │ - } else { │ │ │ │ - gLatLng = new GLatLng(lat, lon) │ │ │ │ - } │ │ │ │ - return gLatLng │ │ │ │ - }, │ │ │ │ - getMapObjectPixelFromXY: function(x, y) { │ │ │ │ - return new GPoint(x, y) │ │ │ │ - } │ │ │ │ -}; │ │ │ │ -OpenLayers.Layer.Google.v3 = { │ │ │ │ - DEFAULTS: { │ │ │ │ - sphericalMercator: true, │ │ │ │ - projection: "EPSG:900913" │ │ │ │ - }, │ │ │ │ - animationEnabled: true, │ │ │ │ - loadMapObject: function() { │ │ │ │ - if (!this.type) { │ │ │ │ - this.type = google.maps.MapTypeId.ROADMAP │ │ │ │ - } │ │ │ │ - var mapObject; │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - mapObject = cache.mapObject; │ │ │ │ - ++cache.count │ │ │ │ - } else { │ │ │ │ - var center = this.map.getCenter(); │ │ │ │ - var container = document.createElement("div"); │ │ │ │ - container.className = "olForeignContainer"; │ │ │ │ - container.style.width = "100%"; │ │ │ │ - container.style.height = "100%"; │ │ │ │ - mapObject = new google.maps.Map(container, { │ │ │ │ - center: center ? new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0), │ │ │ │ - zoom: this.map.getZoom() || 0, │ │ │ │ - mapTypeId: this.type, │ │ │ │ - disableDefaultUI: true, │ │ │ │ - keyboardShortcuts: false, │ │ │ │ - draggable: false, │ │ │ │ - disableDoubleClickZoom: true, │ │ │ │ - scrollwheel: false, │ │ │ │ - streetViewControl: false │ │ │ │ - }); │ │ │ │ - var googleControl = document.createElement("div"); │ │ │ │ - googleControl.style.width = "100%"; │ │ │ │ - googleControl.style.height = "100%"; │ │ │ │ - mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl); │ │ │ │ - cache = { │ │ │ │ - googleControl: googleControl, │ │ │ │ - mapObject: mapObject, │ │ │ │ - count: 1 │ │ │ │ - }; │ │ │ │ - OpenLayers.Layer.Google.cache[this.map.id] = cache │ │ │ │ - } │ │ │ │ - this.mapObject = mapObject; │ │ │ │ - this.setGMapVisibility(this.visibility) │ │ │ │ - }, │ │ │ │ - onMapResize: function() { │ │ │ │ - if (this.visibility) { │ │ │ │ - google.maps.event.trigger(this.mapObject, "resize") │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - setGMapVisibility: function(visible) { │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - var map = this.map; │ │ │ │ - if (cache) { │ │ │ │ - var type = this.type; │ │ │ │ - var layers = map.layers; │ │ │ │ - var layer; │ │ │ │ - for (var i = layers.length - 1; i >= 0; --i) { │ │ │ │ - layer = layers[i]; │ │ │ │ - if (layer instanceof OpenLayers.Layer.Google && layer.visibility === true && layer.inRange === true) { │ │ │ │ - type = layer.type; │ │ │ │ - visible = true; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var container = this.mapObject.getDiv(); │ │ │ │ - if (visible === true) { │ │ │ │ - if (container.parentNode !== map.div) { │ │ │ │ - if (!cache.rendered) { │ │ │ │ - var me = this; │ │ │ │ - google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function() { │ │ │ │ - cache.rendered = true; │ │ │ │ - me.setGMapVisibility(me.getVisibility()); │ │ │ │ - me.moveTo(me.map.getCenter()) │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - map.div.appendChild(container); │ │ │ │ - cache.googleControl.appendChild(map.viewPortDiv); │ │ │ │ - google.maps.event.trigger(this.mapObject, "resize") │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.mapObject.setMapTypeId(type) │ │ │ │ - } else if (cache.googleControl.hasChildNodes()) { │ │ │ │ - map.div.appendChild(map.viewPortDiv); │ │ │ │ - map.div.removeChild(container) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getMapContainer: function() { │ │ │ │ - return this.mapObject.getDiv() │ │ │ │ - }, │ │ │ │ - getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ - var moBounds = null; │ │ │ │ - if (olBounds != null) { │ │ │ │ - var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ - var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ - moBounds = new google.maps.LatLngBounds(new google.maps.LatLng(sw.lat, sw.lon), new google.maps.LatLng(ne.lat, ne.lon)) │ │ │ │ - } │ │ │ │ - return moBounds │ │ │ │ - }, │ │ │ │ - getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ - var size = this.map.getSize(); │ │ │ │ - var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ - var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ - var res = this.map.getResolution(); │ │ │ │ - var delta_x = moPixel.x - size.w / 2; │ │ │ │ - var delta_y = moPixel.y - size.h / 2; │ │ │ │ - var lonlat = new OpenLayers.LonLat(lon + delta_x * res, lat - delta_y * res); │ │ │ │ - if (this.wrapDateLine) { │ │ │ │ - lonlat = lonlat.wrapDateLine(this.maxExtent) │ │ │ │ - } │ │ │ │ - return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat) │ │ │ │ - }, │ │ │ │ - getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - var res = this.map.getResolution(); │ │ │ │ - var extent = this.map.getExtent(); │ │ │ │ - return this.getMapObjectPixelFromXY(1 / res * (lon - extent.left), 1 / res * (extent.top - lat)) │ │ │ │ - }, │ │ │ │ - setMapObjectCenter: function(center, zoom) { │ │ │ │ - if (this.animationEnabled === false && zoom != this.mapObject.zoom) { │ │ │ │ - var mapContainer = this.getMapContainer(); │ │ │ │ - google.maps.event.addListenerOnce(this.mapObject, "idle", function() { │ │ │ │ - mapContainer.style.visibility = "" │ │ │ │ - }); │ │ │ │ - mapContainer.style.visibility = "hidden" │ │ │ │ - } │ │ │ │ - this.mapObject.setOptions({ │ │ │ │ - center: center, │ │ │ │ - zoom: zoom │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ - return this.mapObject.getBoundsZoomLevel(moBounds) │ │ │ │ - }, │ │ │ │ - getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ - var gLatLng; │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - var lonlat = this.inverseMercator(lon, lat); │ │ │ │ - gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon) │ │ │ │ - } else { │ │ │ │ - gLatLng = new google.maps.LatLng(lat, lon) │ │ │ │ - } │ │ │ │ - return gLatLng │ │ │ │ - }, │ │ │ │ - getMapObjectPixelFromXY: function(x, y) { │ │ │ │ - return new google.maps.Point(x, y) │ │ │ │ - } │ │ │ │ -}; │ │ │ │ -OpenLayers.Control = OpenLayers.Class({ │ │ │ │ - id: null, │ │ │ │ - map: null, │ │ │ │ - div: null, │ │ │ │ - type: null, │ │ │ │ - allowSelection: false, │ │ │ │ - displayClass: "", │ │ │ │ - title: "", │ │ │ │ - autoActivate: false, │ │ │ │ - active: null, │ │ │ │ - handlerOptions: null, │ │ │ │ - handler: null, │ │ │ │ - eventListeners: null, │ │ │ │ - events: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - this.displayClass = this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, ""); │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.events = new OpenLayers.Events(this); │ │ │ │ - if (this.eventListeners instanceof Object) { │ │ │ │ - this.events.on(this.eventListeners) │ │ │ │ - } │ │ │ │ - if (this.id == null) { │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_") │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.events) { │ │ │ │ - if (this.eventListeners) { │ │ │ │ - this.events.un(this.eventListeners) │ │ │ │ - } │ │ │ │ - this.events.destroy(); │ │ │ │ - this.events = null │ │ │ │ - } │ │ │ │ - this.eventListeners = null; │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.destroy(); │ │ │ │ - this.handler = null │ │ │ │ - } │ │ │ │ - if (this.handlers) { │ │ │ │ - for (var key in this.handlers) { │ │ │ │ - if (this.handlers.hasOwnProperty(key) && typeof this.handlers[key].destroy == "function") { │ │ │ │ - this.handlers[key].destroy() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.handlers = null │ │ │ │ - } │ │ │ │ - if (this.map) { │ │ │ │ - this.map.removeControl(this); │ │ │ │ - this.map = null │ │ │ │ - } │ │ │ │ - this.div = null │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - this.map = map; │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.setMap(map) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - draw: function(px) { │ │ │ │ - if (this.div == null) { │ │ │ │ - this.div = OpenLayers.Util.createDiv(this.id); │ │ │ │ - this.div.className = this.displayClass; │ │ │ │ - if (!this.allowSelection) { │ │ │ │ - this.div.className += " olControlNoSelect"; │ │ │ │ - this.div.setAttribute("unselectable", "on", 0); │ │ │ │ - this.div.onselectstart = OpenLayers.Function.False │ │ │ │ - } │ │ │ │ - if (this.title != "") { │ │ │ │ - this.div.title = this.title │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (px != null) { │ │ │ │ - this.position = px.clone() │ │ │ │ - } │ │ │ │ - this.moveTo(this.position); │ │ │ │ - return this.div │ │ │ │ - }, │ │ │ │ - moveTo: function(px) { │ │ │ │ - if (px != null && this.div != null) { │ │ │ │ - this.div.style.left = px.x + "px"; │ │ │ │ - this.div.style.top = px.y + "px" │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.activate() │ │ │ │ - } │ │ │ │ - this.active = true; │ │ │ │ - if (this.map) { │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, "") + "Active") │ │ │ │ - } │ │ │ │ - this.events.triggerEvent("activate"); │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.deactivate() │ │ │ │ - } │ │ │ │ - this.active = false; │ │ │ │ - if (this.map) { │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, "") + "Active") │ │ │ │ - } │ │ │ │ - this.events.triggerEvent("deactivate"); │ │ │ │ - return true │ │ │ │ - } │ │ │ │ - return false │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.TYPE_BUTTON = 1; │ │ │ │ -OpenLayers.Control.TYPE_TOGGLE = 2; │ │ │ │ -OpenLayers.Control.TYPE_TOOL = 3; │ │ │ │ -OpenLayers.Events.buttonclick = OpenLayers.Class({ │ │ │ │ - target: null, │ │ │ │ - events: ["mousedown", "mouseup", "click", "dblclick", "touchstart", "touchmove", "touchend", "keydown"], │ │ │ │ - startRegEx: /^mousedown|touchstart$/, │ │ │ │ - cancelRegEx: /^touchmove$/, │ │ │ │ - completeRegEx: /^mouseup|touchend$/, │ │ │ │ - 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 │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - for (var i = this.events.length - 1; i >= 0; --i) { │ │ │ │ - this.target.unregister(this.events[i], this, this.buttonClick) │ │ │ │ - } │ │ │ │ - delete this.target │ │ │ │ - }, │ │ │ │ - getPressedButton: function(element) { │ │ │ │ - var depth = 3, │ │ │ │ - button; │ │ │ │ - do { │ │ │ │ - if (OpenLayers.Element.hasClass(element, "olButton")) { │ │ │ │ - button = element; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - element = element.parentNode │ │ │ │ - } while (--depth > 0 && element); │ │ │ │ - return button │ │ │ │ - }, │ │ │ │ - ignore: function(element) { │ │ │ │ - var depth = 3, │ │ │ │ - ignore = false; │ │ │ │ - do { │ │ │ │ - if (element.nodeName.toLowerCase() === "a") { │ │ │ │ - ignore = true; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - element = element.parentNode │ │ │ │ - } while (--depth > 0 && element); │ │ │ │ - return ignore │ │ │ │ - }, │ │ │ │ - buttonClick: function(evt) { │ │ │ │ - var propagate = true, │ │ │ │ - element = OpenLayers.Event.element(evt); │ │ │ │ - if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) { │ │ │ │ - var button = this.getPressedButton(element); │ │ │ │ - if (button) { │ │ │ │ - if (evt.type === "keydown") { │ │ │ │ - switch (evt.keyCode) { │ │ │ │ - case OpenLayers.Event.KEY_RETURN: │ │ │ │ - case OpenLayers.Event.KEY_SPACE: │ │ │ │ - this.target.triggerEvent("buttonclick", { │ │ │ │ - buttonElement: button │ │ │ │ - }); │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - propagate = false; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } else if (this.startEvt) { │ │ │ │ - if (this.completeRegEx.test(evt.type)) { │ │ │ │ - var pos = OpenLayers.Util.pagePosition(button); │ │ │ │ - var viewportElement = OpenLayers.Util.getViewportElement(); │ │ │ │ - var scrollTop = window.pageYOffset || viewportElement.scrollTop; │ │ │ │ - var scrollLeft = window.pageXOffset || viewportElement.scrollLeft; │ │ │ │ - pos[0] = pos[0] - scrollLeft; │ │ │ │ - pos[1] = pos[1] - scrollTop; │ │ │ │ - this.target.triggerEvent("buttonclick", { │ │ │ │ - buttonElement: button, │ │ │ │ - buttonXY: { │ │ │ │ - x: this.startEvt.clientX - pos[0], │ │ │ │ - y: this.startEvt.clientY - pos[1] │ │ │ │ - } │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - if (this.cancelRegEx.test(evt.type)) { │ │ │ │ - delete this.startEvt │ │ │ │ - } │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - propagate = false │ │ │ │ - } │ │ │ │ - if (this.startRegEx.test(evt.type)) { │ │ │ │ - this.startEvt = evt; │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - propagate = false │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - propagate = !this.ignore(OpenLayers.Event.element(evt)); │ │ │ │ - delete this.startEvt │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return propagate │ │ │ │ - } │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - controls: null, │ │ │ │ - autoActivate: true, │ │ │ │ - defaultControl: null, │ │ │ │ - saveState: false, │ │ │ │ - allowDepress: false, │ │ │ │ - activeState: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.controls = []; │ │ │ │ - this.activeState = {} │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.unregister("buttonclick", this, this.onButtonClick) │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - for (var ctl, i = this.controls.length - 1; i >= 0; i--) { │ │ │ │ - ctl = this.controls[i]; │ │ │ │ - if (ctl.events) { │ │ │ │ - ctl.events.un({ │ │ │ │ - activate: this.iconOn, │ │ │ │ - deactivate: this.iconOff │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - ctl.panel_div = null │ │ │ │ - } │ │ │ │ - this.activeState = null │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - var control; │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - control = this.controls[i]; │ │ │ │ - if (control === this.defaultControl || this.saveState && this.activeState[control.id]) { │ │ │ │ - control.activate() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.saveState === true) { │ │ │ │ - this.defaultControl = null │ │ │ │ - } │ │ │ │ - this.redraw(); │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - var control; │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - control = this.controls[i]; │ │ │ │ - this.activeState[control.id] = control.deactivate() │ │ │ │ - } │ │ │ │ - this.redraw(); │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (this.outsideViewport) { │ │ │ │ - this.events.attachToElement(this.div); │ │ │ │ - this.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ - } else { │ │ │ │ - this.map.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ - } │ │ │ │ - this.addControlsToMap(this.controls); │ │ │ │ - return this.div │ │ │ │ - }, │ │ │ │ - redraw: function() { │ │ │ │ - for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) { │ │ │ │ - this.div.removeChild(this.div.childNodes[i]) │ │ │ │ - } │ │ │ │ - this.div.innerHTML = ""; │ │ │ │ - if (this.active) { │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - this.div.appendChild(this.controls[i].panel_div) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - activateControl: function(control) { │ │ │ │ - if (!this.active) { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - if (control.type == OpenLayers.Control.TYPE_BUTTON) { │ │ │ │ - control.trigger(); │ │ │ │ - return │ │ │ │ - } │ │ │ │ - if (control.type == OpenLayers.Control.TYPE_TOGGLE) { │ │ │ │ - if (control.active) { │ │ │ │ - control.deactivate() │ │ │ │ - } else { │ │ │ │ - control.activate() │ │ │ │ - } │ │ │ │ - return │ │ │ │ - } │ │ │ │ - if (this.allowDepress && control.active) { │ │ │ │ - control.deactivate() │ │ │ │ - } else { │ │ │ │ - var c; │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - c = this.controls[i]; │ │ │ │ - if (c != control && (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) { │ │ │ │ - c.deactivate() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - control.activate() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - addControls: function(controls) { │ │ │ │ - if (!OpenLayers.Util.isArray(controls)) { │ │ │ │ - controls = [controls] │ │ │ │ - } │ │ │ │ - this.controls = this.controls.concat(controls); │ │ │ │ - for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ - var control = controls[i], │ │ │ │ - element = this.createControlMarkup(control); │ │ │ │ - OpenLayers.Element.addClass(element, control.displayClass + "ItemInactive"); │ │ │ │ - OpenLayers.Element.addClass(element, "olButton"); │ │ │ │ - if (control.title != "" && !element.title) { │ │ │ │ - element.title = control.title │ │ │ │ - } │ │ │ │ - control.panel_div = element │ │ │ │ - } │ │ │ │ - if (this.map) { │ │ │ │ - this.addControlsToMap(controls); │ │ │ │ - this.redraw() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - createControlMarkup: function(control) { │ │ │ │ - return document.createElement("div") │ │ │ │ - }, │ │ │ │ - addControlsToMap: function(controls) { │ │ │ │ - var control; │ │ │ │ - for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ - control = controls[i]; │ │ │ │ - if (control.autoActivate === true) { │ │ │ │ - control.autoActivate = false; │ │ │ │ - this.map.addControl(control); │ │ │ │ - control.autoActivate = true │ │ │ │ - } else { │ │ │ │ - this.map.addControl(control); │ │ │ │ - control.deactivate() │ │ │ │ - } │ │ │ │ - control.events.on({ │ │ │ │ - activate: this.iconOn, │ │ │ │ - deactivate: this.iconOff │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - iconOn: function() { │ │ │ │ - var d = this.panel_div; │ │ │ │ - var re = new RegExp("\\b(" + this.displayClass + "Item)Inactive\\b"); │ │ │ │ - d.className = d.className.replace(re, "$1Active") │ │ │ │ - }, │ │ │ │ - iconOff: function() { │ │ │ │ - var d = this.panel_div; │ │ │ │ - var re = new RegExp("\\b(" + this.displayClass + "Item)Active\\b"); │ │ │ │ - d.className = d.className.replace(re, "$1Inactive") │ │ │ │ - }, │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - var controls = this.controls, │ │ │ │ - button = evt.buttonElement; │ │ │ │ - for (var i = controls.length - 1; i >= 0; --i) { │ │ │ │ - if (controls[i].panel_div === button) { │ │ │ │ - this.activateControl(controls[i]); │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getControlsBy: function(property, match) { │ │ │ │ - var test = typeof match.test == "function"; │ │ │ │ - var found = OpenLayers.Array.filter(this.controls, function(item) { │ │ │ │ - return item[property] == match || test && match.test(item[property]) │ │ │ │ - }); │ │ │ │ - return found │ │ │ │ - }, │ │ │ │ - getControlsByName: function(match) { │ │ │ │ - return this.getControlsBy("name", match) │ │ │ │ - }, │ │ │ │ - getControlsByClass: function(match) { │ │ │ │ - return this.getControlsBy("CLASS_NAME", match) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Panel" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - zoomInText: "+", │ │ │ │ - zoomInId: "olZoomInLink", │ │ │ │ - zoomOutText: "−", │ │ │ │ - zoomOutId: "olZoomOutLink", │ │ │ │ - draw: function() { │ │ │ │ - var div = OpenLayers.Control.prototype.draw.apply(this), │ │ │ │ - links = this.getOrCreateLinks(div), │ │ │ │ - zoomIn = links.zoomIn, │ │ │ │ - zoomOut = links.zoomOut, │ │ │ │ - eventsInstance = this.map.events; │ │ │ │ - if (zoomOut.parentNode !== div) { │ │ │ │ - eventsInstance = this.events; │ │ │ │ - eventsInstance.attachToElement(zoomOut.parentNode) │ │ │ │ - } │ │ │ │ - eventsInstance.register("buttonclick", this, this.onZoomClick); │ │ │ │ - this.zoomInLink = zoomIn; │ │ │ │ - this.zoomOutLink = zoomOut; │ │ │ │ - return div │ │ │ │ - }, │ │ │ │ - getOrCreateLinks: function(el) { │ │ │ │ - var zoomIn = document.getElementById(this.zoomInId), │ │ │ │ - zoomOut = document.getElementById(this.zoomOutId); │ │ │ │ - if (!zoomIn) { │ │ │ │ - zoomIn = document.createElement("a"); │ │ │ │ - zoomIn.href = "#zoomIn"; │ │ │ │ - zoomIn.appendChild(document.createTextNode(this.zoomInText)); │ │ │ │ - zoomIn.className = "olControlZoomIn"; │ │ │ │ - el.appendChild(zoomIn) │ │ │ │ - } │ │ │ │ - OpenLayers.Element.addClass(zoomIn, "olButton"); │ │ │ │ - if (!zoomOut) { │ │ │ │ - zoomOut = document.createElement("a"); │ │ │ │ - zoomOut.href = "#zoomOut"; │ │ │ │ - zoomOut.appendChild(document.createTextNode(this.zoomOutText)); │ │ │ │ - zoomOut.className = "olControlZoomOut"; │ │ │ │ - el.appendChild(zoomOut) │ │ │ │ - } │ │ │ │ - OpenLayers.Element.addClass(zoomOut, "olButton"); │ │ │ │ - return { │ │ │ │ - zoomIn: zoomIn, │ │ │ │ - zoomOut: zoomOut │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - onZoomClick: function(evt) { │ │ │ │ - var button = evt.buttonElement; │ │ │ │ - if (button === this.zoomInLink) { │ │ │ │ - this.map.zoomIn() │ │ │ │ - } else if (button === this.zoomOutLink) { │ │ │ │ - this.map.zoomOut() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.unregister("buttonclick", this, this.onZoomClick) │ │ │ │ - } │ │ │ │ - delete this.zoomInLink; │ │ │ │ - delete this.zoomOutLink; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Zoom" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - layerStates: null, │ │ │ │ - layersDiv: null, │ │ │ │ - baseLayersDiv: null, │ │ │ │ - baseLayers: null, │ │ │ │ - dataLbl: null, │ │ │ │ - dataLayersDiv: null, │ │ │ │ - dataLayers: null, │ │ │ │ - minimizeDiv: null, │ │ │ │ - maximizeDiv: null, │ │ │ │ - ascending: true, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ - this.layerStates = [] │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.clearLayersArray("base"); │ │ │ │ - this.clearLayersArray("data"); │ │ │ │ - this.map.events.un({ │ │ │ │ - buttonclick: this.onButtonClick, │ │ │ │ - addlayer: this.redraw, │ │ │ │ - changelayer: this.redraw, │ │ │ │ - removelayer: this.redraw, │ │ │ │ - changebaselayer: this.redraw, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - this.map.events.on({ │ │ │ │ - addlayer: this.redraw, │ │ │ │ - changelayer: this.redraw, │ │ │ │ - removelayer: this.redraw, │ │ │ │ - changebaselayer: this.redraw, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - if (this.outsideViewport) { │ │ │ │ - this.events.attachToElement(this.div); │ │ │ │ - this.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ - } else { │ │ │ │ - this.map.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this); │ │ │ │ - this.loadContents(); │ │ │ │ - if (!this.outsideViewport) { │ │ │ │ - this.minimizeControl() │ │ │ │ - } │ │ │ │ - this.redraw(); │ │ │ │ - return this.div │ │ │ │ - }, │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - var button = evt.buttonElement; │ │ │ │ - if (button === this.minimizeDiv) { │ │ │ │ - this.minimizeControl() │ │ │ │ - } else if (button === this.maximizeDiv) { │ │ │ │ - this.maximizeControl() │ │ │ │ - } else if (button._layerSwitcher === this.id) { │ │ │ │ - if (button["for"]) { │ │ │ │ - button = document.getElementById(button["for"]) │ │ │ │ - } │ │ │ │ - if (!button.disabled) { │ │ │ │ - if (button.type == "radio") { │ │ │ │ - button.checked = true; │ │ │ │ - this.map.setBaseLayer(this.map.getLayer(button._layer)) │ │ │ │ - } else { │ │ │ │ - button.checked = !button.checked; │ │ │ │ - this.updateMap() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - clearLayersArray: function(layersType) { │ │ │ │ - this[layersType + "LayersDiv"].innerHTML = ""; │ │ │ │ - this[layersType + "Layers"] = [] │ │ │ │ - }, │ │ │ │ - checkRedraw: function() { │ │ │ │ - if (!this.layerStates.length || this.map.layers.length != this.layerStates.length) { │ │ │ │ - return true │ │ │ │ - } │ │ │ │ - for (var i = 0, len = this.layerStates.length; i < len; i++) { │ │ │ │ - var layerState = this.layerStates[i]; │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - if (layerState.name != layer.name || layerState.inRange != layer.inRange || layerState.id != layer.id || layerState.visibility != layer.visibility) { │ │ │ │ - return true │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return false │ │ │ │ - }, │ │ │ │ - redraw: function() { │ │ │ │ - if (!this.checkRedraw()) { │ │ │ │ - return this.div │ │ │ │ - } │ │ │ │ - this.clearLayersArray("base"); │ │ │ │ - this.clearLayersArray("data"); │ │ │ │ - var containsOverlays = false; │ │ │ │ - var containsBaseLayers = false; │ │ │ │ - var len = this.map.layers.length; │ │ │ │ - this.layerStates = new Array(len); │ │ │ │ - for (var i = 0; i < len; i++) { │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - this.layerStates[i] = { │ │ │ │ - name: layer.name, │ │ │ │ - visibility: layer.visibility, │ │ │ │ - inRange: layer.inRange, │ │ │ │ - id: layer.id │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var layers = this.map.layers.slice(); │ │ │ │ - if (!this.ascending) { │ │ │ │ - layers.reverse() │ │ │ │ - } │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - var layer = layers[i]; │ │ │ │ - var baseLayer = layer.isBaseLayer; │ │ │ │ - if (layer.displayInLayerSwitcher) { │ │ │ │ - if (baseLayer) { │ │ │ │ - containsBaseLayers = true │ │ │ │ - } else { │ │ │ │ - containsOverlays = true │ │ │ │ - } │ │ │ │ - var checked = baseLayer ? layer == this.map.baseLayer : layer.getVisibility(); │ │ │ │ - var inputElem = document.createElement("input"), │ │ │ │ - inputId = OpenLayers.Util.createUniqueID(this.id + "_input_"); │ │ │ │ - inputElem.id = inputId; │ │ │ │ - inputElem.name = baseLayer ? this.id + "_baseLayers" : layer.name; │ │ │ │ - inputElem.type = baseLayer ? "radio" : "checkbox"; │ │ │ │ - inputElem.value = layer.name; │ │ │ │ - inputElem.checked = checked; │ │ │ │ - inputElem.defaultChecked = checked; │ │ │ │ - inputElem.className = "olButton"; │ │ │ │ - inputElem._layer = layer.id; │ │ │ │ - inputElem._layerSwitcher = this.id; │ │ │ │ - if (!baseLayer && !layer.inRange) { │ │ │ │ - inputElem.disabled = true │ │ │ │ - } │ │ │ │ - var labelSpan = document.createElement("label"); │ │ │ │ - labelSpan["for"] = inputElem.id; │ │ │ │ - OpenLayers.Element.addClass(labelSpan, "labelSpan olButton"); │ │ │ │ - labelSpan._layer = layer.id; │ │ │ │ - labelSpan._layerSwitcher = this.id; │ │ │ │ - if (!baseLayer && !layer.inRange) { │ │ │ │ - labelSpan.style.color = "gray" │ │ │ │ - } │ │ │ │ - labelSpan.innerHTML = layer.name; │ │ │ │ - labelSpan.style.verticalAlign = baseLayer ? "bottom" : "baseline"; │ │ │ │ - var br = document.createElement("br"); │ │ │ │ - var groupArray = baseLayer ? this.baseLayers : this.dataLayers; │ │ │ │ - groupArray.push({ │ │ │ │ - layer: layer, │ │ │ │ - inputElem: inputElem, │ │ │ │ - labelSpan: labelSpan │ │ │ │ - }); │ │ │ │ - var groupDiv = baseLayer ? this.baseLayersDiv : this.dataLayersDiv; │ │ │ │ - groupDiv.appendChild(inputElem); │ │ │ │ - groupDiv.appendChild(labelSpan); │ │ │ │ - groupDiv.appendChild(br) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.dataLbl.style.display = containsOverlays ? "" : "none"; │ │ │ │ - this.baseLbl.style.display = containsBaseLayers ? "" : "none"; │ │ │ │ - return this.div │ │ │ │ - }, │ │ │ │ - updateMap: function() { │ │ │ │ - for (var i = 0, len = this.baseLayers.length; i < len; i++) { │ │ │ │ - var layerEntry = this.baseLayers[i]; │ │ │ │ - if (layerEntry.inputElem.checked) { │ │ │ │ - this.map.setBaseLayer(layerEntry.layer, false) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - for (var i = 0, len = this.dataLayers.length; i < len; i++) { │ │ │ │ - var layerEntry = this.dataLayers[i]; │ │ │ │ - layerEntry.layer.setVisibility(layerEntry.inputElem.checked) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - maximizeControl: function(e) { │ │ │ │ - this.div.style.width = ""; │ │ │ │ - this.div.style.height = ""; │ │ │ │ - this.showControls(false); │ │ │ │ - if (e != null) { │ │ │ │ - OpenLayers.Event.stop(e) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - minimizeControl: function(e) { │ │ │ │ - this.div.style.width = "0px"; │ │ │ │ - this.div.style.height = "0px"; │ │ │ │ - this.showControls(true); │ │ │ │ - if (e != null) { │ │ │ │ - OpenLayers.Event.stop(e) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - showControls: function(minimize) { │ │ │ │ - this.maximizeDiv.style.display = minimize ? "" : "none"; │ │ │ │ - this.minimizeDiv.style.display = minimize ? "none" : ""; │ │ │ │ - this.layersDiv.style.display = minimize ? "none" : "" │ │ │ │ - }, │ │ │ │ - loadContents: function() { │ │ │ │ - this.layersDiv = document.createElement("div"); │ │ │ │ - this.layersDiv.id = this.id + "_layersDiv"; │ │ │ │ - OpenLayers.Element.addClass(this.layersDiv, "layersDiv"); │ │ │ │ - this.baseLbl = document.createElement("div"); │ │ │ │ - this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer"); │ │ │ │ - OpenLayers.Element.addClass(this.baseLbl, "baseLbl"); │ │ │ │ - this.baseLayersDiv = document.createElement("div"); │ │ │ │ - OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv"); │ │ │ │ - this.dataLbl = document.createElement("div"); │ │ │ │ - this.dataLbl.innerHTML = OpenLayers.i18n("Overlays"); │ │ │ │ - OpenLayers.Element.addClass(this.dataLbl, "dataLbl"); │ │ │ │ - this.dataLayersDiv = document.createElement("div"); │ │ │ │ - OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv"); │ │ │ │ - if (this.ascending) { │ │ │ │ - this.layersDiv.appendChild(this.baseLbl); │ │ │ │ - this.layersDiv.appendChild(this.baseLayersDiv); │ │ │ │ - this.layersDiv.appendChild(this.dataLbl); │ │ │ │ - this.layersDiv.appendChild(this.dataLayersDiv) │ │ │ │ - } else { │ │ │ │ - this.layersDiv.appendChild(this.dataLbl); │ │ │ │ - this.layersDiv.appendChild(this.dataLayersDiv); │ │ │ │ - this.layersDiv.appendChild(this.baseLbl); │ │ │ │ - this.layersDiv.appendChild(this.baseLayersDiv) │ │ │ │ - } │ │ │ │ - this.div.appendChild(this.layersDiv); │ │ │ │ - var img = OpenLayers.Util.getImageLocation("layer-switcher-maximize.png"); │ │ │ │ - this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MaximizeDiv", null, null, img, "absolute"); │ │ │ │ - OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton"); │ │ │ │ - this.maximizeDiv.style.display = "none"; │ │ │ │ - this.div.appendChild(this.maximizeDiv); │ │ │ │ - var img = OpenLayers.Util.getImageLocation("layer-switcher-minimize.png"); │ │ │ │ - this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MinimizeDiv", null, null, img, "absolute"); │ │ │ │ - OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton"); │ │ │ │ - this.minimizeDiv.style.display = "none"; │ │ │ │ - this.div.appendChild(this.minimizeDiv) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.LayerSwitcher" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Attribution = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - separator: ", ", │ │ │ │ - template: "${layers}", │ │ │ │ - destroy: function() { │ │ │ │ - this.map.events.un({ │ │ │ │ - removelayer: this.updateAttribution, │ │ │ │ - addlayer: this.updateAttribution, │ │ │ │ - changelayer: this.updateAttribution, │ │ │ │ - changebaselayer: this.updateAttribution, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - this.map.events.on({ │ │ │ │ - changebaselayer: this.updateAttribution, │ │ │ │ - changelayer: this.updateAttribution, │ │ │ │ - addlayer: this.updateAttribution, │ │ │ │ - removelayer: this.updateAttribution, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.updateAttribution(); │ │ │ │ - return this.div │ │ │ │ - }, │ │ │ │ - updateAttribution: function() { │ │ │ │ - var attributions = []; │ │ │ │ - if (this.map && this.map.layers) { │ │ │ │ - for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - if (layer.attribution && layer.getVisibility()) { │ │ │ │ - if (OpenLayers.Util.indexOf(attributions, layer.attribution) === -1) { │ │ │ │ - attributions.push(layer.attribution) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.div.innerHTML = OpenLayers.String.format(this.template, { │ │ │ │ - layers: attributions.join(this.separator) │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Attribution" │ │ │ │ -}); │ │ │ │ -OpenLayers.Handler = OpenLayers.Class({ │ │ │ │ - id: null, │ │ │ │ - control: null, │ │ │ │ - map: null, │ │ │ │ - keyMask: null, │ │ │ │ - active: false, │ │ │ │ - evt: null, │ │ │ │ - touch: false, │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.control = control; │ │ │ │ - this.callbacks = callbacks; │ │ │ │ - var map = this.map || control.map; │ │ │ │ - if (map) { │ │ │ │ - this.setMap(map) │ │ │ │ - } │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_") │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - this.map = map │ │ │ │ - }, │ │ │ │ - checkModifiers: function(evt) { │ │ │ │ - if (this.keyMask == null) { │ │ │ │ - return true │ │ │ │ - } │ │ │ │ - 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); │ │ │ │ - return keyModifiers == this.keyMask │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (!this.active) { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - 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]]) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - callback: function(name, args) { │ │ │ │ - if (name && this.callbacks[name]) { │ │ │ │ - this.callbacks[name].apply(this.control, args) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - register: function(name, method) { │ │ │ │ - this.map.events.registerPriority(name, this, method); │ │ │ │ - this.map.events.registerPriority(name, this, this.setEvent) │ │ │ │ - }, │ │ │ │ - unregister: function(name, method) { │ │ │ │ - this.map.events.unregister(name, this, method); │ │ │ │ - this.map.events.unregister(name, this, this.setEvent) │ │ │ │ - }, │ │ │ │ - setEvent: function(evt) { │ │ │ │ - this.evt = evt; │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - this.control = this.map = null │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Handler" │ │ │ │ -}); │ │ │ │ -OpenLayers.Handler.MOD_NONE = 0; │ │ │ │ -OpenLayers.Handler.MOD_SHIFT = 1; │ │ │ │ -OpenLayers.Handler.MOD_CTRL = 2; │ │ │ │ -OpenLayers.Handler.MOD_ALT = 4; │ │ │ │ -OpenLayers.Handler.MOD_META = 8; │ │ │ │ -OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - started: false, │ │ │ │ - stopDown: true, │ │ │ │ - dragging: false, │ │ │ │ - last: null, │ │ │ │ - start: null, │ │ │ │ - lastMoveEvt: null, │ │ │ │ - oldOnselectstart: null, │ │ │ │ - interval: 0, │ │ │ │ - timeoutId: null, │ │ │ │ - documentDrag: false, │ │ │ │ - documentEvents: null, │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - if (this.documentDrag === true) { │ │ │ │ - var me = this; │ │ │ │ - this._docMove = function(evt) { │ │ │ │ - me.mousemove({ │ │ │ │ - xy: { │ │ │ │ - x: evt.clientX, │ │ │ │ - y: evt.clientY │ │ │ │ - }, │ │ │ │ - element: document │ │ │ │ - }) │ │ │ │ - }; │ │ │ │ - this._docUp = function(evt) { │ │ │ │ - me.mouseup({ │ │ │ │ - xy: { │ │ │ │ - x: evt.clientX, │ │ │ │ - y: evt.clientY │ │ │ │ - } │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - dragstart: function(evt) { │ │ │ │ - var propagate = true; │ │ │ │ - this.dragging = false; │ │ │ │ - if (this.checkModifiers(evt) && (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt))) { │ │ │ │ - this.started = true; │ │ │ │ - this.start = evt.xy; │ │ │ │ - this.last = evt.xy; │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, "olDragDown"); │ │ │ │ - this.down(evt); │ │ │ │ - this.callback("down", [evt.xy]); │ │ │ │ - OpenLayers.Event.preventDefault(evt); │ │ │ │ - if (!this.oldOnselectstart) { │ │ │ │ - this.oldOnselectstart = document.onselectstart ? document.onselectstart : OpenLayers.Function.True │ │ │ │ - } │ │ │ │ - document.onselectstart = OpenLayers.Function.False; │ │ │ │ - propagate = !this.stopDown │ │ │ │ - } else { │ │ │ │ - this.started = false; │ │ │ │ - this.start = null; │ │ │ │ - this.last = null │ │ │ │ - } │ │ │ │ - return propagate │ │ │ │ - }, │ │ │ │ - dragmove: function(evt) { │ │ │ │ - this.lastMoveEvt = evt; │ │ │ │ - if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || evt.xy.y != this.last.y)) { │ │ │ │ - if (this.documentDrag === true && this.documentEvents) { │ │ │ │ - if (evt.element === document) { │ │ │ │ - this.adjustXY(evt); │ │ │ │ - this.setEvent(evt) │ │ │ │ - } else { │ │ │ │ - this.removeDocumentEvents() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.interval > 0) { │ │ │ │ - this.timeoutId = setTimeout(OpenLayers.Function.bind(this.removeTimeout, this), this.interval) │ │ │ │ - } │ │ │ │ - this.dragging = true; │ │ │ │ - this.move(evt); │ │ │ │ - this.callback("move", [evt.xy]); │ │ │ │ - if (!this.oldOnselectstart) { │ │ │ │ - this.oldOnselectstart = document.onselectstart; │ │ │ │ - document.onselectstart = OpenLayers.Function.False │ │ │ │ - } │ │ │ │ - this.last = evt.xy │ │ │ │ - } │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - dragend: function(evt) { │ │ │ │ - if (this.started) { │ │ │ │ - if (this.documentDrag === true && this.documentEvents) { │ │ │ │ - this.adjustXY(evt); │ │ │ │ - this.removeDocumentEvents() │ │ │ │ - } │ │ │ │ - var dragged = this.start != this.last; │ │ │ │ - this.started = false; │ │ │ │ - this.dragging = false; │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDragDown"); │ │ │ │ - this.up(evt); │ │ │ │ - this.callback("up", [evt.xy]); │ │ │ │ - if (dragged) { │ │ │ │ - this.callback("done", [evt.xy]) │ │ │ │ - } │ │ │ │ - document.onselectstart = this.oldOnselectstart │ │ │ │ - } │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - down: function(evt) {}, │ │ │ │ - move: function(evt) {}, │ │ │ │ - up: function(evt) {}, │ │ │ │ - out: function(evt) {}, │ │ │ │ - mousedown: function(evt) { │ │ │ │ - return this.dragstart(evt) │ │ │ │ - }, │ │ │ │ - touchstart: function(evt) { │ │ │ │ - this.startTouch(); │ │ │ │ - return this.dragstart(evt) │ │ │ │ - }, │ │ │ │ - mousemove: function(evt) { │ │ │ │ - return this.dragmove(evt) │ │ │ │ - }, │ │ │ │ - touchmove: function(evt) { │ │ │ │ - return this.dragmove(evt) │ │ │ │ - }, │ │ │ │ - removeTimeout: function() { │ │ │ │ - this.timeoutId = null; │ │ │ │ - if (this.dragging) { │ │ │ │ - this.mousemove(this.lastMoveEvt) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - mouseup: function(evt) { │ │ │ │ - return this.dragend(evt) │ │ │ │ - }, │ │ │ │ - touchend: function(evt) { │ │ │ │ - evt.xy = this.last; │ │ │ │ - return this.dragend(evt) │ │ │ │ - }, │ │ │ │ - mouseout: function(evt) { │ │ │ │ - if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) { │ │ │ │ - if (this.documentDrag === true) { │ │ │ │ - this.addDocumentEvents() │ │ │ │ - } else { │ │ │ │ - var dragged = this.start != this.last; │ │ │ │ - this.started = false; │ │ │ │ - this.dragging = false; │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDragDown"); │ │ │ │ - this.out(evt); │ │ │ │ - this.callback("out", []); │ │ │ │ - if (dragged) { │ │ │ │ - this.callback("done", [evt.xy]) │ │ │ │ - } │ │ │ │ - if (document.onselectstart) { │ │ │ │ - document.onselectstart = this.oldOnselectstart │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - click: function(evt) { │ │ │ │ - return this.start == this.last │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - var activated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.dragging = false; │ │ │ │ - activated = true │ │ │ │ - } │ │ │ │ - return activated │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.started = false; │ │ │ │ - this.dragging = false; │ │ │ │ - this.start = null; │ │ │ │ - this.last = null; │ │ │ │ - deactivated = true; │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDragDown") │ │ │ │ - } │ │ │ │ - return deactivated │ │ │ │ - }, │ │ │ │ - adjustXY: function(evt) { │ │ │ │ - var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv); │ │ │ │ - evt.xy.x -= pos[0]; │ │ │ │ - evt.xy.y -= pos[1] │ │ │ │ - }, │ │ │ │ - addDocumentEvents: function() { │ │ │ │ - OpenLayers.Element.addClass(document.body, "olDragDown"); │ │ │ │ - this.documentEvents = true; │ │ │ │ - OpenLayers.Event.observe(document, "mousemove", this._docMove); │ │ │ │ - OpenLayers.Event.observe(document, "mouseup", this._docUp) │ │ │ │ - }, │ │ │ │ - removeDocumentEvents: function() { │ │ │ │ - OpenLayers.Element.removeClass(document.body, "olDragDown"); │ │ │ │ - this.documentEvents = false; │ │ │ │ - OpenLayers.Event.stopObserving(document, "mousemove", this._docMove); │ │ │ │ - OpenLayers.Event.stopObserving(document, "mouseup", this._docUp) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Drag" │ │ │ │ -}); │ │ │ │ -OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - dragHandler: null, │ │ │ │ - boxDivClassName: "olHandlerBoxZoomBox", │ │ │ │ - boxOffsets: null, │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - this.dragHandler = new OpenLayers.Handler.Drag(this, { │ │ │ │ - down: this.startBox, │ │ │ │ - move: this.moveBox, │ │ │ │ - out: this.removeBox, │ │ │ │ - up: this.endBox │ │ │ │ - }, { │ │ │ │ - keyMask: this.keyMask │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ - if (this.dragHandler) { │ │ │ │ - this.dragHandler.destroy(); │ │ │ │ - this.dragHandler = null │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Handler.prototype.setMap.apply(this, arguments); │ │ │ │ - if (this.dragHandler) { │ │ │ │ - this.dragHandler.setMap(map) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - startBox: function(xy) { │ │ │ │ - this.callback("start", []); │ │ │ │ - this.zoomBox = OpenLayers.Util.createDiv("zoomBox", { │ │ │ │ - x: -9999, │ │ │ │ - y: -9999 │ │ │ │ - }); │ │ │ │ - this.zoomBox.className = this.boxDivClassName; │ │ │ │ - this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1; │ │ │ │ - this.map.viewPortDiv.appendChild(this.zoomBox); │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, "olDrawBox") │ │ │ │ - }, │ │ │ │ - moveBox: function(xy) { │ │ │ │ - var startX = this.dragHandler.start.x; │ │ │ │ - var startY = this.dragHandler.start.y; │ │ │ │ - var deltaX = Math.abs(startX - xy.x); │ │ │ │ - var deltaY = Math.abs(startY - xy.y); │ │ │ │ - var offset = this.getBoxOffsets(); │ │ │ │ - this.zoomBox.style.width = deltaX + offset.width + 1 + "px"; │ │ │ │ - this.zoomBox.style.height = deltaY + offset.height + 1 + "px"; │ │ │ │ - this.zoomBox.style.left = (xy.x < startX ? startX - deltaX - offset.left : startX - offset.left) + "px"; │ │ │ │ - this.zoomBox.style.top = (xy.y < startY ? startY - deltaY - offset.top : startY - offset.top) + "px" │ │ │ │ - }, │ │ │ │ - endBox: function(end) { │ │ │ │ - var result; │ │ │ │ - if (Math.abs(this.dragHandler.start.x - end.x) > 5 || Math.abs(this.dragHandler.start.y - end.y) > 5) { │ │ │ │ - var start = this.dragHandler.start; │ │ │ │ - var top = Math.min(start.y, end.y); │ │ │ │ - var bottom = Math.max(start.y, end.y); │ │ │ │ - var left = Math.min(start.x, end.x); │ │ │ │ - var right = Math.max(start.x, end.x); │ │ │ │ - result = new OpenLayers.Bounds(left, bottom, right, top) │ │ │ │ - } else { │ │ │ │ - result = this.dragHandler.start.clone() │ │ │ │ - } │ │ │ │ - this.removeBox(); │ │ │ │ - this.callback("done", [result]) │ │ │ │ - }, │ │ │ │ - removeBox: function() { │ │ │ │ - this.map.viewPortDiv.removeChild(this.zoomBox); │ │ │ │ - this.zoomBox = null; │ │ │ │ - this.boxOffsets = null; │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDrawBox") │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.dragHandler.activate(); │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - if (this.dragHandler.deactivate()) { │ │ │ │ - if (this.zoomBox) { │ │ │ │ - this.removeBox() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getBoxOffsets: function() { │ │ │ │ - if (!this.boxOffsets) { │ │ │ │ - var testDiv = document.createElement("div"); │ │ │ │ - testDiv.style.position = "absolute"; │ │ │ │ - testDiv.style.border = "1px solid black"; │ │ │ │ - testDiv.style.width = "3px"; │ │ │ │ - document.body.appendChild(testDiv); │ │ │ │ - var w3cBoxModel = testDiv.clientWidth == 3; │ │ │ │ - document.body.removeChild(testDiv); │ │ │ │ - var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-left-width")); │ │ │ │ - var right = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-right-width")); │ │ │ │ - var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-top-width")); │ │ │ │ - var bottom = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-bottom-width")); │ │ │ │ - this.boxOffsets = { │ │ │ │ - left: left, │ │ │ │ - right: right, │ │ │ │ - top: top, │ │ │ │ - bottom: bottom, │ │ │ │ - width: w3cBoxModel === false ? left + right : 0, │ │ │ │ - height: w3cBoxModel === false ? top + bottom : 0 │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return this.boxOffsets │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Box" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ - out: false, │ │ │ │ - keyMask: null, │ │ │ │ - alwaysZoom: false, │ │ │ │ - zoomOnClick: true, │ │ │ │ - draw: function() { │ │ │ │ - this.handler = new OpenLayers.Handler.Box(this, { │ │ │ │ - done: this.zoomBox │ │ │ │ - }, { │ │ │ │ - keyMask: this.keyMask │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - zoomBox: function(position) { │ │ │ │ - if (position instanceof OpenLayers.Bounds) { │ │ │ │ - var bounds, targetCenterPx = position.getCenterPixel(); │ │ │ │ - if (!this.out) { │ │ │ │ - var minXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.left, │ │ │ │ - y: position.bottom │ │ │ │ - }); │ │ │ │ - var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.right, │ │ │ │ - y: position.top │ │ │ │ - }); │ │ │ │ - bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat) │ │ │ │ - } else { │ │ │ │ - var pixWidth = position.right - position.left; │ │ │ │ - var pixHeight = position.bottom - position.top; │ │ │ │ - var zoomFactor = Math.min(this.map.size.h / pixHeight, this.map.size.w / pixWidth); │ │ │ │ - var extent = this.map.getExtent(); │ │ │ │ - var center = this.map.getLonLatFromPixel(targetCenterPx); │ │ │ │ - var xmin = center.lon - extent.getWidth() / 2 * zoomFactor; │ │ │ │ - var xmax = center.lon + extent.getWidth() / 2 * zoomFactor; │ │ │ │ - var ymin = center.lat - extent.getHeight() / 2 * zoomFactor; │ │ │ │ - var ymax = center.lat + extent.getHeight() / 2 * zoomFactor; │ │ │ │ - bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax) │ │ │ │ - } │ │ │ │ - var lastZoom = this.map.getZoom(), │ │ │ │ - size = this.map.getSize(), │ │ │ │ - centerPx = { │ │ │ │ - x: size.w / 2, │ │ │ │ - y: size.h / 2 │ │ │ │ - }, │ │ │ │ - zoom = this.map.getZoomForExtent(bounds), │ │ │ │ - oldRes = this.map.getResolution(), │ │ │ │ - newRes = this.map.getResolutionForZoom(zoom); │ │ │ │ - if (oldRes == newRes) { │ │ │ │ - this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx)) │ │ │ │ - } else { │ │ │ │ - var zoomOriginPx = { │ │ │ │ - x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / (oldRes - newRes), │ │ │ │ - y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / (oldRes - newRes) │ │ │ │ - }; │ │ │ │ - this.map.zoomTo(zoom, zoomOriginPx) │ │ │ │ - } │ │ │ │ - if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) { │ │ │ │ - this.map.zoomTo(lastZoom + (this.out ? -1 : 1)) │ │ │ │ - } │ │ │ │ - } else if (this.zoomOnClick) { │ │ │ │ - if (!this.out) { │ │ │ │ - this.map.zoomTo(this.map.getZoom() + 1, position) │ │ │ │ - } else { │ │ │ │ - this.map.zoomTo(this.map.getZoom() - 1, position) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ZoomBox" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ - panned: false, │ │ │ │ - interval: 0, │ │ │ │ - documentDrag: false, │ │ │ │ - kinetic: null, │ │ │ │ - enableKinetic: true, │ │ │ │ - kineticInterval: 10, │ │ │ │ - draw: function() { │ │ │ │ - if (this.enableKinetic && OpenLayers.Kinetic) { │ │ │ │ - var config = { │ │ │ │ - interval: this.kineticInterval │ │ │ │ - }; │ │ │ │ - if (typeof this.enableKinetic === "object") { │ │ │ │ - config = OpenLayers.Util.extend(config, this.enableKinetic) │ │ │ │ - } │ │ │ │ - this.kinetic = new OpenLayers.Kinetic(config) │ │ │ │ - } │ │ │ │ - this.handler = new OpenLayers.Handler.Drag(this, { │ │ │ │ - move: this.panMap, │ │ │ │ - done: this.panMapDone, │ │ │ │ - down: this.panMapStart │ │ │ │ - }, { │ │ │ │ - interval: this.interval, │ │ │ │ - documentDrag: this.documentDrag │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - panMapStart: function() { │ │ │ │ - if (this.kinetic) { │ │ │ │ - this.kinetic.begin() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - panMap: function(xy) { │ │ │ │ - if (this.kinetic) { │ │ │ │ - this.kinetic.update(xy) │ │ │ │ - } │ │ │ │ - this.panned = true; │ │ │ │ - this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, { │ │ │ │ - dragging: true, │ │ │ │ - animate: false │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - panMapDone: function(xy) { │ │ │ │ - if (this.panned) { │ │ │ │ - var res = null; │ │ │ │ - if (this.kinetic) { │ │ │ │ - res = this.kinetic.end(xy) │ │ │ │ - } │ │ │ │ - this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, { │ │ │ │ - dragging: !!res, │ │ │ │ - animate: false │ │ │ │ - }); │ │ │ │ - if (res) { │ │ │ │ - var self = this; │ │ │ │ - this.kinetic.move(res, function(x, y, end) { │ │ │ │ - self.map.pan(x, y, { │ │ │ │ - dragging: !end, │ │ │ │ - animate: false │ │ │ │ - }) │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - this.panned = false │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.DragPan" │ │ │ │ -}); │ │ │ │ -OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - wheelListener: null, │ │ │ │ - interval: 0, │ │ │ │ - maxDelta: Number.POSITIVE_INFINITY, │ │ │ │ - delta: 0, │ │ │ │ - cumulative: true, │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - this.wheelListener = OpenLayers.Function.bindAsEventListener(this.onWheelEvent, this) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ - this.wheelListener = null │ │ │ │ - }, │ │ │ │ - onWheelEvent: function(e) { │ │ │ │ - if (!this.map || !this.checkModifiers(e)) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var overScrollableDiv = false; │ │ │ │ - var allowScroll = false; │ │ │ │ - var overMapDiv = false; │ │ │ │ - var elem = OpenLayers.Event.element(e); │ │ │ │ - while (elem != null && !overMapDiv && !overScrollableDiv) { │ │ │ │ - if (!overScrollableDiv) { │ │ │ │ - try { │ │ │ │ - var overflow; │ │ │ │ - if (elem.currentStyle) { │ │ │ │ - overflow = elem.currentStyle["overflow"] │ │ │ │ - } else { │ │ │ │ - var style = document.defaultView.getComputedStyle(elem, null); │ │ │ │ - overflow = style.getPropertyValue("overflow") │ │ │ │ - } │ │ │ │ - overScrollableDiv = overflow && overflow == "auto" || overflow == "scroll" │ │ │ │ - } catch (err) {} │ │ │ │ - } │ │ │ │ - if (!allowScroll) { │ │ │ │ - allowScroll = OpenLayers.Element.hasClass(elem, "olScrollable"); │ │ │ │ - if (!allowScroll) { │ │ │ │ - for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - if (elem == layer.div || elem == layer.pane) { │ │ │ │ - allowScroll = true; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - overMapDiv = elem == this.map.div; │ │ │ │ - elem = elem.parentNode │ │ │ │ - } │ │ │ │ - if (!overScrollableDiv && overMapDiv) { │ │ │ │ - if (allowScroll) { │ │ │ │ - var delta = 0; │ │ │ │ - if (e.wheelDelta) { │ │ │ │ - delta = e.wheelDelta; │ │ │ │ - if (delta % 160 === 0) { │ │ │ │ - delta = delta * .75 │ │ │ │ - } │ │ │ │ - delta = delta / 120 │ │ │ │ - } else if (e.detail) { │ │ │ │ - delta = -(e.detail / Math.abs(e.detail)) │ │ │ │ - } │ │ │ │ - this.delta += delta; │ │ │ │ - window.clearTimeout(this._timeoutId); │ │ │ │ - if (this.interval && Math.abs(this.delta) < this.maxDelta) { │ │ │ │ - var evt = OpenLayers.Util.extend({}, e); │ │ │ │ - this._timeoutId = window.setTimeout(OpenLayers.Function.bind(function() { │ │ │ │ - this.wheelZoom(evt) │ │ │ │ - }, this), this.interval) │ │ │ │ - } else { │ │ │ │ - this.wheelZoom(e) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Event.stop(e) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - wheelZoom: function(e) { │ │ │ │ - var delta = this.delta; │ │ │ │ - this.delta = 0; │ │ │ │ - if (delta) { │ │ │ │ - e.xy = this.map.events.getMousePosition(e); │ │ │ │ - if (delta < 0) { │ │ │ │ - this.callback("down", [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]) │ │ │ │ - } else { │ │ │ │ - this.callback("up", [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - activate: function(evt) { │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - var wheelListener = this.wheelListener; │ │ │ │ - OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener); │ │ │ │ - OpenLayers.Event.observe(window, "mousewheel", wheelListener); │ │ │ │ - OpenLayers.Event.observe(document, "mousewheel", wheelListener); │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - deactivate: function(evt) { │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - var wheelListener = this.wheelListener; │ │ │ │ - OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener); │ │ │ │ - OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener); │ │ │ │ - OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener); │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.MouseWheel" │ │ │ │ -}); │ │ │ │ -OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - delay: 300, │ │ │ │ - single: true, │ │ │ │ - double: false, │ │ │ │ - pixelTolerance: 0, │ │ │ │ - dblclickTolerance: 13, │ │ │ │ - stopSingle: false, │ │ │ │ - stopDouble: false, │ │ │ │ - timerId: null, │ │ │ │ - down: null, │ │ │ │ - last: null, │ │ │ │ - first: null, │ │ │ │ - rightclickTimerId: null, │ │ │ │ - touchstart: function(evt) { │ │ │ │ - this.startTouch(); │ │ │ │ - this.down = this.getEventInfo(evt); │ │ │ │ - this.last = this.getEventInfo(evt); │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - touchmove: function(evt) { │ │ │ │ - this.last = this.getEventInfo(evt); │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - touchend: function(evt) { │ │ │ │ - if (this.down) { │ │ │ │ - evt.xy = this.last.xy; │ │ │ │ - evt.lastTouches = this.last.touches; │ │ │ │ - this.handleSingle(evt); │ │ │ │ - this.down = null │ │ │ │ - } │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - mousedown: function(evt) { │ │ │ │ - this.down = this.getEventInfo(evt); │ │ │ │ - this.last = this.getEventInfo(evt); │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - mouseup: function(evt) { │ │ │ │ - var propagate = true; │ │ │ │ - if (this.checkModifiers(evt) && this.control.handleRightClicks && OpenLayers.Event.isRightClick(evt)) { │ │ │ │ - propagate = this.rightclick(evt) │ │ │ │ - } │ │ │ │ - return propagate │ │ │ │ - }, │ │ │ │ - rightclick: function(evt) { │ │ │ │ - if (this.passesTolerance(evt)) { │ │ │ │ - if (this.rightclickTimerId != null) { │ │ │ │ - this.clearTimer(); │ │ │ │ - this.callback("dblrightclick", [evt]); │ │ │ │ - return !this.stopDouble │ │ │ │ - } else { │ │ │ │ - var clickEvent = this["double"] ? OpenLayers.Util.extend({}, evt) : this.callback("rightclick", [evt]); │ │ │ │ - var delayedRightCall = OpenLayers.Function.bind(this.delayedRightCall, this, clickEvent); │ │ │ │ - this.rightclickTimerId = window.setTimeout(delayedRightCall, this.delay) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return !this.stopSingle │ │ │ │ - }, │ │ │ │ - delayedRightCall: function(evt) { │ │ │ │ - this.rightclickTimerId = null; │ │ │ │ - if (evt) { │ │ │ │ - this.callback("rightclick", [evt]) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - click: function(evt) { │ │ │ │ - if (!this.last) { │ │ │ │ - this.last = this.getEventInfo(evt) │ │ │ │ - } │ │ │ │ - this.handleSingle(evt); │ │ │ │ - return !this.stopSingle │ │ │ │ - }, │ │ │ │ - dblclick: function(evt) { │ │ │ │ - this.handleDouble(evt); │ │ │ │ - return !this.stopDouble │ │ │ │ - }, │ │ │ │ - handleDouble: function(evt) { │ │ │ │ - if (this.passesDblclickTolerance(evt)) { │ │ │ │ - if (this["double"]) { │ │ │ │ - this.callback("dblclick", [evt]) │ │ │ │ - } │ │ │ │ - this.clearTimer() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - handleSingle: function(evt) { │ │ │ │ - if (this.passesTolerance(evt)) { │ │ │ │ - if (this.timerId != null) { │ │ │ │ - if (this.last.touches && this.last.touches.length === 1) { │ │ │ │ - if (this["double"]) { │ │ │ │ - OpenLayers.Event.preventDefault(evt) │ │ │ │ - } │ │ │ │ - this.handleDouble(evt) │ │ │ │ - } │ │ │ │ - if (!this.last.touches || this.last.touches.length !== 2) { │ │ │ │ - this.clearTimer() │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.first = this.getEventInfo(evt); │ │ │ │ - var clickEvent = this.single ? OpenLayers.Util.extend({}, evt) : null; │ │ │ │ - this.queuePotentialClick(clickEvent) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - queuePotentialClick: function(evt) { │ │ │ │ - this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay) │ │ │ │ - }, │ │ │ │ - passesTolerance: function(evt) { │ │ │ │ - var passes = true; │ │ │ │ - if (this.pixelTolerance != null && this.down && this.down.xy) { │ │ │ │ - passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy); │ │ │ │ - if (passes && this.touch && this.down.touches.length === this.last.touches.length) { │ │ │ │ - for (var i = 0, ii = this.down.touches.length; i < ii; ++i) { │ │ │ │ - if (this.getTouchDistance(this.down.touches[i], this.last.touches[i]) > this.pixelTolerance) { │ │ │ │ - passes = false; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return passes │ │ │ │ - }, │ │ │ │ - getTouchDistance: function(from, to) { │ │ │ │ - return Math.sqrt(Math.pow(from.clientX - to.clientX, 2) + Math.pow(from.clientY - to.clientY, 2)) │ │ │ │ - }, │ │ │ │ - passesDblclickTolerance: function(evt) { │ │ │ │ - var passes = true; │ │ │ │ - if (this.down && this.first) { │ │ │ │ - passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance │ │ │ │ - } │ │ │ │ - return passes │ │ │ │ - }, │ │ │ │ - clearTimer: function() { │ │ │ │ - if (this.timerId != null) { │ │ │ │ - window.clearTimeout(this.timerId); │ │ │ │ - this.timerId = null │ │ │ │ - } │ │ │ │ - if (this.rightclickTimerId != null) { │ │ │ │ - window.clearTimeout(this.rightclickTimerId); │ │ │ │ - this.rightclickTimerId = null │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - delayedCall: function(evt) { │ │ │ │ - this.timerId = null; │ │ │ │ - if (evt) { │ │ │ │ - this.callback("click", [evt]) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getEventInfo: function(evt) { │ │ │ │ - var touches; │ │ │ │ - if (evt.touches) { │ │ │ │ - var len = evt.touches.length; │ │ │ │ - touches = new Array(len); │ │ │ │ - var touch; │ │ │ │ - for (var i = 0; i < len; i++) { │ │ │ │ - touch = evt.touches[i]; │ │ │ │ - touches[i] = { │ │ │ │ - clientX: touch.olClientX, │ │ │ │ - clientY: touch.olClientY │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return { │ │ │ │ - xy: evt.xy, │ │ │ │ - touches: touches │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.clearTimer(); │ │ │ │ - this.down = null; │ │ │ │ - this.first = null; │ │ │ │ - this.last = null; │ │ │ │ - deactivated = true │ │ │ │ - } │ │ │ │ - return deactivated │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Click" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - dragPan: null, │ │ │ │ - dragPanOptions: null, │ │ │ │ - pinchZoom: null, │ │ │ │ - pinchZoomOptions: null, │ │ │ │ - documentDrag: false, │ │ │ │ - zoomBox: null, │ │ │ │ - zoomBoxEnabled: true, │ │ │ │ - zoomWheelEnabled: true, │ │ │ │ - mouseWheelOptions: null, │ │ │ │ - handleRightClicks: false, │ │ │ │ - zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT, │ │ │ │ - autoActivate: true, │ │ │ │ - initialize: function(options) { │ │ │ │ - this.handlers = {}; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - if (this.dragPan) { │ │ │ │ - this.dragPan.destroy() │ │ │ │ - } │ │ │ │ - this.dragPan = null; │ │ │ │ - if (this.zoomBox) { │ │ │ │ - this.zoomBox.destroy() │ │ │ │ - } │ │ │ │ - this.zoomBox = null; │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.destroy() │ │ │ │ - } │ │ │ │ - this.pinchZoom = null; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - this.dragPan.activate(); │ │ │ │ - if (this.zoomWheelEnabled) { │ │ │ │ - this.handlers.wheel.activate() │ │ │ │ - } │ │ │ │ - this.handlers.click.activate(); │ │ │ │ - if (this.zoomBoxEnabled) { │ │ │ │ - this.zoomBox.activate() │ │ │ │ - } │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.activate() │ │ │ │ - } │ │ │ │ - return OpenLayers.Control.prototype.activate.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.deactivate() │ │ │ │ - } │ │ │ │ - this.zoomBox.deactivate(); │ │ │ │ - this.dragPan.deactivate(); │ │ │ │ - this.handlers.click.deactivate(); │ │ │ │ - this.handlers.wheel.deactivate(); │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - draw: function() { │ │ │ │ - if (this.handleRightClicks) { │ │ │ │ - this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False │ │ │ │ - } │ │ │ │ - var clickCallbacks = { │ │ │ │ - click: this.defaultClick, │ │ │ │ - dblclick: this.defaultDblClick, │ │ │ │ - dblrightclick: this.defaultDblRightClick │ │ │ │ - }; │ │ │ │ - var clickOptions = { │ │ │ │ - double: true, │ │ │ │ - stopDouble: true │ │ │ │ - }; │ │ │ │ - this.handlers.click = new OpenLayers.Handler.Click(this, clickCallbacks, clickOptions); │ │ │ │ - this.dragPan = new OpenLayers.Control.DragPan(OpenLayers.Util.extend({ │ │ │ │ - map: this.map, │ │ │ │ - documentDrag: this.documentDrag │ │ │ │ - }, this.dragPanOptions)); │ │ │ │ - this.zoomBox = new OpenLayers.Control.ZoomBox({ │ │ │ │ - map: this.map, │ │ │ │ - keyMask: this.zoomBoxKeyMask │ │ │ │ - }); │ │ │ │ - this.dragPan.draw(); │ │ │ │ - this.zoomBox.draw(); │ │ │ │ - var wheelOptions = this.map.fractionalZoom ? {} : { │ │ │ │ - cumulative: false, │ │ │ │ - interval: 50, │ │ │ │ - maxDelta: 6 │ │ │ │ - }; │ │ │ │ - this.handlers.wheel = new OpenLayers.Handler.MouseWheel(this, { │ │ │ │ - up: this.wheelUp, │ │ │ │ - down: this.wheelDown │ │ │ │ - }, OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)); │ │ │ │ - if (OpenLayers.Control.PinchZoom) { │ │ │ │ - this.pinchZoom = new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({ │ │ │ │ - map: this.map │ │ │ │ - }, this.pinchZoomOptions)) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - defaultClick: function(evt) { │ │ │ │ - if (evt.lastTouches && evt.lastTouches.length == 2) { │ │ │ │ - this.map.zoomOut() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - defaultDblClick: function(evt) { │ │ │ │ - this.map.zoomTo(this.map.zoom + 1, evt.xy) │ │ │ │ - }, │ │ │ │ - defaultDblRightClick: function(evt) { │ │ │ │ - this.map.zoomTo(this.map.zoom - 1, evt.xy) │ │ │ │ - }, │ │ │ │ - wheelChange: function(evt, deltaZ) { │ │ │ │ - if (!this.map.fractionalZoom) { │ │ │ │ - deltaZ = Math.round(deltaZ) │ │ │ │ - } │ │ │ │ - var currentZoom = this.map.getZoom(), │ │ │ │ - newZoom = currentZoom + deltaZ; │ │ │ │ - newZoom = Math.max(newZoom, 0); │ │ │ │ - newZoom = Math.min(newZoom, this.map.getNumZoomLevels()); │ │ │ │ - if (newZoom === currentZoom) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - this.map.zoomTo(newZoom, evt.xy) │ │ │ │ - }, │ │ │ │ - wheelUp: function(evt, delta) { │ │ │ │ - this.wheelChange(evt, delta || 1) │ │ │ │ - }, │ │ │ │ - wheelDown: function(evt, delta) { │ │ │ │ - this.wheelChange(evt, delta || -1) │ │ │ │ - }, │ │ │ │ - disableZoomBox: function() { │ │ │ │ - this.zoomBoxEnabled = false; │ │ │ │ - this.zoomBox.deactivate() │ │ │ │ - }, │ │ │ │ - enableZoomBox: function() { │ │ │ │ - this.zoomBoxEnabled = true; │ │ │ │ - if (this.active) { │ │ │ │ - this.zoomBox.activate() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - disableZoomWheel: function() { │ │ │ │ - this.zoomWheelEnabled = false; │ │ │ │ - this.handlers.wheel.deactivate() │ │ │ │ - }, │ │ │ │ - enableZoomWheel: function() { │ │ │ │ - this.zoomWheelEnabled = true; │ │ │ │ - if (this.active) { │ │ │ │ - this.handlers.wheel.activate() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Navigation" │ │ │ │ -}); │ │ │ │ -OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - EVENTMAP: { │ │ │ │ - click: { │ │ │ │ - in: "click", │ │ │ │ - out: "clickout" │ │ │ │ - }, │ │ │ │ - mousemove: { │ │ │ │ - in: "over", │ │ │ │ - out: "out" │ │ │ │ - }, │ │ │ │ - dblclick: { │ │ │ │ - in: "dblclick", │ │ │ │ - out: null │ │ │ │ - }, │ │ │ │ - mousedown: { │ │ │ │ - in: null, │ │ │ │ - out: null │ │ │ │ - }, │ │ │ │ - mouseup: { │ │ │ │ - in: null, │ │ │ │ - out: null │ │ │ │ - }, │ │ │ │ - touchstart: { │ │ │ │ - in: "click", │ │ │ │ - out: "clickout" │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - feature: null, │ │ │ │ - lastFeature: null, │ │ │ │ - down: null, │ │ │ │ - up: null, │ │ │ │ - clickTolerance: 4, │ │ │ │ - geometryTypes: null, │ │ │ │ - stopClick: true, │ │ │ │ - stopDown: true, │ │ │ │ - stopUp: false, │ │ │ │ - initialize: function(control, layer, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]); │ │ │ │ - this.layer = layer │ │ │ │ - }, │ │ │ │ - touchstart: function(evt) { │ │ │ │ - this.startTouch(); │ │ │ │ - return OpenLayers.Event.isMultiTouch(evt) ? true : this.mousedown(evt) │ │ │ │ - }, │ │ │ │ - touchmove: function(evt) { │ │ │ │ - OpenLayers.Event.preventDefault(evt) │ │ │ │ - }, │ │ │ │ - mousedown: function(evt) { │ │ │ │ - if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) { │ │ │ │ - this.down = evt.xy │ │ │ │ - } │ │ │ │ - return this.handle(evt) ? !this.stopDown : true │ │ │ │ - }, │ │ │ │ - mouseup: function(evt) { │ │ │ │ - this.up = evt.xy; │ │ │ │ - return this.handle(evt) ? !this.stopUp : true │ │ │ │ - }, │ │ │ │ - click: function(evt) { │ │ │ │ - return this.handle(evt) ? !this.stopClick : true │ │ │ │ - }, │ │ │ │ - mousemove: function(evt) { │ │ │ │ - if (!this.callbacks["over"] && !this.callbacks["out"]) { │ │ │ │ - return true │ │ │ │ - } │ │ │ │ - this.handle(evt); │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - dblclick: function(evt) { │ │ │ │ - return !this.handle(evt) │ │ │ │ - }, │ │ │ │ - geometryTypeMatches: function(feature) { │ │ │ │ - return this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1 │ │ │ │ - }, │ │ │ │ - handle: function(evt) { │ │ │ │ - if (this.feature && !this.feature.layer) { │ │ │ │ - this.feature = null │ │ │ │ - } │ │ │ │ - var type = evt.type; │ │ │ │ - var handled = false; │ │ │ │ - var previouslyIn = !!this.feature; │ │ │ │ - var click = type == "click" || type == "dblclick" || type == "touchstart"; │ │ │ │ - this.feature = this.layer.getFeatureFromEvent(evt); │ │ │ │ - if (this.feature && !this.feature.layer) { │ │ │ │ - this.feature = null │ │ │ │ - } │ │ │ │ - if (this.lastFeature && !this.lastFeature.layer) { │ │ │ │ - this.lastFeature = null │ │ │ │ - } │ │ │ │ - if (this.feature) { │ │ │ │ - if (type === "touchstart") { │ │ │ │ - OpenLayers.Event.preventDefault(evt) │ │ │ │ - } │ │ │ │ - var inNew = this.feature != this.lastFeature; │ │ │ │ - if (this.geometryTypeMatches(this.feature)) { │ │ │ │ - if (previouslyIn && inNew) { │ │ │ │ - if (this.lastFeature) { │ │ │ │ - this.triggerCallback(type, "out", [this.lastFeature]) │ │ │ │ - } │ │ │ │ - this.triggerCallback(type, "in", [this.feature]) │ │ │ │ - } else if (!previouslyIn || click) { │ │ │ │ - this.triggerCallback(type, "in", [this.feature]) │ │ │ │ - } │ │ │ │ - this.lastFeature = this.feature; │ │ │ │ - handled = true │ │ │ │ - } else { │ │ │ │ - if (this.lastFeature && (previouslyIn && inNew || click)) { │ │ │ │ - this.triggerCallback(type, "out", [this.lastFeature]) │ │ │ │ - } │ │ │ │ - this.feature = null │ │ │ │ - } │ │ │ │ - } else if (this.lastFeature && (previouslyIn || click)) { │ │ │ │ - this.triggerCallback(type, "out", [this.lastFeature]) │ │ │ │ - } │ │ │ │ - return handled │ │ │ │ - }, │ │ │ │ - triggerCallback: function(type, mode, args) { │ │ │ │ - var key = this.EVENTMAP[type][mode]; │ │ │ │ - if (key) { │ │ │ │ - if (type == "click" && this.up && this.down) { │ │ │ │ - var dpx = Math.sqrt(Math.pow(this.up.x - this.down.x, 2) + Math.pow(this.up.y - this.down.y, 2)); │ │ │ │ - if (dpx <= this.clickTolerance) { │ │ │ │ - this.callback(key, args) │ │ │ │ - } │ │ │ │ - this.up = this.down = null │ │ │ │ - } else { │ │ │ │ - this.callback(key, args) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - var activated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.moveLayerToTop(); │ │ │ │ - this.map.events.on({ │ │ │ │ - removelayer: this.handleMapEvents, │ │ │ │ - changelayer: this.handleMapEvents, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - activated = true │ │ │ │ - } │ │ │ │ - return activated │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.moveLayerBack(); │ │ │ │ - this.feature = null; │ │ │ │ - this.lastFeature = null; │ │ │ │ - this.down = null; │ │ │ │ - this.up = null; │ │ │ │ - this.map.events.un({ │ │ │ │ - removelayer: this.handleMapEvents, │ │ │ │ - changelayer: this.handleMapEvents, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - deactivated = true │ │ │ │ - } │ │ │ │ - return deactivated │ │ │ │ - }, │ │ │ │ - handleMapEvents: function(evt) { │ │ │ │ - if (evt.type == "removelayer" || evt.property == "order") { │ │ │ │ - this.moveLayerToTop() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - moveLayerToTop: function() { │ │ │ │ - var index = Math.max(this.map.Z_INDEX_BASE["Feature"] - 1, this.layer.getZIndex()) + 1; │ │ │ │ - this.layer.setZIndex(index) │ │ │ │ - }, │ │ │ │ - moveLayerBack: function() { │ │ │ │ - var index = this.layer.getZIndex() - 1; │ │ │ │ - if (index >= this.map.Z_INDEX_BASE["Feature"]) { │ │ │ │ - this.layer.setZIndex(index) │ │ │ │ - } else { │ │ │ │ - this.map.setLayerZIndex(this.layer, this.map.getLayerIndex(this.layer)) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Feature" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ - displayInLayerSwitcher: false, │ │ │ │ - layers: null, │ │ │ │ - display: function() {}, │ │ │ │ - getFeatureFromEvent: function(evt) { │ │ │ │ - var layers = this.layers; │ │ │ │ - var feature; │ │ │ │ - for (var i = 0; i < layers.length; i++) { │ │ │ │ - feature = layers[i].getFeatureFromEvent(evt); │ │ │ │ - if (feature) { │ │ │ │ - return feature │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); │ │ │ │ - this.collectRoots(); │ │ │ │ - map.events.register("changelayer", this, this.handleChangeLayer) │ │ │ │ - }, │ │ │ │ - removeMap: function(map) { │ │ │ │ - map.events.unregister("changelayer", this, this.handleChangeLayer); │ │ │ │ - this.resetRoots(); │ │ │ │ - OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - collectRoots: function() { │ │ │ │ - var layer; │ │ │ │ - for (var i = 0; i < this.map.layers.length; ++i) { │ │ │ │ - layer = this.map.layers[i]; │ │ │ │ - if (OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ - layer.renderer.moveRoot(this.renderer) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - resetRoots: function() { │ │ │ │ - var layer; │ │ │ │ - for (var i = 0; i < this.layers.length; ++i) { │ │ │ │ - layer = this.layers[i]; │ │ │ │ - if (this.renderer && layer.renderer.getRenderLayerId() == this.id) { │ │ │ │ - this.renderer.moveRoot(layer.renderer) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - handleChangeLayer: function(evt) { │ │ │ │ - var layer = evt.layer; │ │ │ │ - if (evt.property == "order" && OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ - this.resetRoots(); │ │ │ │ - this.collectRoots() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - multipleKey: null, │ │ │ │ - toggleKey: null, │ │ │ │ - multiple: false, │ │ │ │ - clickout: true, │ │ │ │ - toggle: false, │ │ │ │ - hover: false, │ │ │ │ - highlightOnly: false, │ │ │ │ - box: false, │ │ │ │ - onBeforeSelect: function() {}, │ │ │ │ - onSelect: function() {}, │ │ │ │ - onUnselect: function() {}, │ │ │ │ - scope: null, │ │ │ │ - geometryTypes: null, │ │ │ │ - layer: null, │ │ │ │ - layers: null, │ │ │ │ - callbacks: null, │ │ │ │ - selectStyle: null, │ │ │ │ - renderIntent: "select", │ │ │ │ - handlers: null, │ │ │ │ - initialize: function(layers, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - if (this.scope === null) { │ │ │ │ - this.scope = this │ │ │ │ - } │ │ │ │ - this.initLayer(layers); │ │ │ │ - var callbacks = { │ │ │ │ - click: this.clickFeature, │ │ │ │ - clickout: this.clickoutFeature │ │ │ │ - }; │ │ │ │ - if (this.hover) { │ │ │ │ - callbacks.over = this.overFeature; │ │ │ │ - callbacks.out = this.outFeature │ │ │ │ - } │ │ │ │ - this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); │ │ │ │ - this.handlers = { │ │ │ │ - feature: new OpenLayers.Handler.Feature(this, this.layer, this.callbacks, { │ │ │ │ - geometryTypes: this.geometryTypes │ │ │ │ - }) │ │ │ │ - }; │ │ │ │ - if (this.box) { │ │ │ │ - this.handlers.box = new OpenLayers.Handler.Box(this, { │ │ │ │ - done: this.selectBox │ │ │ │ - }, { │ │ │ │ - boxDivClassName: "olHandlerBoxSelectFeature" │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - initLayer: function(layers) { │ │ │ │ - if (OpenLayers.Util.isArray(layers)) { │ │ │ │ - this.layers = layers; │ │ │ │ - this.layer = new OpenLayers.Layer.Vector.RootContainer(this.id + "_container", { │ │ │ │ - layers: layers │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - this.layer = layers │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.active && this.layers) { │ │ │ │ - this.map.removeLayer(this.layer) │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - if (this.layers) { │ │ │ │ - this.layer.destroy() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - if (!this.active) { │ │ │ │ - if (this.layers) { │ │ │ │ - this.map.addLayer(this.layer) │ │ │ │ - } │ │ │ │ - this.handlers.feature.activate(); │ │ │ │ - if (this.box && this.handlers.box) { │ │ │ │ - this.handlers.box.activate() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return OpenLayers.Control.prototype.activate.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.handlers.feature.deactivate(); │ │ │ │ - if (this.handlers.box) { │ │ │ │ - this.handlers.box.deactivate() │ │ │ │ - } │ │ │ │ - if (this.layers) { │ │ │ │ - this.map.removeLayer(this.layer) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - unselectAll: function(options) { │ │ │ │ - var layers = this.layers || [this.layer], │ │ │ │ - layer, feature, l, numExcept; │ │ │ │ - for (l = 0; l < layers.length; ++l) { │ │ │ │ - layer = layers[l]; │ │ │ │ - numExcept = 0; │ │ │ │ - if (layer.selectedFeatures != null) { │ │ │ │ - while (layer.selectedFeatures.length > numExcept) { │ │ │ │ - feature = layer.selectedFeatures[numExcept]; │ │ │ │ - if (!options || options.except != feature) { │ │ │ │ - this.unselect(feature) │ │ │ │ - } else { │ │ │ │ - ++numExcept │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - clickFeature: function(feature) { │ │ │ │ - if (!this.hover) { │ │ │ │ - var selected = OpenLayers.Util.indexOf(feature.layer.selectedFeatures, feature) > -1; │ │ │ │ - if (selected) { │ │ │ │ - if (this.toggleSelect()) { │ │ │ │ - this.unselect(feature) │ │ │ │ - } else if (!this.multipleSelect()) { │ │ │ │ - this.unselectAll({ │ │ │ │ - except: feature │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - if (!this.multipleSelect()) { │ │ │ │ - this.unselectAll({ │ │ │ │ - except: feature │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - this.select(feature) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - multipleSelect: function() { │ │ │ │ - return this.multiple || this.handlers.feature.evt && this.handlers.feature.evt[this.multipleKey] │ │ │ │ - }, │ │ │ │ - toggleSelect: function() { │ │ │ │ - return this.toggle || this.handlers.feature.evt && this.handlers.feature.evt[this.toggleKey] │ │ │ │ - }, │ │ │ │ - clickoutFeature: function(feature) { │ │ │ │ - if (!this.hover && this.clickout) { │ │ │ │ - this.unselectAll() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - overFeature: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - if (this.hover) { │ │ │ │ - if (this.highlightOnly) { │ │ │ │ - this.highlight(feature) │ │ │ │ - } else if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { │ │ │ │ - this.select(feature) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - outFeature: function(feature) { │ │ │ │ - if (this.hover) { │ │ │ │ - if (this.highlightOnly) { │ │ │ │ - if (feature._lastHighlighter == this.id) { │ │ │ │ - if (feature._prevHighlighter && feature._prevHighlighter != this.id) { │ │ │ │ - delete feature._lastHighlighter; │ │ │ │ - var control = this.map.getControl(feature._prevHighlighter); │ │ │ │ - if (control) { │ │ │ │ - control.highlight(feature) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.unhighlight(feature) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.unselect(feature) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - highlight: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - var cont = this.events.triggerEvent("beforefeaturehighlighted", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (cont !== false) { │ │ │ │ - feature._prevHighlighter = feature._lastHighlighter; │ │ │ │ - feature._lastHighlighter = this.id; │ │ │ │ - var style = this.selectStyle || this.renderIntent; │ │ │ │ - layer.drawFeature(feature, style); │ │ │ │ - this.events.triggerEvent("featurehighlighted", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - unhighlight: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - if (feature._prevHighlighter == undefined) { │ │ │ │ - delete feature._lastHighlighter │ │ │ │ - } else if (feature._prevHighlighter == this.id) { │ │ │ │ - delete feature._prevHighlighter │ │ │ │ - } else { │ │ │ │ - feature._lastHighlighter = feature._prevHighlighter; │ │ │ │ - delete feature._prevHighlighter │ │ │ │ - } │ │ │ │ - layer.drawFeature(feature, feature.style || feature.layer.style || "default"); │ │ │ │ - this.events.triggerEvent("featureunhighlighted", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - select: function(feature) { │ │ │ │ - var cont = this.onBeforeSelect.call(this.scope, feature); │ │ │ │ - var layer = feature.layer; │ │ │ │ - if (cont !== false) { │ │ │ │ - cont = layer.events.triggerEvent("beforefeatureselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (cont !== false) { │ │ │ │ - layer.selectedFeatures.push(feature); │ │ │ │ - this.highlight(feature); │ │ │ │ - if (!this.handlers.feature.lastFeature) { │ │ │ │ - this.handlers.feature.lastFeature = layer.selectedFeatures[0] │ │ │ │ - } │ │ │ │ - layer.events.triggerEvent("featureselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.onSelect.call(this.scope, feature) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - unselect: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - this.unhighlight(feature); │ │ │ │ - OpenLayers.Util.removeItem(layer.selectedFeatures, feature); │ │ │ │ - layer.events.triggerEvent("featureunselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.onUnselect.call(this.scope, feature) │ │ │ │ - }, │ │ │ │ - selectBox: function(position) { │ │ │ │ - if (position instanceof OpenLayers.Bounds) { │ │ │ │ - var minXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.left, │ │ │ │ - y: position.bottom │ │ │ │ - }); │ │ │ │ - var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.right, │ │ │ │ - y: position.top │ │ │ │ - }); │ │ │ │ - var bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat); │ │ │ │ - if (!this.multipleSelect()) { │ │ │ │ - this.unselectAll() │ │ │ │ - } │ │ │ │ - var prevMultiple = this.multiple; │ │ │ │ - this.multiple = true; │ │ │ │ - var layers = this.layers || [this.layer]; │ │ │ │ - this.events.triggerEvent("boxselectionstart", { │ │ │ │ - layers: layers │ │ │ │ - }); │ │ │ │ - var layer; │ │ │ │ - for (var l = 0; l < layers.length; ++l) { │ │ │ │ - layer = layers[l]; │ │ │ │ - for (var i = 0, len = layer.features.length; i < len; ++i) { │ │ │ │ - var feature = layer.features[i]; │ │ │ │ - if (!feature.getVisibility()) { │ │ │ │ - continue │ │ │ │ - } │ │ │ │ - if (this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1) { │ │ │ │ - if (bounds.toGeometry().intersects(feature.geometry)) { │ │ │ │ - if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { │ │ │ │ - this.select(feature) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.multiple = prevMultiple; │ │ │ │ - this.events.triggerEvent("boxselectionend", { │ │ │ │ - layers: layers │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - this.handlers.feature.setMap(map); │ │ │ │ - if (this.box) { │ │ │ │ - this.handlers.box.setMap(map) │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - setLayer: function(layers) { │ │ │ │ - var isActive = this.active; │ │ │ │ - this.unselectAll(); │ │ │ │ - this.deactivate(); │ │ │ │ - if (this.layers) { │ │ │ │ - this.layer.destroy(); │ │ │ │ - this.layers = null │ │ │ │ - } │ │ │ │ - this.initLayer(layers); │ │ │ │ - this.handlers.feature.layer = this.layer; │ │ │ │ - if (isActive) { │ │ │ │ - this.activate() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.SelectFeature" │ │ │ │ -}); │ │ │ │ -OpenLayers.Strategy = OpenLayers.Class({ │ │ │ │ - layer: null, │ │ │ │ - options: null, │ │ │ │ - active: null, │ │ │ │ - autoActivate: true, │ │ │ │ - autoDestroy: true, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.options = options; │ │ │ │ - this.active = false │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - this.layer = null; │ │ │ │ - this.options = null │ │ │ │ - }, │ │ │ │ - setLayer: function(layer) { │ │ │ │ - this.layer = layer │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - if (!this.active) { │ │ │ │ - this.active = true; │ │ │ │ - return true │ │ │ │ - } │ │ │ │ - return false │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.active = false; │ │ │ │ - return true │ │ │ │ - } │ │ │ │ - return false │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy" │ │ │ │ -}); │ │ │ │ -OpenLayers.Filter = OpenLayers.Class({ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options) │ │ │ │ - }, │ │ │ │ - destroy: function() {}, │ │ │ │ - evaluate: function(context) { │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - clone: function() { │ │ │ │ - return null │ │ │ │ - }, │ │ │ │ - toString: function() { │ │ │ │ - var string; │ │ │ │ - if (OpenLayers.Format && OpenLayers.Format.CQL) { │ │ │ │ - string = OpenLayers.Format.CQL.prototype.write(this) │ │ │ │ - } else { │ │ │ │ - string = Object.prototype.toString.call(this) │ │ │ │ - } │ │ │ │ - return string │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Filter" │ │ │ │ -}); │ │ │ │ -OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ - type: null, │ │ │ │ - property: null, │ │ │ │ - value: null, │ │ │ │ - distance: null, │ │ │ │ - distanceUnits: null, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - clone: function() { │ │ │ │ - var options = OpenLayers.Util.applyDefaults({ │ │ │ │ - value: this.value && this.value.clone && this.value.clone() │ │ │ │ - }, this); │ │ │ │ - return new OpenLayers.Filter.Spatial(options) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Filter.Spatial" │ │ │ │ -}); │ │ │ │ -OpenLayers.Filter.Spatial.BBOX = "BBOX"; │ │ │ │ -OpenLayers.Filter.Spatial.INTERSECTS = "INTERSECTS"; │ │ │ │ -OpenLayers.Filter.Spatial.DWITHIN = "DWITHIN"; │ │ │ │ -OpenLayers.Filter.Spatial.WITHIN = "WITHIN"; │ │ │ │ -OpenLayers.Filter.Spatial.CONTAINS = "CONTAINS"; │ │ │ │ -OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ - bounds: null, │ │ │ │ - resolution: null, │ │ │ │ - ratio: 2, │ │ │ │ - resFactor: null, │ │ │ │ - response: null, │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - this.layer.events.on({ │ │ │ │ - moveend: this.update, │ │ │ │ - refresh: this.update, │ │ │ │ - visibilitychanged: this.update, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.update() │ │ │ │ - } │ │ │ │ - return activated │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - this.layer.events.un({ │ │ │ │ - moveend: this.update, │ │ │ │ - refresh: this.update, │ │ │ │ - visibilitychanged: this.update, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - return deactivated │ │ │ │ - }, │ │ │ │ - update: function(options) { │ │ │ │ - var mapBounds = this.getMapBounds(); │ │ │ │ - if (mapBounds !== null && (options && options.force || this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds))) { │ │ │ │ - this.calculateBounds(mapBounds); │ │ │ │ - this.resolution = this.layer.map.getResolution(); │ │ │ │ - this.triggerRead(options) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getMapBounds: function() { │ │ │ │ - if (this.layer.map === null) { │ │ │ │ - return null │ │ │ │ - } │ │ │ │ - var bounds = this.layer.map.getExtent(); │ │ │ │ - if (bounds && !this.layer.projection.equals(this.layer.map.getProjectionObject())) { │ │ │ │ - bounds = bounds.clone().transform(this.layer.map.getProjectionObject(), this.layer.projection) │ │ │ │ - } │ │ │ │ - return bounds │ │ │ │ - }, │ │ │ │ - invalidBounds: function(mapBounds) { │ │ │ │ - if (!mapBounds) { │ │ │ │ - mapBounds = this.getMapBounds() │ │ │ │ - } │ │ │ │ - var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds); │ │ │ │ - if (!invalid && this.resFactor) { │ │ │ │ - var ratio = this.resolution / this.layer.map.getResolution(); │ │ │ │ - invalid = ratio >= this.resFactor || ratio <= 1 / this.resFactor │ │ │ │ - } │ │ │ │ - return invalid │ │ │ │ - }, │ │ │ │ - calculateBounds: function(mapBounds) { │ │ │ │ - if (!mapBounds) { │ │ │ │ - mapBounds = this.getMapBounds() │ │ │ │ - } │ │ │ │ - var center = mapBounds.getCenterLonLat(); │ │ │ │ - var dataWidth = mapBounds.getWidth() * this.ratio; │ │ │ │ - var dataHeight = mapBounds.getHeight() * this.ratio; │ │ │ │ - this.bounds = new OpenLayers.Bounds(center.lon - dataWidth / 2, center.lat - dataHeight / 2, center.lon + dataWidth / 2, center.lat + dataHeight / 2) │ │ │ │ - }, │ │ │ │ - triggerRead: function(options) { │ │ │ │ - if (this.response && !(options && options.noAbort === true)) { │ │ │ │ - this.layer.protocol.abort(this.response); │ │ │ │ - this.layer.events.triggerEvent("loadend") │ │ │ │ - } │ │ │ │ - var evt = { │ │ │ │ - filter: this.createFilter() │ │ │ │ - }; │ │ │ │ - this.layer.events.triggerEvent("loadstart", evt); │ │ │ │ - this.response = this.layer.protocol.read(OpenLayers.Util.applyDefaults({ │ │ │ │ - filter: evt.filter, │ │ │ │ - callback: this.merge, │ │ │ │ - scope: this │ │ │ │ - }, options)) │ │ │ │ - }, │ │ │ │ - createFilter: function() { │ │ │ │ - var filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ - value: this.bounds, │ │ │ │ - projection: this.layer.projection │ │ │ │ - }); │ │ │ │ - if (this.layer.filter) { │ │ │ │ - filter = new OpenLayers.Filter.Logical({ │ │ │ │ - type: OpenLayers.Filter.Logical.AND, │ │ │ │ - filters: [this.layer.filter, filter] │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - return filter │ │ │ │ - }, │ │ │ │ - merge: function(resp) { │ │ │ │ - this.layer.destroyFeatures(); │ │ │ │ - if (resp.success()) { │ │ │ │ - var features = resp.features; │ │ │ │ - if (features && features.length > 0) { │ │ │ │ - var remote = this.layer.projection; │ │ │ │ - var local = this.layer.map.getProjectionObject(); │ │ │ │ - if (!local.equals(remote)) { │ │ │ │ - var geom; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - geom = features[i].geometry; │ │ │ │ - if (geom) { │ │ │ │ - geom.transform(remote, local) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.layer.addFeatures(features) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.bounds = null │ │ │ │ - } │ │ │ │ - this.response = null; │ │ │ │ - this.layer.events.triggerEvent("loadend", { │ │ │ │ - response: resp │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy.BBOX" │ │ │ │ -}); │ │ │ │ -OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ - preload: false, │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); │ │ │ │ - if (activated) { │ │ │ │ - this.layer.events.on({ │ │ │ │ - refresh: this.load, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - if (this.layer.visibility == true || this.preload) { │ │ │ │ - this.load() │ │ │ │ - } else { │ │ │ │ - this.layer.events.on({ │ │ │ │ - visibilitychanged: this.load, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return activated │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - this.layer.events.un({ │ │ │ │ - refresh: this.load, │ │ │ │ - visibilitychanged: this.load, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - return deactivated │ │ │ │ - }, │ │ │ │ - load: function(options) { │ │ │ │ - var layer = this.layer; │ │ │ │ - layer.events.triggerEvent("loadstart", { │ │ │ │ - filter: layer.filter │ │ │ │ - }); │ │ │ │ - layer.protocol.read(OpenLayers.Util.applyDefaults({ │ │ │ │ - callback: this.merge, │ │ │ │ - filter: layer.filter, │ │ │ │ - scope: this │ │ │ │ - }, options)); │ │ │ │ - layer.events.un({ │ │ │ │ - visibilitychanged: this.load, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - merge: function(resp) { │ │ │ │ - var layer = this.layer; │ │ │ │ - layer.destroyFeatures(); │ │ │ │ - var features = resp.features; │ │ │ │ - if (features && features.length > 0) { │ │ │ │ - var remote = layer.projection; │ │ │ │ - var local = layer.map.getProjectionObject(); │ │ │ │ - if (!local.equals(remote)) { │ │ │ │ - var geom; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - geom = features[i].geometry; │ │ │ │ - if (geom) { │ │ │ │ - geom.transform(remote, local) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - layer.addFeatures(features) │ │ │ │ - } │ │ │ │ - layer.events.triggerEvent("loadend", { │ │ │ │ - response: resp │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy.Fixed" │ │ │ │ -}); │ │ │ │ -OpenLayers.ElementsIndexer = OpenLayers.Class({ │ │ │ │ - maxZIndex: null, │ │ │ │ - order: null, │ │ │ │ - indices: null, │ │ │ │ - compare: null, │ │ │ │ - initialize: function(yOrdering) { │ │ │ │ - this.compare = yOrdering ? OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER; │ │ │ │ - this.clear() │ │ │ │ - }, │ │ │ │ - insert: function(newNode) { │ │ │ │ - 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); │ │ │ │ - return this.getNextElement(rightIndex) │ │ │ │ - }, │ │ │ │ - remove: function(node) { │ │ │ │ - var nodeId = node.id; │ │ │ │ - var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId); │ │ │ │ - if (arrayIndex >= 0) { │ │ │ │ - this.order.splice(arrayIndex, 1); │ │ │ │ - delete this.indices[nodeId]; │ │ │ │ - if (this.order.length > 0) { │ │ │ │ - var lastId = this.order[this.order.length - 1]; │ │ │ │ - this.maxZIndex = this.indices[lastId] │ │ │ │ - } else { │ │ │ │ - this.maxZIndex = 0 │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - clear: function() { │ │ │ │ - this.order = []; │ │ │ │ - this.indices = {}; │ │ │ │ - this.maxZIndex = 0 │ │ │ │ - }, │ │ │ │ - exists: function(node) { │ │ │ │ - return this.indices[node.id] != null │ │ │ │ - }, │ │ │ │ - getZIndex: function(node) { │ │ │ │ - return node._style.graphicZIndex │ │ │ │ - }, │ │ │ │ - determineZIndex: function(node) { │ │ │ │ - var zIndex = node._style.graphicZIndex; │ │ │ │ - if (zIndex == null) { │ │ │ │ - zIndex = this.maxZIndex; │ │ │ │ - node._style.graphicZIndex = zIndex │ │ │ │ - } else if (zIndex > this.maxZIndex) { │ │ │ │ - this.maxZIndex = zIndex │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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" │ │ │ │ -}); │ │ │ │ -OpenLayers.ElementsIndexer.IndexingMethods = { │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ - var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, nextNode); │ │ │ │ - if (nextNode && returnVal == 0) { │ │ │ │ - returnVal = 1 │ │ │ │ - } │ │ │ │ - return returnVal │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ -}; │ │ │ │ -OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, { │ │ │ │ - rendererRoot: null, │ │ │ │ - root: null, │ │ │ │ - vectorRoot: null, │ │ │ │ - textRoot: null, │ │ │ │ - xmlns: null, │ │ │ │ - xOffset: 0, │ │ │ │ - indexer: null, │ │ │ │ - BACKGROUND_ID_SUFFIX: "_background", │ │ │ │ - LABEL_ID_SUFFIX: "_label", │ │ │ │ - LABEL_OUTLINE_SUFFIX: "_outline", │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.clear(); │ │ │ │ - this.rendererRoot = null; │ │ │ │ - this.root = null; │ │ │ │ - this.xmlns = null; │ │ │ │ - OpenLayers.Renderer.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - 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() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - setExtent: function(extent, resolutionChanged) { │ │ │ │ - var coordSysUnchanged = OpenLayers.Renderer.prototype.setExtent.apply(this, arguments); │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) { │ │ │ │ - var rightOfDateLine, ratio = extent.getWidth() / this.map.getExtent().getWidth(), │ │ │ │ - extent = extent.scale(1 / ratio), │ │ │ │ - world = this.map.getMaxExtent(); │ │ │ │ - if (world.right > extent.left && world.right < extent.right) { │ │ │ │ - rightOfDateLine = true │ │ │ │ - } else if (world.left > extent.left && world.left < extent.right) { │ │ │ │ - rightOfDateLine = false │ │ │ │ - } │ │ │ │ - if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) { │ │ │ │ - coordSysUnchanged = false; │ │ │ │ - this.xOffset = rightOfDateLine === true ? world.getWidth() / resolution : 0 │ │ │ │ - } │ │ │ │ - this.rightOfDateLine = rightOfDateLine │ │ │ │ - } │ │ │ │ - return coordSysUnchanged │ │ │ │ - }, │ │ │ │ - getNodeType: function(geometry, style) {}, │ │ │ │ - drawGeometry: function(geometry, style, featureId) { │ │ │ │ - var className = geometry.CLASS_NAME; │ │ │ │ - var rendered = true; │ │ │ │ - if (className == "OpenLayers.Geometry.Collection" || className == "OpenLayers.Geometry.MultiPoint" || className == "OpenLayers.Geometry.MultiLineString" || className == "OpenLayers.Geometry.MultiPolygon") { │ │ │ │ - for (var i = 0, len = geometry.components.length; i < len; i++) { │ │ │ │ - rendered = this.drawGeometry(geometry.components[i], style, featureId) && rendered │ │ │ │ - } │ │ │ │ - return rendered │ │ │ │ - } │ │ │ │ - rendered = false; │ │ │ │ - var removeBackground = false; │ │ │ │ - if (style.display != "none") { │ │ │ │ - if (style.backgroundGraphic) { │ │ │ │ - this.redrawBackgroundNode(geometry.id, geometry, style, featureId) │ │ │ │ - } else { │ │ │ │ - removeBackground = true │ │ │ │ - } │ │ │ │ - rendered = this.redrawNode(geometry.id, geometry, style, featureId) │ │ │ │ - } │ │ │ │ - if (rendered == false) { │ │ │ │ - var node = document.getElementById(geometry.id); │ │ │ │ - if (node) { │ │ │ │ - if (node._style.backgroundGraphic) { │ │ │ │ - removeBackground = true │ │ │ │ - } │ │ │ │ - node.parentNode.removeChild(node) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (removeBackground) { │ │ │ │ - var node = document.getElementById(geometry.id + this.BACKGROUND_ID_SUFFIX); │ │ │ │ - if (node) { │ │ │ │ - node.parentNode.removeChild(node) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return rendered │ │ │ │ - }, │ │ │ │ - redrawNode: function(id, geometry, style, featureId) { │ │ │ │ - style = this.applyDefaultSymbolizer(style); │ │ │ │ - var node = this.nodeFactory(id, this.getNodeType(geometry, style)); │ │ │ │ - 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; │ │ │ │ - if (this.indexer) { │ │ │ │ - var insert = this.indexer.insert(node); │ │ │ │ - if (insert) { │ │ │ │ - this.vectorRoot.insertBefore(node, insert) │ │ │ │ - } else { │ │ │ │ - this.vectorRoot.appendChild(node) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - if (node.parentNode !== this.vectorRoot) { │ │ │ │ - this.vectorRoot.appendChild(node) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.postDraw(node); │ │ │ │ - return drawResult.complete │ │ │ │ - }, │ │ │ │ - redrawBackgroundNode: function(id, geometry, style, featureId) { │ │ │ │ - var backgroundStyle = OpenLayers.Util.extend({}, style); │ │ │ │ - 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; │ │ │ │ - backgroundStyle.backgroundGraphic = null; │ │ │ │ - backgroundStyle.backgroundXOffset = null; │ │ │ │ - backgroundStyle.backgroundYOffset = null; │ │ │ │ - backgroundStyle.backgroundGraphicZIndex = null; │ │ │ │ - return this.redrawNode(id + this.BACKGROUND_ID_SUFFIX, geometry, backgroundStyle, null) │ │ │ │ - }, │ │ │ │ - drawGeometryNode: function(node, geometry, style) { │ │ │ │ - style = style || node._style; │ │ │ │ - var options = { │ │ │ │ - isFilled: style.fill === undefined ? true : style.fill, │ │ │ │ - isStroked: style.stroke === undefined ? !!style.strokeWidth : style.stroke │ │ │ │ - }; │ │ │ │ - var drawn; │ │ │ │ - switch (geometry.CLASS_NAME) { │ │ │ │ - case "OpenLayers.Geometry.Point": │ │ │ │ - if (style.graphic === false) { │ │ │ │ - options.isFilled = false; │ │ │ │ - options.isStroked = false │ │ │ │ - } │ │ │ │ - drawn = this.drawPoint(node, geometry); │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.LineString": │ │ │ │ - options.isFilled = false; │ │ │ │ - drawn = this.drawLineString(node, geometry); │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.LinearRing": │ │ │ │ - drawn = this.drawLinearRing(node, geometry); │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.Polygon": │ │ │ │ - drawn = this.drawPolygon(node, geometry); │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.Rectangle": │ │ │ │ - drawn = this.drawRectangle(node, geometry); │ │ │ │ - break; │ │ │ │ - default: │ │ │ │ - break │ │ │ │ - } │ │ │ │ - node._options = options; │ │ │ │ - if (drawn != false) { │ │ │ │ - return { │ │ │ │ - node: this.setStyle(node, style, options, geometry), │ │ │ │ - complete: drawn │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - postDraw: function(node) {}, │ │ │ │ - drawPoint: function(node, geometry) {}, │ │ │ │ - drawLineString: function(node, geometry) {}, │ │ │ │ - drawLinearRing: function(node, geometry) {}, │ │ │ │ - drawPolygon: function(node, geometry) {}, │ │ │ │ - drawRectangle: function(node, geometry) {}, │ │ │ │ - drawCircle: function(node, geometry) {}, │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getFeatureIdFromEvent: function(evt) { │ │ │ │ - var target = evt.target; │ │ │ │ - var useElement = target && target.correspondingUseElement; │ │ │ │ - var node = useElement ? useElement : target || evt.srcElement; │ │ │ │ - return node._featureId │ │ │ │ - }, │ │ │ │ - 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) { │ │ │ │ - bElem.parentNode.removeChild(bElem) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - nodeFactory: function(id, type) { │ │ │ │ - var node = OpenLayers.Util.getElement(id); │ │ │ │ - if (node) { │ │ │ │ - if (!this.nodeTypeCompare(node, type)) { │ │ │ │ - node.parentNode.removeChild(node); │ │ │ │ - node = this.nodeFactory(id, type) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - node = this.createNode(type, id) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - nodeTypeCompare: function(node, type) {}, │ │ │ │ - createNode: function(type, id) {}, │ │ │ │ - moveRoot: function(renderer) { │ │ │ │ - var root = this.root; │ │ │ │ - if (renderer.root.parentNode == this.rendererRoot) { │ │ │ │ - root = renderer.root │ │ │ │ - } │ │ │ │ - root.parentNode.removeChild(root); │ │ │ │ - renderer.rendererRoot.appendChild(root) │ │ │ │ - }, │ │ │ │ - getRenderLayerId: function() { │ │ │ │ - return this.root.parentNode.parentNode.id │ │ │ │ - }, │ │ │ │ - isComplexSymbol: function(graphicName) { │ │ │ │ - return graphicName != "circle" && !!graphicName │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer.Elements" │ │ │ │ -}); │ │ │ │ -OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ - xmlns: "http://www.w3.org/2000/svg", │ │ │ │ - xlinkns: "http://www.w3.org/1999/xlink", │ │ │ │ - MAX_PIXEL: 15e3, │ │ │ │ - translationParameters: null, │ │ │ │ - symbolMetrics: null, │ │ │ │ - initialize: function(containerID) { │ │ │ │ - if (!this.supported()) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments); │ │ │ │ - this.translationParameters = { │ │ │ │ - x: 0, │ │ │ │ - y: 0 │ │ │ │ - }; │ │ │ │ - this.symbolMetrics = {} │ │ │ │ - }, │ │ │ │ - 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")) │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - 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 (resolutionChanged) { │ │ │ │ - this.left = left; │ │ │ │ - this.top = top; │ │ │ │ - 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) { │ │ │ │ - this.setExtent(extent, true) │ │ │ │ - } │ │ │ │ - return coordSysUnchanged && inRange │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - getNodeType: function(geometry, style) { │ │ │ │ - var nodeType = null; │ │ │ │ - switch (geometry.CLASS_NAME) { │ │ │ │ - case "OpenLayers.Geometry.Point": │ │ │ │ - if (style.externalGraphic) { │ │ │ │ - nodeType = "image" │ │ │ │ - } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ - nodeType = "svg" │ │ │ │ - } else { │ │ │ │ - nodeType = "circle" │ │ │ │ - } │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.Rectangle": │ │ │ │ - nodeType = "rect"; │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.LineString": │ │ │ │ - nodeType = "polyline"; │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.LinearRing": │ │ │ │ - nodeType = "polygon"; │ │ │ │ - break; │ │ │ │ - case "OpenLayers.Geometry.Polygon": │ │ │ │ - case "OpenLayers.Geometry.Curve": │ │ │ │ - nodeType = "path"; │ │ │ │ - break; │ │ │ │ - default: │ │ │ │ - break │ │ │ │ - } │ │ │ │ - return nodeType │ │ │ │ - }, │ │ │ │ - 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); │ │ │ │ - var titleNode = node.getElementsByTagName("title"); │ │ │ │ - if (titleNode.length > 0) { │ │ │ │ - titleNode[0].firstChild.textContent = title │ │ │ │ - } else { │ │ │ │ - var label = this.nodeFactory(null, "title"); │ │ │ │ - label.textContent = title; │ │ │ │ - node.appendChild(label) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var r = parseFloat(node.getAttributeNS(null, "r")); │ │ │ │ - var widthFactor = 1; │ │ │ │ - var pos; │ │ │ │ - if (node._geometryClass == "OpenLayers.Geometry.Point" && r) { │ │ │ │ - node.style.visibility = ""; │ │ │ │ - if (style.graphic === false) { │ │ │ │ - node.style.visibility = "hidden" │ │ │ │ - } else if (style.externalGraphic) { │ │ │ │ - pos = this.getPosition(node); │ │ │ │ - if (style.graphicWidth && style.graphicHeight) { │ │ │ │ - node.setAttributeNS(null, "preserveAspectRatio", "none") │ │ │ │ - } │ │ │ │ - var width = style.graphicWidth || style.graphicHeight; │ │ │ │ - var height = style.graphicHeight || style.graphicWidth; │ │ │ │ - width = width ? width : style.pointRadius * 2; │ │ │ │ - height = height ? height : style.pointRadius * 2; │ │ │ │ - var xOffset = style.graphicXOffset != undefined ? style.graphicXOffset : -(.5 * width); │ │ │ │ - var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.5 * height); │ │ │ │ - var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ - node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed()); │ │ │ │ - node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed()); │ │ │ │ - node.setAttributeNS(null, "width", width); │ │ │ │ - node.setAttributeNS(null, "height", height); │ │ │ │ - node.setAttributeNS(this.xlinkns, "xlink:href", style.externalGraphic); │ │ │ │ - node.setAttributeNS(null, "style", "opacity: " + opacity); │ │ │ │ - node.onclick = OpenLayers.Event.preventDefault │ │ │ │ - } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ - 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; │ │ │ │ - var parent = node.parentNode; │ │ │ │ - var nextSibling = node.nextSibling; │ │ │ │ - if (parent) { │ │ │ │ - parent.removeChild(node) │ │ │ │ - } │ │ │ │ - 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); │ │ │ │ - if (nextSibling) { │ │ │ │ - parent.insertBefore(node, nextSibling) │ │ │ │ - } else if (parent) { │ │ │ │ - parent.appendChild(node) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - node.setAttributeNS(null, "r", style.pointRadius) │ │ │ │ + node.setAttributeNS(null, "r", style.pointRadius) │ │ │ │ } │ │ │ │ var rotation = style.rotation; │ │ │ │ if ((rotation !== undefined || node._rotation !== undefined) && pos) { │ │ │ │ node._rotation = rotation; │ │ │ │ rotation |= 0; │ │ │ │ if (node.nodeName !== "svg") { │ │ │ │ node.setAttributeNS(null, "transform", "rotate(" + rotation + " " + pos.x + " " + pos.y + ")") │ │ │ │ @@ -10860,526 +6140,832 @@ │ │ │ │ OpenLayers.Renderer.SVG.LABEL_VFACTOR = { │ │ │ │ t: 0, │ │ │ │ b: -1 │ │ │ │ }; │ │ │ │ OpenLayers.Renderer.SVG.preventDefault = function(e) { │ │ │ │ OpenLayers.Event.preventDefault(e) │ │ │ │ }; │ │ │ │ -OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ - xmlns: "urn:schemas-microsoft-com:vml", │ │ │ │ - symbolCache: {}, │ │ │ │ - offset: null, │ │ │ │ - initialize: function(containerID) { │ │ │ │ - if (!this.supported()) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - if (!document.namespaces.olv) { │ │ │ │ - document.namespaces.add("olv", this.xmlns); │ │ │ │ - var style = document.createStyleSheet(); │ │ │ │ - var shapes = ["shape", "rect", "oval", "fill", "stroke", "imagedata", "group", "textbox"]; │ │ │ │ - for (var i = 0, len = shapes.length; i < len; i++) { │ │ │ │ - style.addRule("olv\\:" + shapes[i], "behavior: url(#default#VML); " + "position: absolute; display: inline-block;") │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - supported: function() { │ │ │ │ - return !!document.namespaces │ │ │ │ - }, │ │ │ │ - setExtent: function(extent, resolutionChanged) { │ │ │ │ - var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var left = extent.left / resolution | 0; │ │ │ │ - var top = extent.top / resolution - this.size.h | 0; │ │ │ │ - if (resolutionChanged || !this.offset) { │ │ │ │ - this.offset = { │ │ │ │ - x: left, │ │ │ │ - y: top │ │ │ │ - }; │ │ │ │ - left = 0; │ │ │ │ - top = 0 │ │ │ │ - } else { │ │ │ │ - left = left - this.offset.x; │ │ │ │ - top = top - this.offset.y │ │ │ │ - } │ │ │ │ - var org = left - this.xOffset + " " + top; │ │ │ │ - this.root.coordorigin = org; │ │ │ │ - var roots = [this.root, this.vectorRoot, this.textRoot]; │ │ │ │ - var root; │ │ │ │ - for (var i = 0, len = roots.length; i < len; ++i) { │ │ │ │ - root = roots[i]; │ │ │ │ - var size = this.size.w + " " + this.size.h; │ │ │ │ - root.coordsize = size │ │ │ │ +OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ + type: null, │ │ │ │ + property: null, │ │ │ │ + value: null, │ │ │ │ + matchCase: true, │ │ │ │ + lowerBoundary: null, │ │ │ │ + upperBoundary: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Filter.prototype.initialize.apply(this, [options]); │ │ │ │ + if (this.type === OpenLayers.Filter.Comparison.LIKE && options.matchCase === undefined) { │ │ │ │ + this.matchCase = null │ │ │ │ } │ │ │ │ - this.root.style.flip = "y"; │ │ │ │ - return coordSysUnchanged │ │ │ │ }, │ │ │ │ - setSize: function(size) { │ │ │ │ - OpenLayers.Renderer.prototype.setSize.apply(this, arguments); │ │ │ │ - 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 │ │ │ │ + evaluate: function(context) { │ │ │ │ + if (context instanceof OpenLayers.Feature.Vector) { │ │ │ │ + context = context.attributes │ │ │ │ } │ │ │ │ - }, │ │ │ │ - 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" │ │ │ │ + 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 { │ │ │ │ - nodeType = "olv:oval" │ │ │ │ + result = got == exp │ │ │ │ } │ │ │ │ break; │ │ │ │ - case "OpenLayers.Geometry.Rectangle": │ │ │ │ - nodeType = "olv:rect"; │ │ │ │ + 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.Geometry.LineString": │ │ │ │ - case "OpenLayers.Geometry.LinearRing": │ │ │ │ - case "OpenLayers.Geometry.Polygon": │ │ │ │ - case "OpenLayers.Geometry.Curve": │ │ │ │ - nodeType = "olv:shape"; │ │ │ │ + case OpenLayers.Filter.Comparison.LESS_THAN: │ │ │ │ + result = got < this.value; │ │ │ │ break; │ │ │ │ - default: │ │ │ │ + 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 nodeType │ │ │ │ + return result │ │ │ │ }, │ │ │ │ - setStyle: function(node, style, options, geometry) { │ │ │ │ - style = style || node._style; │ │ │ │ - options = options || node._options; │ │ │ │ - var fillColor = style.fillColor; │ │ │ │ - var title = style.title || style.graphicTitle; │ │ │ │ - if (title) { │ │ │ │ - node.title = title │ │ │ │ + value2regex: function(wildCard, singleChar, escapeChar) { │ │ │ │ + if (wildCard == ".") { │ │ │ │ + throw new Error("'.' is an unsupported wildCard character for " + "OpenLayers.Filter.Comparison") │ │ │ │ } │ │ │ │ - if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ - if (style.externalGraphic) { │ │ │ │ - options.isFilled = true; │ │ │ │ - var width = style.graphicWidth || style.graphicHeight; │ │ │ │ - var height = style.graphicHeight || style.graphicWidth; │ │ │ │ - width = width ? width : style.pointRadius * 2; │ │ │ │ - height = height ? height : style.pointRadius * 2; │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var xOffset = style.graphicXOffset != undefined ? style.graphicXOffset : -(.5 * width); │ │ │ │ - var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.5 * height); │ │ │ │ - node.style.left = ((geometry.x - this.featureDx) / resolution - this.offset.x + xOffset | 0) + "px"; │ │ │ │ - node.style.top = (geometry.y / resolution - this.offset.y - (yOffset + height) | 0) + "px"; │ │ │ │ - node.style.width = width + "px"; │ │ │ │ - node.style.height = height + "px"; │ │ │ │ - node.style.flip = "y"; │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + regex2value: function() { │ │ │ │ + var value = this.value; │ │ │ │ + value = value.replace(/!/g, "!!"); │ │ │ │ + value = value.replace(/(\\)?\\\./g, function($0, $1) { │ │ │ │ + return $1 ? $0 : "!." │ │ │ │ + }); │ │ │ │ + value = value.replace(/(\\)?\\\*/g, function($0, $1) { │ │ │ │ + return $1 ? $0 : "!*" │ │ │ │ + }); │ │ │ │ + value = value.replace(/\\\\/g, "\\"); │ │ │ │ + value = value.replace(/\.\*/g, "*"); │ │ │ │ + return value │ │ │ │ + }, │ │ │ │ + clone: function() { │ │ │ │ + return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison, this) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Filter.Comparison" │ │ │ │ +}); │ │ │ │ +OpenLayers.Filter.Comparison.EQUAL_TO = "=="; │ │ │ │ +OpenLayers.Filter.Comparison.NOT_EQUAL_TO = "!="; │ │ │ │ +OpenLayers.Filter.Comparison.LESS_THAN = "<"; │ │ │ │ +OpenLayers.Filter.Comparison.GREATER_THAN = ">"; │ │ │ │ +OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO = "<="; │ │ │ │ +OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO = ">="; │ │ │ │ +OpenLayers.Filter.Comparison.BETWEEN = ".."; │ │ │ │ +OpenLayers.Filter.Comparison.LIKE = "~"; │ │ │ │ +OpenLayers.Filter.Comparison.IS_NULL = "NULL"; │ │ │ │ +OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ + filters: null, │ │ │ │ + type: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + this.filters = []; │ │ │ │ + OpenLayers.Filter.prototype.initialize.apply(this, [options]) │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.filters = null; │ │ │ │ + OpenLayers.Filter.prototype.destroy.apply(this) │ │ │ │ + }, │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - if (options.isFilled) { │ │ │ │ - node.fillcolor = fillColor │ │ │ │ - } else { │ │ │ │ - node.filled = "false" │ │ │ │ + return undefined │ │ │ │ + }, │ │ │ │ + clone: function() { │ │ │ │ + var filters = []; │ │ │ │ + for (var i = 0, len = this.filters.length; i < len; ++i) { │ │ │ │ + filters.push(this.filters[i].clone()) │ │ │ │ } │ │ │ │ - var fills = node.getElementsByTagName("fill"); │ │ │ │ - var fill = fills.length == 0 ? null : fills[0]; │ │ │ │ - if (!options.isFilled) { │ │ │ │ - if (fill) { │ │ │ │ - node.removeChild(fill) │ │ │ │ - } │ │ │ │ + return new OpenLayers.Filter.Logical({ │ │ │ │ + type: this.type, │ │ │ │ + filters: filters │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Filter.Logical" │ │ │ │ +}); │ │ │ │ +OpenLayers.Filter.Logical.AND = "&&"; │ │ │ │ +OpenLayers.Filter.Logical.OR = "||"; │ │ │ │ +OpenLayers.Filter.Logical.NOT = "!"; │ │ │ │ +OpenLayers.Protocol = OpenLayers.Class({ │ │ │ │ + format: null, │ │ │ │ + options: null, │ │ │ │ + autoDestroy: true, │ │ │ │ + defaultFilter: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.options = options │ │ │ │ + }, │ │ │ │ + mergeWithDefaultFilter: function(filter) { │ │ │ │ + var merged; │ │ │ │ + if (filter && this.defaultFilter) { │ │ │ │ + merged = new OpenLayers.Filter.Logical({ │ │ │ │ + type: OpenLayers.Filter.Logical.AND, │ │ │ │ + filters: [this.defaultFilter, filter] │ │ │ │ + }) │ │ │ │ } else { │ │ │ │ - if (!fill) { │ │ │ │ - fill = this.createNode("olv:fill", node.id + "_fill") │ │ │ │ + merged = filter || this.defaultFilter || undefined │ │ │ │ + } │ │ │ │ + return merged │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.options = null; │ │ │ │ + this.format = null │ │ │ │ + }, │ │ │ │ + read: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + options.filter = this.mergeWithDefaultFilter(options.filter) │ │ │ │ + }, │ │ │ │ + create: function() {}, │ │ │ │ + update: function() {}, │ │ │ │ + delete: function() {}, │ │ │ │ + commit: function() {}, │ │ │ │ + abort: function(response) {}, │ │ │ │ + createCallback: function(method, response, options) { │ │ │ │ + return OpenLayers.Function.bind(function() { │ │ │ │ + method.apply(this, [response, options]) │ │ │ │ + }, this) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol" │ │ │ │ +}); │ │ │ │ +OpenLayers.Protocol.Response = OpenLayers.Class({ │ │ │ │ + code: null, │ │ │ │ + requestType: null, │ │ │ │ + last: true, │ │ │ │ + features: null, │ │ │ │ + data: null, │ │ │ │ + reqFeatures: null, │ │ │ │ + priv: null, │ │ │ │ + error: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options) │ │ │ │ + }, │ │ │ │ + success: function() { │ │ │ │ + return this.code > 0 │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.Response" │ │ │ │ +}); │ │ │ │ +OpenLayers.Protocol.Response.SUCCESS = 1; │ │ │ │ +OpenLayers.Protocol.Response.FAILURE = 0; │ │ │ │ +OpenLayers.ProxyHost = ""; │ │ │ │ +if (!OpenLayers.Request) { │ │ │ │ + OpenLayers.Request = {} │ │ │ │ +} │ │ │ │ +OpenLayers.Util.extend(OpenLayers.Request, { │ │ │ │ + DEFAULT_CONFIG: { │ │ │ │ + method: "GET", │ │ │ │ + url: window.location.href, │ │ │ │ + async: true, │ │ │ │ + user: undefined, │ │ │ │ + password: undefined, │ │ │ │ + params: null, │ │ │ │ + proxy: OpenLayers.ProxyHost, │ │ │ │ + headers: {}, │ │ │ │ + data: null, │ │ │ │ + callback: function() {}, │ │ │ │ + success: null, │ │ │ │ + failure: null, │ │ │ │ + scope: null │ │ │ │ + }, │ │ │ │ + URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/, │ │ │ │ + events: new OpenLayers.Events(this), │ │ │ │ + makeSameOrigin: function(url, proxy) { │ │ │ │ + var sameOrigin = url.indexOf("http") !== 0; │ │ │ │ + var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX); │ │ │ │ + if (urlParts) { │ │ │ │ + var location = window.location; │ │ │ │ + sameOrigin = urlParts[1] == location.protocol && urlParts[3] == location.hostname; │ │ │ │ + var uPort = urlParts[4], │ │ │ │ + lPort = location.port; │ │ │ │ + if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") { │ │ │ │ + sameOrigin = sameOrigin && uPort == lPort │ │ │ │ } │ │ │ │ - fill.opacity = style.fillOpacity; │ │ │ │ - if (node._geometryClass === "OpenLayers.Geometry.Point" && style.externalGraphic) { │ │ │ │ - if (style.graphicOpacity) { │ │ │ │ - fill.opacity = style.graphicOpacity │ │ │ │ - } │ │ │ │ - fill.src = style.externalGraphic; │ │ │ │ - fill.type = "frame"; │ │ │ │ - if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ - fill.aspect = "atmost" │ │ │ │ + } │ │ │ │ + if (!sameOrigin) { │ │ │ │ + if (proxy) { │ │ │ │ + if (typeof proxy == "function") { │ │ │ │ + url = proxy(url) │ │ │ │ + } else { │ │ │ │ + url = proxy + encodeURIComponent(url) │ │ │ │ } │ │ │ │ } │ │ │ │ - if (fill.parentNode != node) { │ │ │ │ - node.appendChild(fill) │ │ │ │ - } │ │ │ │ } │ │ │ │ - var rotation = style.rotation; │ │ │ │ - if (rotation !== undefined || node._rotation !== undefined) { │ │ │ │ - node._rotation = rotation; │ │ │ │ - if (style.externalGraphic) { │ │ │ │ - this.graphicRotate(node, xOffset, yOffset, style); │ │ │ │ - fill.opacity = 0 │ │ │ │ - } else if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ - node.style.rotation = rotation || 0 │ │ │ │ + return url │ │ │ │ + }, │ │ │ │ + issue: function(config) { │ │ │ │ + var defaultConfig = OpenLayers.Util.extend(this.DEFAULT_CONFIG, { │ │ │ │ + proxy: OpenLayers.ProxyHost │ │ │ │ + }); │ │ │ │ + config = config || {}; │ │ │ │ + config.headers = config.headers || {}; │ │ │ │ + config = OpenLayers.Util.applyDefaults(config, defaultConfig); │ │ │ │ + config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers); │ │ │ │ + var customRequestedWithHeader = false, │ │ │ │ + headerKey; │ │ │ │ + for (headerKey in config.headers) { │ │ │ │ + if (config.headers.hasOwnProperty(headerKey)) { │ │ │ │ + if (headerKey.toLowerCase() === "x-requested-with") { │ │ │ │ + customRequestedWithHeader = true │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ - var strokes = node.getElementsByTagName("stroke"); │ │ │ │ - var stroke = strokes.length == 0 ? null : strokes[0]; │ │ │ │ - if (!options.isStroked) { │ │ │ │ - node.stroked = false; │ │ │ │ - if (stroke) { │ │ │ │ - stroke.on = false │ │ │ │ + if (customRequestedWithHeader === false) { │ │ │ │ + config.headers["X-Requested-With"] = "XMLHttpRequest" │ │ │ │ + } │ │ │ │ + var request = new OpenLayers.Request.XMLHttpRequest; │ │ │ │ + var url = OpenLayers.Util.urlAppend(config.url, OpenLayers.Util.getParameterString(config.params || {})); │ │ │ │ + url = OpenLayers.Request.makeSameOrigin(url, config.proxy); │ │ │ │ + request.open(config.method, url, config.async, config.user, config.password); │ │ │ │ + for (var header in config.headers) { │ │ │ │ + request.setRequestHeader(header, config.headers[header]) │ │ │ │ + } │ │ │ │ + var events = this.events; │ │ │ │ + var self = this; │ │ │ │ + request.onreadystatechange = function() { │ │ │ │ + if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) { │ │ │ │ + var proceed = events.triggerEvent("complete", { │ │ │ │ + request: request, │ │ │ │ + config: config, │ │ │ │ + requestUrl: url │ │ │ │ + }); │ │ │ │ + if (proceed !== false) { │ │ │ │ + self.runCallbacks({ │ │ │ │ + request: request, │ │ │ │ + config: config, │ │ │ │ + requestUrl: url │ │ │ │ + }) │ │ │ │ + } │ │ │ │ } │ │ │ │ + }; │ │ │ │ + if (config.async === false) { │ │ │ │ + request.send(config.data) │ │ │ │ } else { │ │ │ │ - if (!stroke) { │ │ │ │ - stroke = this.createNode("olv:stroke", node.id + "_stroke"); │ │ │ │ - node.appendChild(stroke) │ │ │ │ + window.setTimeout(function() { │ │ │ │ + if (request.readyState !== 0) { │ │ │ │ + request.send(config.data) │ │ │ │ + } │ │ │ │ + }, 0) │ │ │ │ + } │ │ │ │ + return request │ │ │ │ + }, │ │ │ │ + runCallbacks: function(options) { │ │ │ │ + var request = options.request; │ │ │ │ + var config = options.config; │ │ │ │ + var complete = config.scope ? OpenLayers.Function.bind(config.callback, config.scope) : config.callback; │ │ │ │ + var success; │ │ │ │ + if (config.success) { │ │ │ │ + success = config.scope ? OpenLayers.Function.bind(config.success, config.scope) : config.success │ │ │ │ + } │ │ │ │ + var failure; │ │ │ │ + if (config.failure) { │ │ │ │ + failure = config.scope ? OpenLayers.Function.bind(config.failure, config.scope) : config.failure │ │ │ │ + } │ │ │ │ + if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" && request.responseText) { │ │ │ │ + request.status = 200 │ │ │ │ + } │ │ │ │ + complete(request); │ │ │ │ + if (!request.status || request.status >= 200 && request.status < 300) { │ │ │ │ + this.events.triggerEvent("success", options); │ │ │ │ + if (success) { │ │ │ │ + success(request) │ │ │ │ } │ │ │ │ - stroke.on = true; │ │ │ │ - stroke.color = style.strokeColor; │ │ │ │ - stroke.weight = style.strokeWidth + "px"; │ │ │ │ - stroke.opacity = style.strokeOpacity; │ │ │ │ - stroke.endcap = style.strokeLinecap == "butt" ? "flat" : style.strokeLinecap || "round"; │ │ │ │ - if (style.strokeDashstyle) { │ │ │ │ - stroke.dashstyle = this.dashStyle(style) │ │ │ │ + } │ │ │ │ + if (request.status && (request.status < 200 || request.status >= 300)) { │ │ │ │ + this.events.triggerEvent("failure", options); │ │ │ │ + if (failure) { │ │ │ │ + failure(request) │ │ │ │ } │ │ │ │ } │ │ │ │ - if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ - node.style.cursor = style.cursor │ │ │ │ + }, │ │ │ │ + GET: function(config) { │ │ │ │ + config = OpenLayers.Util.extend(config, { │ │ │ │ + method: "GET" │ │ │ │ + }); │ │ │ │ + return OpenLayers.Request.issue(config) │ │ │ │ + }, │ │ │ │ + POST: function(config) { │ │ │ │ + config = OpenLayers.Util.extend(config, { │ │ │ │ + method: "POST" │ │ │ │ + }); │ │ │ │ + config.headers = config.headers ? config.headers : {}; │ │ │ │ + if (!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) { │ │ │ │ + config.headers["Content-Type"] = "application/xml" │ │ │ │ } │ │ │ │ - return node │ │ │ │ + return OpenLayers.Request.issue(config) │ │ │ │ }, │ │ │ │ - graphicRotate: function(node, xOffset, yOffset, style) { │ │ │ │ - var style = style || node._style; │ │ │ │ - var rotation = style.rotation || 0; │ │ │ │ - var aspectRatio, size; │ │ │ │ - if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ - 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) │ │ │ │ + PUT: function(config) { │ │ │ │ + config = OpenLayers.Util.extend(config, { │ │ │ │ + method: "PUT" │ │ │ │ + }); │ │ │ │ + config.headers = config.headers ? config.headers : {}; │ │ │ │ + if (!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) { │ │ │ │ + config.headers["Content-Type"] = "application/xml" │ │ │ │ + } │ │ │ │ + return OpenLayers.Request.issue(config) │ │ │ │ + }, │ │ │ │ + DELETE: function(config) { │ │ │ │ + config = OpenLayers.Util.extend(config, { │ │ │ │ + method: "DELETE" │ │ │ │ + }); │ │ │ │ + return OpenLayers.Request.issue(config) │ │ │ │ + }, │ │ │ │ + HEAD: function(config) { │ │ │ │ + config = OpenLayers.Util.extend(config, { │ │ │ │ + method: "HEAD" │ │ │ │ + }); │ │ │ │ + return OpenLayers.Request.issue(config) │ │ │ │ + }, │ │ │ │ + OPTIONS: function(config) { │ │ │ │ + config = OpenLayers.Util.extend(config, { │ │ │ │ + method: "OPTIONS" │ │ │ │ + }); │ │ │ │ + return OpenLayers.Request.issue(config) │ │ │ │ + } │ │ │ │ +}); │ │ │ │ +(function() { │ │ │ │ + var oXMLHttpRequest = window.XMLHttpRequest; │ │ │ │ + var bGecko = !!window.controllers, │ │ │ │ + bIE = window.document.all && !window.opera, │ │ │ │ + bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/); │ │ │ │ + │ │ │ │ + function fXMLHttpRequest() { │ │ │ │ + this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP"); │ │ │ │ + this._listeners = [] │ │ │ │ + } │ │ │ │ + │ │ │ │ + function cXMLHttpRequest() { │ │ │ │ + return new fXMLHttpRequest │ │ │ │ + } │ │ │ │ + cXMLHttpRequest.prototype = fXMLHttpRequest.prototype; │ │ │ │ + if (bGecko && oXMLHttpRequest.wrapped) cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped; │ │ │ │ + cXMLHttpRequest.UNSENT = 0; │ │ │ │ + cXMLHttpRequest.OPENED = 1; │ │ │ │ + cXMLHttpRequest.HEADERS_RECEIVED = 2; │ │ │ │ + cXMLHttpRequest.LOADING = 3; │ │ │ │ + cXMLHttpRequest.DONE = 4; │ │ │ │ + cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ + cXMLHttpRequest.prototype.responseText = ""; │ │ │ │ + cXMLHttpRequest.prototype.responseXML = null; │ │ │ │ + cXMLHttpRequest.prototype.status = 0; │ │ │ │ + cXMLHttpRequest.prototype.statusText = ""; │ │ │ │ + cXMLHttpRequest.prototype.priority = "NORMAL"; │ │ │ │ + cXMLHttpRequest.prototype.onreadystatechange = null; │ │ │ │ + cXMLHttpRequest.onreadystatechange = null; │ │ │ │ + cXMLHttpRequest.onopen = null; │ │ │ │ + cXMLHttpRequest.onsend = null; │ │ │ │ + cXMLHttpRequest.onabort = null; │ │ │ │ + cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) { │ │ │ │ + delete this._headers; │ │ │ │ + if (arguments.length < 3) bAsync = true; │ │ │ │ + this._async = bAsync; │ │ │ │ + var oRequest = this, │ │ │ │ + nState = this.readyState, │ │ │ │ + fOnUnload; │ │ │ │ + if (bIE && bAsync) { │ │ │ │ + fOnUnload = function() { │ │ │ │ + if (nState != cXMLHttpRequest.DONE) { │ │ │ │ + fCleanTransport(oRequest); │ │ │ │ + oRequest.abort() │ │ │ │ } │ │ │ │ - }, this); │ │ │ │ - img.src = style.externalGraphic; │ │ │ │ - return │ │ │ │ - } else { │ │ │ │ - size = Math.max(style.graphicWidth, style.graphicHeight); │ │ │ │ - aspectRatio = style.graphicWidth / style.graphicHeight │ │ │ │ + }; │ │ │ │ + window.attachEvent("onunload", fOnUnload) │ │ │ │ } │ │ │ │ - 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"; │ │ │ │ - var image = document.getElementById(node.id + "_image"); │ │ │ │ - if (!image) { │ │ │ │ - image = this.createNode("olv:imagedata", node.id + "_image"); │ │ │ │ - node.appendChild(image) │ │ │ │ + if (cXMLHttpRequest.onopen) cXMLHttpRequest.onopen.apply(this, arguments); │ │ │ │ + if (arguments.length > 4) this._object.open(sMethod, sUrl, bAsync, sUser, sPassword); │ │ │ │ + else if (arguments.length > 3) this._object.open(sMethod, sUrl, bAsync, sUser); │ │ │ │ + else this._object.open(sMethod, sUrl, bAsync); │ │ │ │ + this.readyState = cXMLHttpRequest.OPENED; │ │ │ │ + fReadyStateChange(this); │ │ │ │ + this._object.onreadystatechange = function() { │ │ │ │ + if (bGecko && !bAsync) return; │ │ │ │ + oRequest.readyState = oRequest._object.readyState; │ │ │ │ + fSynchronizeValues(oRequest); │ │ │ │ + if (oRequest._aborted) { │ │ │ │ + oRequest.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ + return │ │ │ │ + } │ │ │ │ + if (oRequest.readyState == cXMLHttpRequest.DONE) { │ │ │ │ + delete oRequest._data; │ │ │ │ + fCleanTransport(oRequest); │ │ │ │ + if (bIE && bAsync) window.detachEvent("onunload", fOnUnload) │ │ │ │ + } │ │ │ │ + if (nState != oRequest.readyState) fReadyStateChange(oRequest); │ │ │ │ + nState = oRequest.readyState │ │ │ │ } │ │ │ │ - image.style.width = width + "px"; │ │ │ │ - image.style.height = height + "px"; │ │ │ │ - image.src = style.externalGraphic; │ │ │ │ - image.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + "src='', sizingMethod='scale')"; │ │ │ │ - var rot = rotation * Math.PI / 180; │ │ │ │ - var sintheta = Math.sin(rot); │ │ │ │ - var costheta = Math.cos(rot); │ │ │ │ - var filter = "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta + ",M12=" + -sintheta + ",M21=" + sintheta + ",M22=" + costheta + ",SizingMethod='auto expand')\n"; │ │ │ │ - var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ - if (opacity && opacity != 1) { │ │ │ │ - filter += "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + opacity + ")\n" │ │ │ │ + }; │ │ │ │ + │ │ │ │ + function fXMLHttpRequest_send(oRequest) { │ │ │ │ + oRequest._object.send(oRequest._data); │ │ │ │ + if (bGecko && !oRequest._async) { │ │ │ │ + oRequest.readyState = cXMLHttpRequest.OPENED; │ │ │ │ + fSynchronizeValues(oRequest); │ │ │ │ + while (oRequest.readyState < cXMLHttpRequest.DONE) { │ │ │ │ + oRequest.readyState++; │ │ │ │ + fReadyStateChange(oRequest); │ │ │ │ + if (oRequest._aborted) return │ │ │ │ + } │ │ │ │ } │ │ │ │ - node.style.filter = filter; │ │ │ │ - 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" │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ + } │ │ │ │ + cXMLHttpRequest.prototype.send = function(vData) { │ │ │ │ + if (cXMLHttpRequest.onsend) cXMLHttpRequest.onsend.apply(this, arguments); │ │ │ │ + if (!arguments.length) vData = null; │ │ │ │ + if (vData && vData.nodeType) { │ │ │ │ + vData = window.XMLSerializer ? (new window.XMLSerializer).serializeToString(vData) : vData.xml; │ │ │ │ + if (!this._headers["Content-Type"]) this._object.setRequestHeader("Content-Type", "application/xml") │ │ │ │ } │ │ │ │ - if (strokeColor == "none" && node.strokecolor != strokeColor) { │ │ │ │ - node.strokecolor = strokeColor │ │ │ │ + this._data = vData; │ │ │ │ + fXMLHttpRequest_send(this) │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.abort = function() { │ │ │ │ + if (cXMLHttpRequest.onabort) cXMLHttpRequest.onabort.apply(this, arguments); │ │ │ │ + if (this.readyState > cXMLHttpRequest.UNSENT) this._aborted = true; │ │ │ │ + this._object.abort(); │ │ │ │ + fCleanTransport(this); │ │ │ │ + this.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ + delete this._data │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.getAllResponseHeaders = function() { │ │ │ │ + return this._object.getAllResponseHeaders() │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.getResponseHeader = function(sName) { │ │ │ │ + return this._object.getResponseHeader(sName) │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) { │ │ │ │ + if (!this._headers) this._headers = {}; │ │ │ │ + this._headers[sName] = sValue; │ │ │ │ + return this._object.setRequestHeader(sName, sValue) │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) { │ │ │ │ + for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) │ │ │ │ + if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) return; │ │ │ │ + this._listeners.push([sName, fHandler, bUseCapture]) │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) { │ │ │ │ + for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) │ │ │ │ + if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) break; │ │ │ │ + if (oListener) this._listeners.splice(nIndex, 1) │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) { │ │ │ │ + var oEventPseudo = { │ │ │ │ + type: oEvent.type, │ │ │ │ + target: this, │ │ │ │ + currentTarget: this, │ │ │ │ + eventPhase: 2, │ │ │ │ + bubbles: oEvent.bubbles, │ │ │ │ + cancelable: oEvent.cancelable, │ │ │ │ + timeStamp: oEvent.timeStamp, │ │ │ │ + stopPropagation: function() {}, │ │ │ │ + preventDefault: function() {}, │ │ │ │ + initEvent: function() {} │ │ │ │ + }; │ │ │ │ + if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)(this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]); │ │ │ │ + for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) │ │ │ │ + if (oListener[0] == oEventPseudo.type && !oListener[2])(oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]) │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.prototype.toString = function() { │ │ │ │ + return "[" + "object" + " " + "XMLHttpRequest" + "]" │ │ │ │ + }; │ │ │ │ + cXMLHttpRequest.toString = function() { │ │ │ │ + return "[" + "XMLHttpRequest" + "]" │ │ │ │ + }; │ │ │ │ + │ │ │ │ + function fReadyStateChange(oRequest) { │ │ │ │ + if (cXMLHttpRequest.onreadystatechange) cXMLHttpRequest.onreadystatechange.apply(oRequest); │ │ │ │ + oRequest.dispatchEvent({ │ │ │ │ + type: "readystatechange", │ │ │ │ + bubbles: false, │ │ │ │ + cancelable: false, │ │ │ │ + timeStamp: new Date + 0 │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + │ │ │ │ + function fGetDocument(oRequest) { │ │ │ │ + var oDocument = oRequest.responseXML, │ │ │ │ + sResponse = oRequest.responseText; │ │ │ │ + if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) { │ │ │ │ + oDocument = new window.ActiveXObject("Microsoft.XMLDOM"); │ │ │ │ + oDocument.async = false; │ │ │ │ + oDocument.validateOnParse = false; │ │ │ │ + oDocument.loadXML(sResponse) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - setNodeDimension: function(node, geometry) { │ │ │ │ - var bbox = geometry.getBounds(); │ │ │ │ - if (bbox) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var scaledBox = new OpenLayers.Bounds((bbox.left - this.featureDx) / resolution - this.offset.x | 0, bbox.bottom / resolution - this.offset.y | 0, (bbox.right - this.featureDx) / resolution - this.offset.x | 0, bbox.top / resolution - this.offset.y | 0); │ │ │ │ - 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() │ │ │ │ + if (oDocument) │ │ │ │ + if (bIE && oDocument.parseError != 0 || !oDocument.documentElement || oDocument.documentElement && oDocument.documentElement.tagName == "parsererror") return null; │ │ │ │ + return oDocument │ │ │ │ + } │ │ │ │ + │ │ │ │ + function fSynchronizeValues(oRequest) { │ │ │ │ + try { │ │ │ │ + oRequest.responseText = oRequest._object.responseText │ │ │ │ + } catch (e) {} │ │ │ │ + try { │ │ │ │ + oRequest.responseXML = fGetDocument(oRequest._object) │ │ │ │ + } catch (e) {} │ │ │ │ + try { │ │ │ │ + oRequest.status = oRequest._object.status │ │ │ │ + } catch (e) {} │ │ │ │ + try { │ │ │ │ + oRequest.statusText = oRequest._object.statusText │ │ │ │ + } catch (e) {} │ │ │ │ + } │ │ │ │ + │ │ │ │ + function fCleanTransport(oRequest) { │ │ │ │ + oRequest._object.onreadystatechange = new window.Function │ │ │ │ + } │ │ │ │ + if (!window.Function.prototype.apply) { │ │ │ │ + window.Function.prototype.apply = function(oRequest, oArguments) { │ │ │ │ + if (!oArguments) oArguments = []; │ │ │ │ + oRequest.__func = this; │ │ │ │ + oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]); │ │ │ │ + delete oRequest.__func │ │ │ │ } │ │ │ │ - }, │ │ │ │ - dashStyle: function(style) { │ │ │ │ - var dash = style.strokeDashstyle; │ │ │ │ - switch (dash) { │ │ │ │ - case "solid": │ │ │ │ - case "dot": │ │ │ │ - case "dash": │ │ │ │ - case "dashdot": │ │ │ │ - case "longdash": │ │ │ │ - case "longdashdot": │ │ │ │ - return dash; │ │ │ │ - default: │ │ │ │ - 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" │ │ │ │ + } │ │ │ │ + if (!OpenLayers.Request) { │ │ │ │ + OpenLayers.Request = {} │ │ │ │ + } │ │ │ │ + OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest │ │ │ │ +})(); │ │ │ │ +OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ + url: null, │ │ │ │ + headers: null, │ │ │ │ + params: null, │ │ │ │ + callback: null, │ │ │ │ + scope: null, │ │ │ │ + readWithPOST: false, │ │ │ │ + updateWithPOST: false, │ │ │ │ + deleteWithPOST: false, │ │ │ │ + wildcarded: false, │ │ │ │ + srsInBBOX: false, │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + this.params = {}; │ │ │ │ + this.headers = {}; │ │ │ │ + OpenLayers.Protocol.prototype.initialize.apply(this, arguments); │ │ │ │ + if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { │ │ │ │ + var format = new OpenLayers.Format.QueryStringFilter({ │ │ │ │ + wildcarded: this.wildcarded, │ │ │ │ + srsInBBOX: this.srsInBBOX │ │ │ │ + }); │ │ │ │ + this.filterToParams = function(filter, params) { │ │ │ │ + return format.write(filter, params) │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ - createNode: function(type, id) { │ │ │ │ - var node = document.createElement(type); │ │ │ │ - if (id) { │ │ │ │ - node.id = id │ │ │ │ - } │ │ │ │ - node.unselectable = "on"; │ │ │ │ - node.onselectstart = OpenLayers.Function.False; │ │ │ │ - return node │ │ │ │ + destroy: function() { │ │ │ │ + this.params = null; │ │ │ │ + this.headers = null; │ │ │ │ + OpenLayers.Protocol.prototype.destroy.apply(this) │ │ │ │ }, │ │ │ │ - nodeTypeCompare: function(node, type) { │ │ │ │ - var subType = type; │ │ │ │ - var splitIndex = subType.indexOf(":"); │ │ │ │ - if (splitIndex != -1) { │ │ │ │ - subType = subType.substr(splitIndex + 1) │ │ │ │ + read: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ + options = options || {}; │ │ │ │ + options.params = OpenLayers.Util.applyDefaults(options.params, this.options.params); │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + if (options.filter && this.filterToParams) { │ │ │ │ + options.params = this.filterToParams(options.filter, options.params) │ │ │ │ } │ │ │ │ - var nodeName = node.nodeName; │ │ │ │ - splitIndex = nodeName.indexOf(":"); │ │ │ │ - if (splitIndex != -1) { │ │ │ │ - nodeName = nodeName.substr(splitIndex + 1) │ │ │ │ + var readWithPOST = options.readWithPOST !== undefined ? options.readWithPOST : this.readWithPOST; │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "read" │ │ │ │ + }); │ │ │ │ + if (readWithPOST) { │ │ │ │ + var headers = options.headers || {}; │ │ │ │ + headers["Content-Type"] = "application/x-www-form-urlencoded"; │ │ │ │ + resp.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ + data: OpenLayers.Util.getParameterString(options.params), │ │ │ │ + headers: headers │ │ │ │ + }) │ │ │ │ + } else { │ │ │ │ + resp.priv = OpenLayers.Request.GET({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ + params: options.params, │ │ │ │ + headers: options.headers │ │ │ │ + }) │ │ │ │ } │ │ │ │ - return subType == nodeName │ │ │ │ - }, │ │ │ │ - createRenderRoot: function() { │ │ │ │ - return this.nodeFactory(this.container.id + "_vmlRoot", "div") │ │ │ │ + return resp │ │ │ │ }, │ │ │ │ - createRoot: function(suffix) { │ │ │ │ - return this.nodeFactory(this.container.id + suffix, "olv:group") │ │ │ │ + handleRead: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options) │ │ │ │ }, │ │ │ │ - drawPoint: function(node, geometry) { │ │ │ │ - return this.drawCircle(node, geometry, 1) │ │ │ │ + create: function(features, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: features, │ │ │ │ + requestType: "create" │ │ │ │ + }); │ │ │ │ + resp.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleCreate, resp, options), │ │ │ │ + headers: options.headers, │ │ │ │ + data: this.format.write(features) │ │ │ │ + }); │ │ │ │ + return resp │ │ │ │ }, │ │ │ │ - drawCircle: function(node, geometry, radius) { │ │ │ │ - if (!isNaN(geometry.x) && !isNaN(geometry.y)) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - node.style.left = ((geometry.x - this.featureDx) / resolution - this.offset.x | 0) - radius + "px"; │ │ │ │ - node.style.top = (geometry.y / resolution - this.offset.y | 0) - radius + "px"; │ │ │ │ - var diameter = radius * 2; │ │ │ │ - node.style.width = diameter + "px"; │ │ │ │ - node.style.height = diameter + "px"; │ │ │ │ - return node │ │ │ │ - } │ │ │ │ - return false │ │ │ │ + handleCreate: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options) │ │ │ │ }, │ │ │ │ - drawLineString: function(node, geometry) { │ │ │ │ - return this.drawLine(node, geometry, false) │ │ │ │ + update: function(feature, options) { │ │ │ │ + options = options || {}; │ │ │ │ + var url = options.url || feature.url || this.options.url + "/" + feature.fid; │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: feature, │ │ │ │ + requestType: "update" │ │ │ │ + }); │ │ │ │ + var method = this.updateWithPOST ? "POST" : "PUT"; │ │ │ │ + resp.priv = OpenLayers.Request[method]({ │ │ │ │ + url: url, │ │ │ │ + callback: this.createCallback(this.handleUpdate, resp, options), │ │ │ │ + headers: options.headers, │ │ │ │ + data: this.format.write(feature) │ │ │ │ + }); │ │ │ │ + return resp │ │ │ │ }, │ │ │ │ - drawLinearRing: function(node, geometry) { │ │ │ │ - return this.drawLine(node, geometry, true) │ │ │ │ + handleUpdate: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options) │ │ │ │ }, │ │ │ │ - drawLine: function(node, geometry, closeLine) { │ │ │ │ - this.setNodeDimension(node, geometry); │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var numComponents = geometry.components.length; │ │ │ │ - var parts = new Array(numComponents); │ │ │ │ - var comp, x, y; │ │ │ │ - for (var i = 0; i < numComponents; i++) { │ │ │ │ - comp = geometry.components[i]; │ │ │ │ - x = (comp.x - this.featureDx) / resolution - this.offset.x | 0; │ │ │ │ - y = comp.y / resolution - this.offset.y | 0; │ │ │ │ - parts[i] = " " + x + "," + y + " l " │ │ │ │ + delete: function(feature, options) { │ │ │ │ + options = options || {}; │ │ │ │ + var url = options.url || feature.url || this.options.url + "/" + feature.fid; │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: feature, │ │ │ │ + requestType: "delete" │ │ │ │ + }); │ │ │ │ + var method = this.deleteWithPOST ? "POST" : "DELETE"; │ │ │ │ + var requestOptions = { │ │ │ │ + url: url, │ │ │ │ + callback: this.createCallback(this.handleDelete, resp, options), │ │ │ │ + headers: options.headers │ │ │ │ + }; │ │ │ │ + if (this.deleteWithPOST) { │ │ │ │ + requestOptions.data = this.format.write(feature) │ │ │ │ } │ │ │ │ - var end = closeLine ? " x e" : " e"; │ │ │ │ - node.path = "m" + parts.join("") + end; │ │ │ │ - return node │ │ │ │ + resp.priv = OpenLayers.Request[method](requestOptions); │ │ │ │ + return resp │ │ │ │ }, │ │ │ │ - drawPolygon: function(node, geometry) { │ │ │ │ - this.setNodeDimension(node, geometry); │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var path = []; │ │ │ │ - var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y; │ │ │ │ - for (j = 0, jj = geometry.components.length; j < jj; j++) { │ │ │ │ - path.push("m"); │ │ │ │ - points = geometry.components[j].components; │ │ │ │ - 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) { │ │ │ │ - if (!first) { │ │ │ │ - first = pathComp │ │ │ │ - } else if (first != pathComp) { │ │ │ │ - if (!second) { │ │ │ │ - second = pathComp │ │ │ │ - } else if (second != pathComp) { │ │ │ │ - area = true │ │ │ │ - } │ │ │ │ - } │ │ │ │ + handleDelete: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options) │ │ │ │ + }, │ │ │ │ + handleResponse: function(resp, options) { │ │ │ │ + var request = resp.priv; │ │ │ │ + if (options.callback) { │ │ │ │ + if (request.status >= 200 && request.status < 300) { │ │ │ │ + if (resp.requestType != "delete") { │ │ │ │ + resp.features = this.parseFeatures(request) │ │ │ │ } │ │ │ │ + resp.code = OpenLayers.Protocol.Response.SUCCESS │ │ │ │ + } else { │ │ │ │ + resp.code = OpenLayers.Protocol.Response.FAILURE │ │ │ │ } │ │ │ │ - path.push(area ? " x " : " ") │ │ │ │ + options.callback.call(options.scope, resp) │ │ │ │ } │ │ │ │ - path.push("e"); │ │ │ │ - node.path = path.join(""); │ │ │ │ - return node │ │ │ │ }, │ │ │ │ - drawRectangle: function(node, geometry) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - node.style.left = ((geometry.x - this.featureDx) / resolution - this.offset.x | 0) + "px"; │ │ │ │ - node.style.top = (geometry.y / resolution - this.offset.y | 0) + "px"; │ │ │ │ - node.style.width = (geometry.width / resolution | 0) + "px"; │ │ │ │ - node.style.height = (geometry.height / resolution | 0) + "px"; │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - drawText: function(featureId, style, location) { │ │ │ │ - var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect"); │ │ │ │ - var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox"); │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - label.style.left = ((location.x - this.featureDx) / resolution - this.offset.x | 0) + "px"; │ │ │ │ - label.style.top = (location.y / resolution - this.offset.y | 0) + "px"; │ │ │ │ - label.style.flip = "y"; │ │ │ │ - textbox.innerText = style.label; │ │ │ │ - if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ - textbox.style.cursor = style.cursor │ │ │ │ - } │ │ │ │ - if (style.fontColor) { │ │ │ │ - textbox.style.color = style.fontColor │ │ │ │ - } │ │ │ │ - if (style.fontOpacity) { │ │ │ │ - textbox.style.filter = "alpha(opacity=" + style.fontOpacity * 100 + ")" │ │ │ │ + parseFeatures: function(request) { │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText │ │ │ │ } │ │ │ │ - if (style.fontFamily) { │ │ │ │ - textbox.style.fontFamily = style.fontFamily │ │ │ │ + if (!doc || doc.length <= 0) { │ │ │ │ + return null │ │ │ │ } │ │ │ │ - if (style.fontSize) { │ │ │ │ - textbox.style.fontSize = style.fontSize │ │ │ │ + return this.format.read(doc) │ │ │ │ + }, │ │ │ │ + commit: function(features, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var resp = [], │ │ │ │ + nResponses = 0; │ │ │ │ + var types = {}; │ │ │ │ + types[OpenLayers.State.INSERT] = []; │ │ │ │ + types[OpenLayers.State.UPDATE] = []; │ │ │ │ + types[OpenLayers.State.DELETE] = []; │ │ │ │ + var feature, list, requestFeatures = []; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + feature = features[i]; │ │ │ │ + list = types[feature.state]; │ │ │ │ + if (list) { │ │ │ │ + list.push(feature); │ │ │ │ + requestFeatures.push(feature) │ │ │ │ + } │ │ │ │ } │ │ │ │ - if (style.fontWeight) { │ │ │ │ - textbox.style.fontWeight = style.fontWeight │ │ │ │ + var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + types[OpenLayers.State.UPDATE].length + types[OpenLayers.State.DELETE].length; │ │ │ │ + var success = true; │ │ │ │ + var finalResponse = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: requestFeatures │ │ │ │ + }); │ │ │ │ + │ │ │ │ + function insertCallback(response) { │ │ │ │ + var len = response.features ? response.features.length : 0; │ │ │ │ + var fids = new Array(len); │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ + fids[i] = response.features[i].fid │ │ │ │ + } │ │ │ │ + finalResponse.insertIds = fids; │ │ │ │ + callback.apply(this, [response]) │ │ │ │ } │ │ │ │ - if (style.fontStyle) { │ │ │ │ - textbox.style.fontStyle = style.fontStyle │ │ │ │ + │ │ │ │ + function callback(response) { │ │ │ │ + this.callUserCallback(response, options); │ │ │ │ + success = success && response.success(); │ │ │ │ + nResponses++; │ │ │ │ + if (nResponses >= nRequests) { │ │ │ │ + if (options.callback) { │ │ │ │ + finalResponse.code = success ? OpenLayers.Protocol.Response.SUCCESS : OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + options.callback.apply(options.scope, [finalResponse]) │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - if (style.labelSelect === true) { │ │ │ │ - label._featureId = featureId; │ │ │ │ - textbox._featureId = featureId; │ │ │ │ - textbox._geometry = location; │ │ │ │ - textbox._geometryClass = location.CLASS_NAME │ │ │ │ + var queue = types[OpenLayers.State.INSERT]; │ │ │ │ + if (queue.length > 0) { │ │ │ │ + resp.push(this.create(queue, OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: insertCallback, │ │ │ │ + scope: this │ │ │ │ + }, options.create))) │ │ │ │ } │ │ │ │ - textbox.style.whiteSpace = "nowrap"; │ │ │ │ - textbox.inset = "1px,0px,0px,0px"; │ │ │ │ - if (!label.parentNode) { │ │ │ │ - label.appendChild(textbox); │ │ │ │ - this.textRoot.appendChild(label) │ │ │ │ + queue = types[OpenLayers.State.UPDATE]; │ │ │ │ + for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ + resp.push(this.update(queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: callback, │ │ │ │ + scope: this │ │ │ │ + }, options.update))) │ │ │ │ } │ │ │ │ - var align = style.labelAlign || "cm"; │ │ │ │ - if (align.length == 1) { │ │ │ │ - align += "m" │ │ │ │ + queue = types[OpenLayers.State.DELETE]; │ │ │ │ + for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ + resp.push(this["delete"](queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: callback, │ │ │ │ + scope: this │ │ │ │ + }, options["delete"]))) │ │ │ │ } │ │ │ │ - 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" │ │ │ │ + return resp │ │ │ │ }, │ │ │ │ - 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) │ │ │ │ + abort: function(response) { │ │ │ │ + if (response) { │ │ │ │ + response.priv.abort() │ │ │ │ } │ │ │ │ - layer && layer.renderer.clear(); │ │ │ │ - OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments); │ │ │ │ - layer && layer.redraw() │ │ │ │ }, │ │ │ │ - importSymbol: function(graphicName) { │ │ │ │ - var id = this.container.id + "-" + graphicName; │ │ │ │ - var cache = this.symbolCache[id]; │ │ │ │ - if (cache) { │ │ │ │ - return cache │ │ │ │ - } │ │ │ │ - var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ - if (!symbol) { │ │ │ │ - throw new Error(graphicName + " is not a valid symbol name") │ │ │ │ - } │ │ │ │ - var symbolExtent = new OpenLayers.Bounds(Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ - var pathitems = ["m"]; │ │ │ │ - for (var i = 0; i < symbol.length; i = i + 2) { │ │ │ │ - var x = symbol[i]; │ │ │ │ - var y = symbol[i + 1]; │ │ │ │ - symbolExtent.left = Math.min(symbolExtent.left, x); │ │ │ │ - symbolExtent.bottom = Math.min(symbolExtent.bottom, y); │ │ │ │ - symbolExtent.right = Math.max(symbolExtent.right, x); │ │ │ │ - symbolExtent.top = Math.max(symbolExtent.top, y); │ │ │ │ - pathitems.push(x); │ │ │ │ - pathitems.push(y); │ │ │ │ - if (i == 0) { │ │ │ │ - pathitems.push("l") │ │ │ │ - } │ │ │ │ - } │ │ │ │ - pathitems.push("x e"); │ │ │ │ - var path = pathitems.join(" "); │ │ │ │ - var diff = (symbolExtent.getWidth() - symbolExtent.getHeight()) / 2; │ │ │ │ - if (diff > 0) { │ │ │ │ - symbolExtent.bottom = symbolExtent.bottom - diff; │ │ │ │ - symbolExtent.top = symbolExtent.top + diff │ │ │ │ - } else { │ │ │ │ - symbolExtent.left = symbolExtent.left + diff; │ │ │ │ - symbolExtent.right = symbolExtent.right - diff │ │ │ │ + callUserCallback: function(resp, options) { │ │ │ │ + var opt = options[resp.requestType]; │ │ │ │ + if (opt && opt.callback) { │ │ │ │ + opt.callback.call(opt.scope, resp) │ │ │ │ } │ │ │ │ - cache = { │ │ │ │ - path: path, │ │ │ │ - size: symbolExtent.getWidth(), │ │ │ │ - left: symbolExtent.left, │ │ │ │ - bottom: symbolExtent.bottom │ │ │ │ - }; │ │ │ │ - this.symbolCache[id] = cache; │ │ │ │ - return cache │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer.VML" │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.HTTP" │ │ │ │ }); │ │ │ │ -OpenLayers.Renderer.VML.LABEL_SHIFT = { │ │ │ │ - l: 0, │ │ │ │ - c: .5, │ │ │ │ - r: 1, │ │ │ │ - t: 0, │ │ │ │ - m: .5, │ │ │ │ - b: 1 │ │ │ │ -}; │ │ │ │ OpenLayers.Format = OpenLayers.Class({ │ │ │ │ options: null, │ │ │ │ externalProjection: null, │ │ │ │ internalProjection: null, │ │ │ │ data: null, │ │ │ │ keepData: false, │ │ │ │ initialize: function(options) { │ │ │ │ @@ -12757,1410 +8343,3579 @@ │ │ │ │ } │ │ │ │ return new OpenLayers.Geometry.Point(x, y) │ │ │ │ } else { │ │ │ │ return null │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ - getArea: function() { │ │ │ │ - var area = 0; │ │ │ │ - if (this.components && this.components.length > 2) { │ │ │ │ - var sum = 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 │ │ │ │ - } │ │ │ │ - return area │ │ │ │ + getArea: function() { │ │ │ │ + var area = 0; │ │ │ │ + if (this.components && this.components.length > 2) { │ │ │ │ + var sum = 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 │ │ │ │ + } │ │ │ │ + return area │ │ │ │ + }, │ │ │ │ + getGeodesicArea: function(projection) { │ │ │ │ + var ring = this; │ │ │ │ + if (projection) { │ │ │ │ + var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + if (!gg.equals(projection)) { │ │ │ │ + ring = this.clone().transform(projection, gg) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var area = 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 * 6378137 / 2 │ │ │ │ + } │ │ │ │ + return area │ │ │ │ + }, │ │ │ │ + containsPoint: function(point) { │ │ │ │ + var approx = OpenLayers.Number.limitSigDigs; │ │ │ │ + var digs = 14; │ │ │ │ + var px = approx(point.x, digs); │ │ │ │ + var py = approx(point.y, digs); │ │ │ │ + │ │ │ │ + function getX(y, x1, y1, x2, y2) { │ │ │ │ + return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2 │ │ │ │ + } │ │ │ │ + var numSeg = this.components.length - 1; │ │ │ │ + var start, end, x1, y1, x2, y2, cx, cy; │ │ │ │ + var crosses = 0; │ │ │ │ + for (var i = 0; i < numSeg; ++i) { │ │ │ │ + start = this.components[i]; │ │ │ │ + x1 = approx(start.x, digs); │ │ │ │ + y1 = approx(start.y, digs); │ │ │ │ + end = this.components[i + 1]; │ │ │ │ + x2 = approx(end.x, digs); │ │ │ │ + y2 = approx(end.y, digs); │ │ │ │ + if (y1 == y2) { │ │ │ │ + if (py == y1) { │ │ │ │ + if (x1 <= x2 && (px >= x1 && px <= x2) || x1 >= x2 && (px <= x1 && px >= x2)) { │ │ │ │ + crosses = -1; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + continue │ │ │ │ + } │ │ │ │ + cx = approx(getX(py, x1, y1, x2, y2), digs); │ │ │ │ + if (cx == px) { │ │ │ │ + if (y1 < y2 && (py >= y1 && py <= y2) || y1 > y2 && (py <= y1 && py >= y2)) { │ │ │ │ + crosses = -1; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (cx <= px) { │ │ │ │ + continue │ │ │ │ + } │ │ │ │ + if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) { │ │ │ │ + continue │ │ │ │ + } │ │ │ │ + if (y1 < y2 && (py >= y1 && py < y2) || y1 > y2 && (py < y1 && py >= y2)) { │ │ │ │ + ++crosses │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var contained = crosses == -1 ? 1 : !!(crosses & 1); │ │ │ │ + return contained │ │ │ │ + }, │ │ │ │ + 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 { │ │ │ │ + for (var i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ + intersect = geometry.components[i].intersects(this); │ │ │ │ + if (intersect) { │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return intersect │ │ │ │ + }, │ │ │ │ + getVertices: function(nodes) { │ │ │ │ + return nodes === true ? [] : this.components.slice(0, this.components.length - 1) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.LinearRing" │ │ │ │ +}); │ │ │ │ +OpenLayers.Geometry.Polygon = OpenLayers.Class(OpenLayers.Geometry.Collection, { │ │ │ │ + componentTypes: ["OpenLayers.Geometry.LinearRing"], │ │ │ │ + getArea: function() { │ │ │ │ + var area = 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 │ │ │ │ + }, │ │ │ │ + getGeodesicArea: function(projection) { │ │ │ │ + var area = 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 │ │ │ │ + }, │ │ │ │ + containsPoint: function(point) { │ │ │ │ + var numRings = this.components.length; │ │ │ │ + var contained = false; │ │ │ │ + if (numRings > 0) { │ │ │ │ + contained = this.components[0].containsPoint(point); │ │ │ │ + if (contained !== 1) { │ │ │ │ + if (contained && numRings > 1) { │ │ │ │ + var hole; │ │ │ │ + for (var i = 1; i < numRings; ++i) { │ │ │ │ + hole = this.components[i].containsPoint(point); │ │ │ │ + if (hole) { │ │ │ │ + if (hole === 1) { │ │ │ │ + contained = 1 │ │ │ │ + } else { │ │ │ │ + contained = false │ │ │ │ + } │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return contained │ │ │ │ + }, │ │ │ │ + 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") { │ │ │ │ + for (i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ + intersect = geometry.intersects(this.components[i]); │ │ │ │ + if (intersect) { │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (!intersect) { │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") { │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + distanceTo: function(geometry, options) { │ │ │ │ + var edge = !(options && options.edge === false); │ │ │ │ + var result; │ │ │ │ + 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" │ │ │ │ +}); │ │ │ │ +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 = OpenLayers.Class(OpenLayers.Geometry.Collection, { │ │ │ │ + componentTypes: ["OpenLayers.Geometry.Polygon"], │ │ │ │ + CLASS_NAME: "OpenLayers.Geometry.MultiPolygon" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, { │ │ │ │ + ignoreExtraDims: false, │ │ │ │ + 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": │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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) { │ │ │ │ + OpenLayers.Console.error("Unsupported geometry type: " + obj.type) │ │ │ │ + } else { │ │ │ │ + valid = true │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case "FeatureCollection": │ │ │ │ + valid = true; │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + if (obj.type == type) { │ │ │ │ + valid = true │ │ │ │ + } else { │ │ │ │ + OpenLayers.Console.error("Cannot convert types from " + obj.type + " to " + type) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return valid │ │ │ │ + }, │ │ │ │ + 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) { │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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) { │ │ │ │ + throw err │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (this.internalProjection && this.externalProjection && !collection) { │ │ │ │ + geometry.transform(this.externalProjection, this.internalProjection) │ │ │ │ + } │ │ │ │ + return geometry │ │ │ │ + }, │ │ │ │ + parseCoords: { │ │ │ │ + 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]) │ │ │ │ + }, │ │ │ │ + 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) │ │ │ │ + }, │ │ │ │ + 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) │ │ │ │ + }, │ │ │ │ + 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) │ │ │ │ + }, │ │ │ │ + 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) │ │ │ │ + }, │ │ │ │ + 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) │ │ │ │ + }, │ │ │ │ + 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])])]) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + 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]) │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + extract: { │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + geometry: function(geometry) { │ │ │ │ + if (geometry == null) { │ │ │ │ + return null │ │ │ │ + } │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry = geometry.clone(); │ │ │ │ + geometry.transform(this.internalProjection, this.externalProjection) │ │ │ │ + } │ │ │ │ + var geometryType = geometry.CLASS_NAME.split(".")[2]; │ │ │ │ + var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]); │ │ │ │ + var json; │ │ │ │ + if (geometryType == "Collection") { │ │ │ │ + json = { │ │ │ │ + type: "GeometryCollection", │ │ │ │ + geometries: data │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + json = { │ │ │ │ + type: geometryType, │ │ │ │ + coordinates: data │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return json │ │ │ │ + }, │ │ │ │ + point: function(point) { │ │ │ │ + return [point.x, point.y] │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + collection: function(collection) { │ │ │ │ + var len = collection.components.length; │ │ │ │ + var array = new Array(len); │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ + array[i] = this.extract.geometry.apply(this, [collection.components[i]]) │ │ │ │ + } │ │ │ │ + return array │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.GeoJSON" │ │ │ │ +}); │ │ │ │ +OpenLayers.Layer = OpenLayers.Class({ │ │ │ │ + id: null, │ │ │ │ + name: null, │ │ │ │ + div: null, │ │ │ │ + opacity: 1, │ │ │ │ + alwaysInRange: null, │ │ │ │ + RESOLUTION_PROPERTIES: ["scales", "resolutions", "maxScale", "minScale", "maxResolution", "minResolution", "numZoomLevels", "maxZoomLevel"], │ │ │ │ + events: null, │ │ │ │ + map: null, │ │ │ │ + isBaseLayer: false, │ │ │ │ + alpha: false, │ │ │ │ + displayInLayerSwitcher: true, │ │ │ │ + visibility: true, │ │ │ │ + attribution: null, │ │ │ │ + inRange: false, │ │ │ │ + imageSize: null, │ │ │ │ + options: null, │ │ │ │ + eventListeners: null, │ │ │ │ + gutter: 0, │ │ │ │ + projection: null, │ │ │ │ + units: null, │ │ │ │ + scales: null, │ │ │ │ + resolutions: null, │ │ │ │ + maxExtent: null, │ │ │ │ + minExtent: null, │ │ │ │ + maxResolution: null, │ │ │ │ + minResolution: null, │ │ │ │ + numZoomLevels: null, │ │ │ │ + minScale: null, │ │ │ │ + maxScale: null, │ │ │ │ + displayOutsideMaxExtent: false, │ │ │ │ + wrapDateLine: false, │ │ │ │ + metadata: null, │ │ │ │ + initialize: function(name, options) { │ │ │ │ + this.metadata = {}; │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function(setNewBaseLayer) { │ │ │ │ + if (setNewBaseLayer == null) { │ │ │ │ + setNewBaseLayer = true │ │ │ │ + } │ │ │ │ + if (this.map != null) { │ │ │ │ + this.map.removeLayer(this, setNewBaseLayer) │ │ │ │ + } │ │ │ │ + this.projection = null; │ │ │ │ + this.map = null; │ │ │ │ + this.name = null; │ │ │ │ + this.div = null; │ │ │ │ + this.options = null; │ │ │ │ + if (this.events) { │ │ │ │ + if (this.eventListeners) { │ │ │ │ + this.events.un(this.eventListeners) │ │ │ │ + } │ │ │ │ + this.events.destroy() │ │ │ │ + } │ │ │ │ + this.eventListeners = null; │ │ │ │ + this.events = null │ │ │ │ + }, │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer(this.name, this.getOptions()) │ │ │ │ + } │ │ │ │ + OpenLayers.Util.applyDefaults(obj, this); │ │ │ │ + obj.map = null; │ │ │ │ + return obj │ │ │ │ + }, │ │ │ │ + getOptions: function() { │ │ │ │ + var options = {}; │ │ │ │ + for (var o in this.options) { │ │ │ │ + options[o] = this[o] │ │ │ │ + } │ │ │ │ + return options │ │ │ │ + }, │ │ │ │ + setName: function(newName) { │ │ │ │ + if (newName != this.name) { │ │ │ │ + this.name = newName; │ │ │ │ + if (this.map != null) { │ │ │ │ + this.map.events.triggerEvent("changelayer", { │ │ │ │ + layer: this, │ │ │ │ + property: "name" │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + addOptions: function(newOptions, reinitialize) { │ │ │ │ + if (this.options == null) { │ │ │ │ + this.options = {} │ │ │ │ + } │ │ │ │ + if (newOptions) { │ │ │ │ + if (typeof newOptions.projection == "string") { │ │ │ │ + newOptions.projection = new OpenLayers.Projection(newOptions.projection) │ │ │ │ + } │ │ │ │ + if (newOptions.projection) { │ │ │ │ + OpenLayers.Util.applyDefaults(newOptions, OpenLayers.Projection.defaults[newOptions.projection.getCode()]) │ │ │ │ + } │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + OpenLayers.Util.extend(this.options, newOptions); │ │ │ │ + OpenLayers.Util.extend(this, newOptions); │ │ │ │ + if (this.projection && this.projection.getUnits()) { │ │ │ │ + this.units = this.projection.getUnits() │ │ │ │ + } │ │ │ │ + if (this.map) { │ │ │ │ + var resolution = this.map.getResolution(); │ │ │ │ + var properties = this.RESOLUTION_PROPERTIES.concat(["projection", "units", "minExtent", "maxExtent"]); │ │ │ │ + for (var o in newOptions) { │ │ │ │ + if (newOptions.hasOwnProperty(o) && OpenLayers.Util.indexOf(properties, o) >= 0) { │ │ │ │ + this.initResolutions(); │ │ │ │ + if (reinitialize && this.map.baseLayer === this) { │ │ │ │ + this.map.setCenter(this.map.getCenter(), this.map.getZoomForResolution(resolution), false, true); │ │ │ │ + this.map.events.triggerEvent("changebaselayer", { │ │ │ │ + layer: this │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + onMapResize: function() {}, │ │ │ │ + redraw: function() { │ │ │ │ + var redrawn = false; │ │ │ │ + if (this.map) { │ │ │ │ + this.inRange = this.calculateInRange(); │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + var display = this.visibility; │ │ │ │ + if (!this.isBaseLayer) { │ │ │ │ + display = display && this.inRange │ │ │ │ + } │ │ │ │ + this.display(display) │ │ │ │ + }, │ │ │ │ + moveByPx: function(dx, dy) {}, │ │ │ │ + setMap: function(map) { │ │ │ │ + if (this.map == null) { │ │ │ │ + this.map = map; │ │ │ │ + this.maxExtent = this.maxExtent || this.map.maxExtent; │ │ │ │ + this.minExtent = this.minExtent || this.map.minExtent; │ │ │ │ + this.projection = this.projection || this.map.projection; │ │ │ │ + if (typeof this.projection == "string") { │ │ │ │ + this.projection = new OpenLayers.Projection(this.projection) │ │ │ │ + } │ │ │ │ + this.units = this.projection.getUnits() || this.units || this.map.units; │ │ │ │ + this.initResolutions(); │ │ │ │ + if (!this.isBaseLayer) { │ │ │ │ + this.inRange = this.calculateInRange(); │ │ │ │ + var show = this.visibility && this.inRange; │ │ │ │ + this.div.style.display = show ? "" : "none" │ │ │ │ + } │ │ │ │ + this.setTileSize() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + afterAdd: function() {}, │ │ │ │ + removeMap: function(map) {}, │ │ │ │ + getImageSize: function(bounds) { │ │ │ │ + return this.imageSize || this.tileSize │ │ │ │ + }, │ │ │ │ + setTileSize: function(size) { │ │ │ │ + var tileSize = size ? size : this.tileSize ? this.tileSize : this.map.getTileSize(); │ │ │ │ + this.tileSize = tileSize; │ │ │ │ + if (this.gutter) { │ │ │ │ + this.imageSize = new OpenLayers.Size(tileSize.w + 2 * this.gutter, tileSize.h + 2 * this.gutter) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + getVisibility: function() { │ │ │ │ + return this.visibility │ │ │ │ + }, │ │ │ │ + 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") │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + display: function(display) { │ │ │ │ + if (display != (this.div.style.display != "none")) { │ │ │ │ + this.div.style.display = display && this.calculateInRange() ? "block" : "none" │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + calculateInRange: function() { │ │ │ │ + var inRange = false; │ │ │ │ + if (this.alwaysInRange) { │ │ │ │ + inRange = true │ │ │ │ + } else { │ │ │ │ + if (this.map) { │ │ │ │ + var resolution = this.map.getResolution(); │ │ │ │ + inRange = resolution >= this.minResolution && resolution <= this.maxResolution │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return inRange │ │ │ │ + }, │ │ │ │ + setIsBaseLayer: function(isBaseLayer) { │ │ │ │ + if (isBaseLayer != this.isBaseLayer) { │ │ │ │ + this.isBaseLayer = isBaseLayer; │ │ │ │ + if (this.map != null) { │ │ │ │ + this.map.events.triggerEvent("changebaselayer", { │ │ │ │ + layer: this │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + initResolutions: function() { │ │ │ │ + var i, len, p; │ │ │ │ + var props = {}, │ │ │ │ + alwaysInRange = true; │ │ │ │ + for (i = 0, len = this.RESOLUTION_PROPERTIES.length; i < len; i++) { │ │ │ │ + p = this.RESOLUTION_PROPERTIES[i]; │ │ │ │ + props[p] = this.options[p]; │ │ │ │ + if (alwaysInRange && this.options[p]) { │ │ │ │ + alwaysInRange = false │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (this.options.alwaysInRange == null) { │ │ │ │ + this.alwaysInRange = alwaysInRange │ │ │ │ + } │ │ │ │ + if (props.resolutions == null) { │ │ │ │ + props.resolutions = this.resolutionsFromScales(props.scales) │ │ │ │ + } │ │ │ │ + if (props.resolutions == null) { │ │ │ │ + props.resolutions = this.calculateResolutions(props) │ │ │ │ + } │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + var minResolution; │ │ │ │ + if (this.options.minResolution && this.options.minResolution !== "auto") { │ │ │ │ + minResolution = this.options.minResolution │ │ │ │ + } │ │ │ │ + if (this.options.maxScale) { │ │ │ │ + minResolution = OpenLayers.Util.getResolutionFromScale(this.options.maxScale, this.units) │ │ │ │ + } │ │ │ │ + if (props.resolutions) { │ │ │ │ + props.resolutions.sort(function(a, b) { │ │ │ │ + return b - a │ │ │ │ + }); │ │ │ │ + if (!maxResolution) { │ │ │ │ + maxResolution = props.resolutions[0] │ │ │ │ + } │ │ │ │ + if (!minResolution) { │ │ │ │ + var lastIdx = props.resolutions.length - 1; │ │ │ │ + minResolution = props.resolutions[lastIdx] │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.resolutions = props.resolutions; │ │ │ │ + if (this.resolutions) { │ │ │ │ + len = this.resolutions.length; │ │ │ │ + this.scales = new Array(len); │ │ │ │ + for (i = 0; i < len; i++) { │ │ │ │ + this.scales[i] = OpenLayers.Util.getScaleFromResolution(this.resolutions[i], this.units) │ │ │ │ + } │ │ │ │ + this.numZoomLevels = len │ │ │ │ + } │ │ │ │ + this.minResolution = minResolution; │ │ │ │ + if (minResolution) { │ │ │ │ + this.maxScale = OpenLayers.Util.getScaleFromResolution(minResolution, this.units) │ │ │ │ + } │ │ │ │ + this.maxResolution = maxResolution; │ │ │ │ + if (maxResolution) { │ │ │ │ + this.minScale = OpenLayers.Util.getScaleFromResolution(maxResolution, this.units) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + calculateResolutions: function(props) { │ │ │ │ + var viewSize, wRes, hRes; │ │ │ │ + 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 minResolution = props.minResolution; │ │ │ │ + if (props.maxScale != null) { │ │ │ │ + minResolution = OpenLayers.Util.getResolutionFromScale(props.maxScale, this.units) │ │ │ │ + } else if (props.minResolution == "auto" && this.minExtent != null) { │ │ │ │ + viewSize = this.map.getSize(); │ │ │ │ + wRes = this.minExtent.getWidth() / viewSize.w; │ │ │ │ + hRes = this.minExtent.getHeight() / viewSize.h; │ │ │ │ + minResolution = Math.max(wRes, hRes) │ │ │ │ + } │ │ │ │ + if (typeof maxResolution !== "number" && typeof minResolution !== "number" && this.maxExtent != null) { │ │ │ │ + var tileSize = this.map.getTileSize(); │ │ │ │ + maxResolution = Math.max(this.maxExtent.getWidth() / tileSize.w, this.maxExtent.getHeight() / tileSize.h) │ │ │ │ + } │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ + if (typeof numZoomLevels !== "number" || numZoomLevels <= 0 || typeof maxResolution !== "number" && typeof minResolution !== "number") { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + var resolutions = new Array(numZoomLevels); │ │ │ │ + var base = 2; │ │ │ │ + if (typeof minResolution == "number" && typeof maxResolution == "number") { │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + getResolution: function() { │ │ │ │ + var zoom = this.map.getZoom(); │ │ │ │ + return this.getResolutionForZoom(zoom) │ │ │ │ + }, │ │ │ │ + getExtent: function() { │ │ │ │ + return this.map.calculateBounds() │ │ │ │ + }, │ │ │ │ + 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) │ │ │ │ + }, │ │ │ │ + getDataExtent: function() {}, │ │ │ │ + getResolutionForZoom: function(zoom) { │ │ │ │ + zoom = Math.max(0, Math.min(zoom, this.resolutions.length - 1)); │ │ │ │ + var resolution; │ │ │ │ + if (this.map.fractionalZoom) { │ │ │ │ + var low = Math.floor(zoom); │ │ │ │ + var high = Math.ceil(zoom); │ │ │ │ + resolution = this.resolutions[low] - (zoom - low) * (this.resolutions[low] - this.resolutions[high]) │ │ │ │ + } else { │ │ │ │ + resolution = this.resolutions[Math.round(zoom)] │ │ │ │ + } │ │ │ │ + return resolution │ │ │ │ + }, │ │ │ │ + getZoomForResolution: function(resolution, closest) { │ │ │ │ + var zoom, i, len; │ │ │ │ + if (this.map.fractionalZoom) { │ │ │ │ + var lowZoom = 0; │ │ │ │ + var highZoom = this.resolutions.length - 1; │ │ │ │ + var highRes = this.resolutions[lowZoom]; │ │ │ │ + var lowRes = this.resolutions[highZoom]; │ │ │ │ + var res; │ │ │ │ + for (i = 0, len = this.resolutions.length; i < len; ++i) { │ │ │ │ + res = this.resolutions[i]; │ │ │ │ + if (res >= resolution) { │ │ │ │ + highRes = res; │ │ │ │ + lowZoom = i │ │ │ │ + } │ │ │ │ + if (res <= resolution) { │ │ │ │ + lowRes = res; │ │ │ │ + highZoom = i; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var dRes = highRes - lowRes; │ │ │ │ + if (dRes > 0) { │ │ │ │ + zoom = lowZoom + (highRes - resolution) / dRes │ │ │ │ + } else { │ │ │ │ + zoom = lowZoom │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + var diff; │ │ │ │ + var minDiff = Number.POSITIVE_INFINITY; │ │ │ │ + for (i = 0, len = this.resolutions.length; i < len; i++) { │ │ │ │ + if (closest) { │ │ │ │ + diff = Math.abs(this.resolutions[i] - resolution); │ │ │ │ + if (diff > minDiff) { │ │ │ │ + break │ │ │ │ + } │ │ │ │ + minDiff = diff │ │ │ │ + } else { │ │ │ │ + if (this.resolutions[i] < resolution) { │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + zoom = Math.max(0, i - 1) │ │ │ │ + } │ │ │ │ + return zoom │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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; │ │ │ │ + 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" │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + getZIndex: function() { │ │ │ │ + return this.div.style.zIndex │ │ │ │ + }, │ │ │ │ + setZIndex: function(zIndex) { │ │ │ │ + this.div.style.zIndex = zIndex │ │ │ │ + }, │ │ │ │ + adjustBounds: function(bounds) { │ │ │ │ + if (this.gutter) { │ │ │ │ + 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) { │ │ │ │ + var wrappingOptions = { │ │ │ │ + rightTolerance: this.getResolution(), │ │ │ │ + leftTolerance: this.getResolution() │ │ │ │ + }; │ │ │ │ + bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions) │ │ │ │ + } │ │ │ │ + return bounds │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Layer" │ │ │ │ +}); │ │ │ │ +OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ + isBaseLayer: false, │ │ │ │ + isFixed: false, │ │ │ │ + features: null, │ │ │ │ + filter: null, │ │ │ │ + selectedFeatures: null, │ │ │ │ + unrenderedFeatures: null, │ │ │ │ + reportError: true, │ │ │ │ + style: null, │ │ │ │ + styleMap: null, │ │ │ │ + strategies: null, │ │ │ │ + protocol: null, │ │ │ │ + renderers: ["SVG", "VML", "Canvas"], │ │ │ │ + renderer: null, │ │ │ │ + rendererOptions: null, │ │ │ │ + geometryType: null, │ │ │ │ + drawn: false, │ │ │ │ + ratio: 1, │ │ │ │ + initialize: function(name, options) { │ │ │ │ + OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ + if (!this.renderer || !this.renderer.supported()) { │ │ │ │ + this.assignRenderer() │ │ │ │ + } │ │ │ │ + if (!this.renderer || !this.renderer.supported()) { │ │ │ │ + this.renderer = null; │ │ │ │ + this.displayError() │ │ │ │ + } │ │ │ │ + if (!this.styleMap) { │ │ │ │ + this.styleMap = new OpenLayers.StyleMap │ │ │ │ + } │ │ │ │ + this.features = []; │ │ │ │ + this.selectedFeatures = []; │ │ │ │ + this.unrenderedFeatures = {}; │ │ │ │ + if (this.strategies) { │ │ │ │ + for (var i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ + this.strategies[i].setLayer(this) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + if (this.strategies) { │ │ │ │ + var strategy, i, len; │ │ │ │ + for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ + strategy = this.strategies[i]; │ │ │ │ + if (strategy.autoDestroy) { │ │ │ │ + strategy.destroy() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.strategies = null │ │ │ │ + } │ │ │ │ + if (this.protocol) { │ │ │ │ + if (this.protocol.autoDestroy) { │ │ │ │ + this.protocol.destroy() │ │ │ │ + } │ │ │ │ + this.protocol = null │ │ │ │ + } │ │ │ │ + this.destroyFeatures(); │ │ │ │ + this.features = null; │ │ │ │ + this.selectedFeatures = null; │ │ │ │ + this.unrenderedFeatures = null; │ │ │ │ + if (this.renderer) { │ │ │ │ + this.renderer.destroy() │ │ │ │ + } │ │ │ │ + this.renderer = null; │ │ │ │ + this.geometryType = null; │ │ │ │ + this.drawn = null; │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.Vector(this.name, this.getOptions()) │ │ │ │ + } │ │ │ │ + obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ + var features = this.features; │ │ │ │ + var len = features.length; │ │ │ │ + var clonedFeatures = new Array(len); │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ + clonedFeatures[i] = features[i].clone() │ │ │ │ + } │ │ │ │ + obj.features = clonedFeatures; │ │ │ │ + return obj │ │ │ │ + }, │ │ │ │ + refresh: function(obj) { │ │ │ │ + if (this.calculateInRange() && this.visibility) { │ │ │ │ + this.events.triggerEvent("refresh", obj) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + assignRenderer: function() { │ │ │ │ + for (var i = 0, len = this.renderers.length; i < len; i++) { │ │ │ │ + var rendererClass = this.renderers[i]; │ │ │ │ + var renderer = typeof rendererClass == "function" ? rendererClass : OpenLayers.Renderer[rendererClass]; │ │ │ │ + if (renderer && renderer.prototype.supported()) { │ │ │ │ + this.renderer = new renderer(this.div, this.rendererOptions); │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + displayError: function() { │ │ │ │ + if (this.reportError) { │ │ │ │ + OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", { │ │ │ │ + renderers: this.renderers.join("\n") │ │ │ │ + })) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ + if (!this.renderer) { │ │ │ │ + this.map.removeLayer(this) │ │ │ │ + } else { │ │ │ │ + this.renderer.map = this.map; │ │ │ │ + var newSize = this.map.getSize(); │ │ │ │ + newSize.w = newSize.w * this.ratio; │ │ │ │ + newSize.h = newSize.h * this.ratio; │ │ │ │ + this.renderer.setSize(newSize) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + afterAdd: function() { │ │ │ │ + if (this.strategies) { │ │ │ │ + var strategy, i, len; │ │ │ │ + for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ + strategy = this.strategies[i]; │ │ │ │ + if (strategy.autoActivate) { │ │ │ │ + strategy.activate() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + removeMap: function(map) { │ │ │ │ + this.drawn = false; │ │ │ │ + if (this.strategies) { │ │ │ │ + var strategy, i, len; │ │ │ │ + for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ + strategy = this.strategies[i]; │ │ │ │ + if (strategy.autoActivate) { │ │ │ │ + strategy.deactivate() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + onMapResize: function() { │ │ │ │ + OpenLayers.Layer.prototype.onMapResize.apply(this, arguments); │ │ │ │ + var newSize = this.map.getSize(); │ │ │ │ + newSize.w = newSize.w * this.ratio; │ │ │ │ + newSize.h = newSize.h * this.ratio; │ │ │ │ + this.renderer.setSize(newSize) │ │ │ │ + }, │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ + var coordSysUnchanged = true; │ │ │ │ + if (!dragging) { │ │ │ │ + this.renderer.root.style.visibility = "hidden"; │ │ │ │ + var viewSize = this.map.getSize(), │ │ │ │ + viewWidth = viewSize.w, │ │ │ │ + viewHeight = viewSize.h, │ │ │ │ + offsetLeft = viewWidth / 2 * this.ratio - viewWidth / 2, │ │ │ │ + offsetTop = viewHeight / 2 * this.ratio - viewHeight / 2; │ │ │ │ + offsetLeft += this.map.layerContainerOriginPx.x; │ │ │ │ + offsetLeft = -Math.round(offsetLeft); │ │ │ │ + offsetTop += this.map.layerContainerOriginPx.y; │ │ │ │ + offsetTop = -Math.round(offsetTop); │ │ │ │ + this.div.style.left = offsetLeft + "px"; │ │ │ │ + this.div.style.top = offsetTop + "px"; │ │ │ │ + var extent = this.map.getExtent().scale(this.ratio); │ │ │ │ + coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged); │ │ │ │ + this.renderer.root.style.visibility = "visible"; │ │ │ │ + if (OpenLayers.IS_GECKO === true) { │ │ │ │ + this.div.scrollLeft = this.div.scrollLeft │ │ │ │ + } │ │ │ │ + if (!zoomChanged && coordSysUnchanged) { │ │ │ │ + for (var i in this.unrenderedFeatures) { │ │ │ │ + var feature = this.unrenderedFeatures[i]; │ │ │ │ + this.drawFeature(feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (!this.drawn || zoomChanged || !coordSysUnchanged) { │ │ │ │ + this.drawn = true; │ │ │ │ + var feature; │ │ │ │ + for (var i = 0, len = this.features.length; i < len; i++) { │ │ │ │ + this.renderer.locked = i !== len - 1; │ │ │ │ + feature = this.features[i]; │ │ │ │ + this.drawFeature(feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + display: function(display) { │ │ │ │ + OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ + var currentDisplay = this.div.style.display; │ │ │ │ + if (currentDisplay != this.renderer.root.style.display) { │ │ │ │ + this.renderer.root.style.display = currentDisplay │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + addFeatures: function(features, options) { │ │ │ │ + if (!OpenLayers.Util.isArray(features)) { │ │ │ │ + features = [features] │ │ │ │ + } │ │ │ │ + var notify = !options || !options.silent; │ │ │ │ + if (notify) { │ │ │ │ + var event = { │ │ │ │ + features: features │ │ │ │ + }; │ │ │ │ + var ret = this.events.triggerEvent("beforefeaturesadded", event); │ │ │ │ + if (ret === false) { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + features = event.features │ │ │ │ + } │ │ │ │ + var featuresAdded = []; │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ + if (i != features.length - 1) { │ │ │ │ + this.renderer.locked = true │ │ │ │ + } else { │ │ │ │ + this.renderer.locked = false │ │ │ │ + } │ │ │ │ + var feature = features[i]; │ │ │ │ + if (this.geometryType && !(feature.geometry instanceof this.geometryType)) { │ │ │ │ + throw new TypeError("addFeatures: component should be an " + this.geometryType.prototype.CLASS_NAME) │ │ │ │ + } │ │ │ │ + feature.layer = this; │ │ │ │ + if (!feature.style && this.style) { │ │ │ │ + feature.style = OpenLayers.Util.extend({}, this.style) │ │ │ │ + } │ │ │ │ + if (notify) { │ │ │ │ + if (this.events.triggerEvent("beforefeatureadded", { │ │ │ │ + feature: feature │ │ │ │ + }) === false) { │ │ │ │ + continue │ │ │ │ + } │ │ │ │ + this.preFeatureInsert(feature) │ │ │ │ + } │ │ │ │ + featuresAdded.push(feature); │ │ │ │ + this.features.push(feature); │ │ │ │ + this.drawFeature(feature); │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featureadded", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + this.onFeatureInsert(feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featuresadded", { │ │ │ │ + features: featuresAdded │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + removeFeatures: function(features, options) { │ │ │ │ + if (!features || features.length === 0) { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + if (features === this.features) { │ │ │ │ + return this.removeAllFeatures(options) │ │ │ │ + } │ │ │ │ + if (!OpenLayers.Util.isArray(features)) { │ │ │ │ + features = [features] │ │ │ │ + } │ │ │ │ + if (features === this.selectedFeatures) { │ │ │ │ + features = features.slice() │ │ │ │ + } │ │ │ │ + var notify = !options || !options.silent; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("beforefeaturesremoved", { │ │ │ │ + features: features │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ + if (i != 0 && features[i - 1].geometry) { │ │ │ │ + this.renderer.locked = true │ │ │ │ + } else { │ │ │ │ + this.renderer.locked = false │ │ │ │ + } │ │ │ │ + var feature = features[i]; │ │ │ │ + delete this.unrenderedFeatures[feature.id]; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ + feature: feature │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + this.features = OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ + feature.layer = null; │ │ │ │ + if (feature.geometry) { │ │ │ │ + this.renderer.eraseFeatures(feature) │ │ │ │ + } │ │ │ │ + if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) { │ │ │ │ + OpenLayers.Util.removeItem(this.selectedFeatures, feature) │ │ │ │ + } │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featureremoved", { │ │ │ │ + feature: feature │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featuresremoved", { │ │ │ │ + features: features │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + removeAllFeatures: function(options) { │ │ │ │ + var notify = !options || !options.silent; │ │ │ │ + var features = this.features; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("beforefeaturesremoved", { │ │ │ │ + features: features │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + var feature; │ │ │ │ + for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ + feature = features[i]; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ + feature: feature │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + feature.layer = null; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featureremoved", { │ │ │ │ + feature: feature │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.renderer.clear(); │ │ │ │ + this.features = []; │ │ │ │ + this.unrenderedFeatures = {}; │ │ │ │ + this.selectedFeatures = []; │ │ │ │ + if (notify) { │ │ │ │ + this.events.triggerEvent("featuresremoved", { │ │ │ │ + features: features │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroyFeatures: function(features, options) { │ │ │ │ + var all = features == undefined; │ │ │ │ + if (all) { │ │ │ │ + features = this.features │ │ │ │ + } │ │ │ │ + if (features) { │ │ │ │ + this.removeFeatures(features, options); │ │ │ │ + for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ + features[i].destroy() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + drawFeature: function(feature, style) { │ │ │ │ + if (!this.drawn) { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + if (typeof style != "object") { │ │ │ │ + if (!style && feature.state === OpenLayers.State.DELETE) { │ │ │ │ + style = "delete" │ │ │ │ + } │ │ │ │ + var renderIntent = style || feature.renderIntent; │ │ │ │ + style = feature.style || this.style; │ │ │ │ + if (!style) { │ │ │ │ + style = this.styleMap.createSymbolizer(feature, renderIntent) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var drawn = this.renderer.drawFeature(feature, style); │ │ │ │ + if (drawn === false || drawn === null) { │ │ │ │ + this.unrenderedFeatures[feature.id] = feature │ │ │ │ + } else { │ │ │ │ + delete this.unrenderedFeatures[feature.id] │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + eraseFeatures: function(features) { │ │ │ │ + this.renderer.eraseFeatures(features) │ │ │ │ + }, │ │ │ │ + getFeatureFromEvent: function(evt) { │ │ │ │ + if (!this.renderer) { │ │ │ │ + throw new Error("getFeatureFromEvent called on layer with no " + "renderer. This usually means you destroyed a " + "layer, but not some handler which is associated " + "with it.") │ │ │ │ + } │ │ │ │ + var feature = null; │ │ │ │ + var featureId = this.renderer.getFeatureIdFromEvent(evt); │ │ │ │ + if (featureId) { │ │ │ │ + if (typeof featureId === "string") { │ │ │ │ + feature = this.getFeatureById(featureId) │ │ │ │ + } else { │ │ │ │ + feature = featureId │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return feature │ │ │ │ + }, │ │ │ │ + getFeatureBy: function(property, value) { │ │ │ │ + var feature = null; │ │ │ │ + for (var i = 0, len = this.features.length; i < len; ++i) { │ │ │ │ + if (this.features[i][property] == value) { │ │ │ │ + feature = this.features[i]; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return feature │ │ │ │ + }, │ │ │ │ + getFeatureById: function(featureId) { │ │ │ │ + return this.getFeatureBy("id", featureId) │ │ │ │ + }, │ │ │ │ + getFeatureByFid: function(featureFid) { │ │ │ │ + return this.getFeatureBy("fid", featureFid) │ │ │ │ + }, │ │ │ │ + getFeaturesByAttribute: function(attrName, attrValue) { │ │ │ │ + var i, feature, len = this.features.length, │ │ │ │ + foundFeatures = []; │ │ │ │ + for (i = 0; i < len; i++) { │ │ │ │ + feature = this.features[i]; │ │ │ │ + if (feature && feature.attributes) { │ │ │ │ + if (feature.attributes[attrName] === attrValue) { │ │ │ │ + foundFeatures.push(feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return foundFeatures │ │ │ │ + }, │ │ │ │ + onFeatureInsert: function(feature) {}, │ │ │ │ + preFeatureInsert: function(feature) {}, │ │ │ │ + getDataExtent: function() { │ │ │ │ + var maxExtent = null; │ │ │ │ + var features = this.features; │ │ │ │ + if (features && features.length > 0) { │ │ │ │ + var geometry = null; │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ + geometry = features[i].geometry; │ │ │ │ + if (geometry) { │ │ │ │ + if (maxExtent === null) { │ │ │ │ + maxExtent = new OpenLayers.Bounds │ │ │ │ + } │ │ │ │ + maxExtent.extend(geometry.getBounds()) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return maxExtent │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Vector" │ │ │ │ +}); │ │ │ │ +OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ + URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2, │ │ │ │ + url: null, │ │ │ │ + params: null, │ │ │ │ + reproject: false, │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.url = null; │ │ │ │ + this.params = null; │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.HTTPRequest(this.name, this.url, this.params, this.getOptions()) │ │ │ │ + } │ │ │ │ + obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ + return obj │ │ │ │ + }, │ │ │ │ + setUrl: function(newUrl) { │ │ │ │ + this.url = newUrl │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + redraw: function(force) { │ │ │ │ + if (force) { │ │ │ │ + return this.mergeNewParams({ │ │ │ │ + _olSalt: Math.random() │ │ │ │ + }) │ │ │ │ + } else { │ │ │ │ + return OpenLayers.Layer.prototype.redraw.apply(this, []) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + 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)] │ │ │ │ + }, │ │ │ │ + getFullRequestString: function(newParams, altUrl) { │ │ │ │ + var url = altUrl || this.url; │ │ │ │ + var allParams = OpenLayers.Util.extend({}, this.params); │ │ │ │ + allParams = OpenLayers.Util.extend(allParams, newParams); │ │ │ │ + var paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ + if (OpenLayers.Util.isArray(url)) { │ │ │ │ + url = this.selectUrl(paramsString, url) │ │ │ │ + } │ │ │ │ + var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url)); │ │ │ │ + for (var key in allParams) { │ │ │ │ + if (key.toUpperCase() in urlParams) { │ │ │ │ + delete allParams[key] │ │ │ │ + } │ │ │ │ + } │ │ │ │ + paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ + return OpenLayers.Util.urlAppend(url, paramsString) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.HTTPRequest" │ │ │ │ +}); │ │ │ │ +OpenLayers.Tile = OpenLayers.Class({ │ │ │ │ + events: null, │ │ │ │ + eventListeners: null, │ │ │ │ + id: null, │ │ │ │ + layer: null, │ │ │ │ + url: null, │ │ │ │ + bounds: null, │ │ │ │ + size: null, │ │ │ │ + position: null, │ │ │ │ + isLoading: false, │ │ │ │ + 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() │ │ │ │ + } │ │ │ │ + this.id = OpenLayers.Util.createUniqueID("Tile_"); │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.events = new OpenLayers.Events(this); │ │ │ │ + if (this.eventListeners instanceof Object) { │ │ │ │ + this.events.on(this.eventListeners) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + unload: function() { │ │ │ │ + if (this.isLoading) { │ │ │ │ + this.isLoading = false; │ │ │ │ + this.events.triggerEvent("unload") │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.layer = null; │ │ │ │ + this.bounds = null; │ │ │ │ + this.size = null; │ │ │ │ + this.position = null; │ │ │ │ + if (this.eventListeners) { │ │ │ │ + this.events.un(this.eventListeners) │ │ │ │ + } │ │ │ │ + this.events.destroy(); │ │ │ │ + this.eventListeners = null; │ │ │ │ + this.events = null │ │ │ │ + }, │ │ │ │ + draw: function(force) { │ │ │ │ + if (!force) { │ │ │ │ + this.clear() │ │ │ │ + } │ │ │ │ + var draw = this.shouldDraw(); │ │ │ │ + if (draw && !force && this.events.triggerEvent("beforedraw") === false) { │ │ │ │ + draw = null │ │ │ │ + } │ │ │ │ + return draw │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + moveTo: function(bounds, position, redraw) { │ │ │ │ + if (redraw == null) { │ │ │ │ + redraw = true │ │ │ │ + } │ │ │ │ + this.setBounds(bounds); │ │ │ │ + this.position = position.clone(); │ │ │ │ + if (redraw) { │ │ │ │ + this.draw() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + clear: function(draw) {}, │ │ │ │ + CLASS_NAME: "OpenLayers.Tile" │ │ │ │ +}); │ │ │ │ +OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { │ │ │ │ + url: null, │ │ │ │ + imgDiv: null, │ │ │ │ + frame: null, │ │ │ │ + imageReloadAttempts: null, │ │ │ │ + layerAlphaHack: null, │ │ │ │ + asyncRequestId: null, │ │ │ │ + maxGetUrlLength: null, │ │ │ │ + canvasContext: null, │ │ │ │ + crossOriginKeyword: null, │ │ │ │ + initialize: function(layer, position, bounds, url, size, options) { │ │ │ │ + OpenLayers.Tile.prototype.initialize.apply(this, arguments); │ │ │ │ + this.url = url; │ │ │ │ + this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack(); │ │ │ │ + if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) { │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + if (this.imgDiv) { │ │ │ │ + this.clear(); │ │ │ │ + this.imgDiv = null; │ │ │ │ + this.frame = null │ │ │ │ + } │ │ │ │ + this.asyncRequestId = null; │ │ │ │ + OpenLayers.Tile.prototype.destroy.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + draw: function() { │ │ │ │ + var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments); │ │ │ │ + if (shouldDraw) { │ │ │ │ + if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { │ │ │ │ + this.bounds = this.getBoundsFromBaseLayer(this.position) │ │ │ │ + } │ │ │ │ + if (this.isLoading) { │ │ │ │ + this._loadEvent = "reload" │ │ │ │ + } else { │ │ │ │ + this.isLoading = true; │ │ │ │ + this._loadEvent = "loadstart" │ │ │ │ + } │ │ │ │ + this.renderTile(); │ │ │ │ + this.positionTile() │ │ │ │ + } else if (shouldDraw === false) { │ │ │ │ + this.unload() │ │ │ │ + } │ │ │ │ + return shouldDraw │ │ │ │ + }, │ │ │ │ + renderTile: function() { │ │ │ │ + if (this.layer.async) { │ │ │ │ + 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 { │ │ │ │ + this.url = this.layer.getURL(this.bounds); │ │ │ │ + this.initImage() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + 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" │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + getImage: function() { │ │ │ │ + if (!this.imgDiv) { │ │ │ │ + this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false); │ │ │ │ + var style = this.imgDiv.style; │ │ │ │ + if (this.frame) { │ │ │ │ + var left = 0, │ │ │ │ + top = 0; │ │ │ │ + if (this.layer.gutter) { │ │ │ │ + left = this.layer.gutter / this.layer.tileSize.w * 100; │ │ │ │ + top = this.layer.gutter / this.layer.tileSize.h * 100 │ │ │ │ + } │ │ │ │ + style.left = -left + "%"; │ │ │ │ + style.top = -top + "%"; │ │ │ │ + style.width = 2 * left + 100 + "%"; │ │ │ │ + style.height = 2 * top + 100 + "%" │ │ │ │ + } │ │ │ │ + style.visibility = "hidden"; │ │ │ │ + style.opacity = 0; │ │ │ │ + if (this.layer.opacity < 1) { │ │ │ │ + style.filter = "alpha(opacity=" + this.layer.opacity * 100 + ")" │ │ │ │ + } │ │ │ │ + style.position = "absolute"; │ │ │ │ + if (this.layerAlphaHack) { │ │ │ │ + style.paddingTop = style.height; │ │ │ │ + style.height = "0"; │ │ │ │ + style.width = "100%" │ │ │ │ + } │ │ │ │ + if (this.frame) { │ │ │ │ + this.frame.appendChild(this.imgDiv) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return this.imgDiv │ │ │ │ + }, │ │ │ │ + setImage: function(img) { │ │ │ │ + this.imgDiv = img │ │ │ │ + }, │ │ │ │ + initImage: function() { │ │ │ │ + if (!this.url && !this.imgDiv) { │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + setImgSrc: function(url) { │ │ │ │ + var img = this.imgDiv; │ │ │ │ + if (url) { │ │ │ │ + img.style.visibility = "hidden"; │ │ │ │ + img.style.opacity = 0; │ │ │ │ + if (this.crossOriginKeyword) { │ │ │ │ + if (url.substr(0, 5) !== "data:") { │ │ │ │ + img.setAttribute("crossorigin", this.crossOriginKeyword) │ │ │ │ + } else { │ │ │ │ + img.removeAttribute("crossorigin") │ │ │ │ + } │ │ │ │ + } │ │ │ │ + img.src = url │ │ │ │ + } else { │ │ │ │ + this.stopLoading(); │ │ │ │ + this.imgDiv = null; │ │ │ │ + if (img.parentNode) { │ │ │ │ + img.parentNode.removeChild(img) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + getTile: function() { │ │ │ │ + return this.frame ? this.frame : this.getImage() │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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')" │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + 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() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + stopLoading: function() { │ │ │ │ + OpenLayers.Event.stopObservingElement(this.imgDiv); │ │ │ │ + window.clearTimeout(this._loadTimeout); │ │ │ │ + delete this._loadTimeout │ │ │ │ + }, │ │ │ │ + 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" │ │ │ │ +}); │ │ │ │ +OpenLayers.Tile.Image.IMAGE = function() { │ │ │ │ + var img = new Image; │ │ │ │ + img.className = "olTileImage"; │ │ │ │ + img.galleryImg = "no"; │ │ │ │ + return img │ │ │ │ +}(); │ │ │ │ +OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { │ │ │ │ + tileSize: null, │ │ │ │ + tileOriginCorner: "bl", │ │ │ │ + tileOrigin: null, │ │ │ │ + tileOptions: null, │ │ │ │ + tileClass: OpenLayers.Tile.Image, │ │ │ │ + grid: null, │ │ │ │ + singleTile: false, │ │ │ │ + ratio: 1.5, │ │ │ │ + buffer: 0, │ │ │ │ + transitionEffect: "resize", │ │ │ │ + numLoadingTiles: 0, │ │ │ │ + serverResolutions: null, │ │ │ │ + loading: false, │ │ │ │ + backBuffer: null, │ │ │ │ + gridResolution: null, │ │ │ │ + backBufferResolution: null, │ │ │ │ + backBufferLonLat: null, │ │ │ │ + backBufferTimerId: null, │ │ │ │ + removeBackBufferDelay: null, │ │ │ │ + className: null, │ │ │ │ + gridLayout: null, │ │ │ │ + rowSign: null, │ │ │ │ + transitionendEvents: ["transitionend", "webkitTransitionEnd", "otransitionend", "oTransitionEnd"], │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + initProperties: function() { │ │ │ │ + if (this.options.removeBackBufferDelay === undefined) { │ │ │ │ + this.removeBackBufferDelay = this.singleTile ? 0 : 2500 │ │ │ │ + } │ │ │ │ + if (this.options.className === undefined) { │ │ │ │ + this.className = this.singleTile ? "olLayerGridSingleTile" : "olLayerGrid" │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map); │ │ │ │ + OpenLayers.Element.addClass(this.div, this.className) │ │ │ │ + }, │ │ │ │ + removeMap: function(map) { │ │ │ │ + this.removeBackBuffer() │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.removeBackBuffer(); │ │ │ │ + this.clearGrid(); │ │ │ │ + this.grid = null; │ │ │ │ + this.tileSize = null; │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.Grid(this.name, this.url, this.params, this.getOptions()) │ │ │ │ + } │ │ │ │ + obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]); │ │ │ │ + if (this.tileSize != null) { │ │ │ │ + obj.tileSize = this.tileSize.clone() │ │ │ │ + } │ │ │ │ + obj.grid = []; │ │ │ │ + obj.gridResolution = null; │ │ │ │ + obj.backBuffer = null; │ │ │ │ + obj.backBufferTimerId = null; │ │ │ │ + obj.loading = false; │ │ │ │ + obj.numLoadingTiles = 0; │ │ │ │ + return obj │ │ │ │ + }, │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments); │ │ │ │ + bounds = bounds || this.map.getExtent(); │ │ │ │ + if (bounds != null) { │ │ │ │ + var forceReTile = !this.grid.length || zoomChanged; │ │ │ │ + var tilesBounds = this.getTilesBounds(); │ │ │ │ + var resolution = this.map.getResolution(); │ │ │ │ + var serverResolution = this.getServerResolution(resolution); │ │ │ │ + if (this.singleTile) { │ │ │ │ + if (forceReTile || !dragging && !tilesBounds.containsBounds(bounds)) { │ │ │ │ + if (zoomChanged && this.transitionEffect !== "resize") { │ │ │ │ + this.removeBackBuffer() │ │ │ │ + } │ │ │ │ + if (!zoomChanged || this.transitionEffect === "resize") { │ │ │ │ + this.applyBackBuffer(resolution) │ │ │ │ + } │ │ │ │ + this.initSingleTile(bounds) │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + 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() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + 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) { │ │ │ │ + if (this.map.baseLayer.wrapDateLine) { │ │ │ │ + var worldWidth = this.map.getMaxExtent().getWidth(); │ │ │ │ + var worldsAway = Math.ceil((left - x) / worldWidth); │ │ │ │ + x += worldWidth * worldsAway │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var dtx = (x - left) / (res * tileWidth); │ │ │ │ + var dty = (top - y) / (res * tileHeight); │ │ │ │ + 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, │ │ │ │ + i: Math.floor((dtx - col) * tileWidth), │ │ │ │ + j: Math.floor((dty - row) * tileHeight) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return data │ │ │ │ + }, │ │ │ │ + destroyTile: function(tile) { │ │ │ │ + this.removeTileMonitoringHooks(tile); │ │ │ │ + tile.destroy() │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + getServerZoom: function() { │ │ │ │ + var resolution = this.getServerResolution(); │ │ │ │ + return this.serverResolutions ? OpenLayers.Util.indexOf(this.serverResolutions, resolution) : this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0) │ │ │ │ + }, │ │ │ │ + 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; │ │ │ │ + var topLeftTileBounds = this.grid[0][0].bounds; │ │ │ │ + this.backBufferLonLat = { │ │ │ │ + lon: topLeftTileBounds.left, │ │ │ │ + lat: topLeftTileBounds.top │ │ │ │ + }; │ │ │ │ + this.backBufferResolution = this.gridResolution │ │ │ │ + } │ │ │ │ + var ratio = this.backBufferResolution / resolution; │ │ │ │ + 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" │ │ │ │ + } │ │ │ │ + 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" │ │ │ │ + }, │ │ │ │ + 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.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 │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + moveByPx: function(dx, dy) { │ │ │ │ + if (!this.singleTile) { │ │ │ │ + this.moveGriddedTiles() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + 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]) │ │ │ │ + }, │ │ │ │ + getTilesBounds: function() { │ │ │ │ + var bounds = null; │ │ │ │ + var length = this.grid.length; │ │ │ │ + if (length) { │ │ │ │ + var bottomLeftTileBounds = this.grid[length - 1][0].bounds, │ │ │ │ + width = this.grid[0].length * bottomLeftTileBounds.getWidth(), │ │ │ │ + height = this.grid.length * bottomLeftTileBounds.getHeight(); │ │ │ │ + bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, bottomLeftTileBounds.bottom, bottomLeftTileBounds.left + width, bottomLeftTileBounds.bottom + height) │ │ │ │ + } │ │ │ │ + return bounds │ │ │ │ + }, │ │ │ │ + initSingleTile: function(bounds) { │ │ │ │ + this.events.triggerEvent("retile"); │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + this.removeExcessTiles(1, 1); │ │ │ │ + this.gridResolution = this.getServerResolution() │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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) │ │ │ │ + }, │ │ │ │ + initGriddedTiles: function(bounds) { │ │ │ │ + this.events.triggerEvent("retile"); │ │ │ │ + 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); │ │ │ │ + this.removeExcessTiles(rowidx, colidx); │ │ │ │ + var resolution = this.getServerResolution(); │ │ │ │ + this.gridResolution = resolution; │ │ │ │ + tileData.sort(function(a, b) { │ │ │ │ + return a.distance - b.distance │ │ │ │ + }); │ │ │ │ + for (var i = 0, ii = tileData.length; i < ii; ++i) { │ │ │ │ + tileData[i].tile.draw() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + getMaxExtent: function() { │ │ │ │ + return this.maxExtent │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + addTileMonitoringHooks: function(tile) { │ │ │ │ + var replacingCls = "olTileReplacing"; │ │ │ │ + tile.onLoadStart = function() { │ │ │ │ + if (this.loading === false) { │ │ │ │ + this.loading = true; │ │ │ │ + this.events.triggerEvent("loadstart") │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("tileloadstart", { │ │ │ │ + tile: tile │ │ │ │ + }); │ │ │ │ + this.numLoadingTiles++; │ │ │ │ + if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ + OpenLayers.Element.addClass(tile.getTile(), replacingCls) │ │ │ │ + } │ │ │ │ + }; │ │ │ │ + tile.onLoadEnd = function(evt) { │ │ │ │ + this.numLoadingTiles--; │ │ │ │ + var aborted = evt.type === "unload"; │ │ │ │ + this.events.triggerEvent("tileloaded", { │ │ │ │ + tile: tile, │ │ │ │ + aborted: aborted │ │ │ │ + }); │ │ │ │ + if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ + var tileDiv = tile.getTile(); │ │ │ │ + if (OpenLayers.Element.getStyle(tileDiv, "display") === "none") { │ │ │ │ + var bufferTile = document.getElementById(tile.id + "_bb"); │ │ │ │ + if (bufferTile) { │ │ │ │ + bufferTile.parentNode.removeChild(bufferTile) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + OpenLayers.Element.removeClass(tileDiv, replacingCls) │ │ │ │ + } │ │ │ │ + if (this.numLoadingTiles === 0) { │ │ │ │ + if (this.backBuffer) { │ │ │ │ + if (this.backBuffer.childNodes.length === 0) { │ │ │ │ + this.removeBackBuffer() │ │ │ │ + } else { │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + 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 │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + removeTileMonitoringHooks: function(tile) { │ │ │ │ + tile.unload(); │ │ │ │ + tile.events.un({ │ │ │ │ + loadstart: tile.onLoadStart, │ │ │ │ + loadend: tile.onLoadEnd, │ │ │ │ + unload: tile.onLoadEnd, │ │ │ │ + loaderror: tile.onLoadError, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + 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) │ │ │ │ + }, │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + removeExcessTiles: function(rows, columns) { │ │ │ │ + var i, l; │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + onMapResize: function() { │ │ │ │ + if (this.singleTile) { │ │ │ │ + this.clearGrid(); │ │ │ │ + this.setTileSize() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + getTileBounds: function(viewPortPx) { │ │ │ │ + var maxExtent = this.maxExtent; │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var tileMapWidth = resolution * this.tileSize.w; │ │ │ │ + var tileMapHeight = resolution * this.tileSize.h; │ │ │ │ + var mapPoint = this.getLonLatFromViewPortPx(viewPortPx); │ │ │ │ + var tileLeft = maxExtent.left + tileMapWidth * Math.floor((mapPoint.lon - maxExtent.left) / tileMapWidth); │ │ │ │ + var tileBottom = maxExtent.bottom + tileMapHeight * Math.floor((mapPoint.lat - maxExtent.bottom) / tileMapHeight); │ │ │ │ + return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Grid" │ │ │ │ +}); │ │ │ │ +OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + isBaseLayer: true, │ │ │ │ + sphericalMercator: false, │ │ │ │ + zoomOffset: 0, │ │ │ │ + serverResolutions: null, │ │ │ │ + initialize: function(name, url, options) { │ │ │ │ + if (options && options.sphericalMercator || this.sphericalMercator) { │ │ │ │ + options = OpenLayers.Util.extend({ │ │ │ │ + projection: "EPSG:900913", │ │ │ │ + numZoomLevels: 19 │ │ │ │ + }, options) │ │ │ │ + } │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name || this.name, url || this.url, {}, options]) │ │ │ │ + }, │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.XYZ(this.name, this.url, this.getOptions()) │ │ │ │ + } │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + return obj │ │ │ │ + }, │ │ │ │ + getURL: function(bounds) { │ │ │ │ + var xyz = this.getXYZ(bounds); │ │ │ │ + var url = this.url; │ │ │ │ + if (OpenLayers.Util.isArray(url)) { │ │ │ │ + var s = "" + xyz.x + xyz.y + xyz.z; │ │ │ │ + url = this.selectUrl(s, url) │ │ │ │ + } │ │ │ │ + return OpenLayers.String.format(url, xyz) │ │ │ │ + }, │ │ │ │ + getXYZ: function(bounds) { │ │ │ │ + var res = this.getServerResolution(); │ │ │ │ + var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w)); │ │ │ │ + var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h)); │ │ │ │ + var z = this.getServerZoom(); │ │ │ │ + if (this.wrapDateLine) { │ │ │ │ + var limit = Math.pow(2, z); │ │ │ │ + x = (x % limit + limit) % limit │ │ │ │ + } │ │ │ │ + return { │ │ │ │ + x: x, │ │ │ │ + y: y, │ │ │ │ + z: z │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ + if (!this.tileOrigin) { │ │ │ │ + this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.bottom) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.XYZ" │ │ │ │ +}); │ │ │ │ +OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ + name: "OpenStreetMap", │ │ │ │ + url: ["http://a.tile.openstreetmap.org/${z}/${x}/${y}.png", "http://b.tile.openstreetmap.org/${z}/${x}/${y}.png", "http://c.tile.openstreetmap.org/${z}/${x}/${y}.png"], │ │ │ │ + attribution: "© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors", │ │ │ │ + sphericalMercator: true, │ │ │ │ + wrapDateLine: true, │ │ │ │ + tileOptions: null, │ │ │ │ + initialize: function(name, url, options) { │ │ │ │ + OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); │ │ │ │ + this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ + crossOriginKeyword: "anonymous" │ │ │ │ + }, this.options && this.options.tileOptions) │ │ │ │ + }, │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.OSM(this.name, this.url, this.getOptions()) │ │ │ │ + } │ │ │ │ + obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ + return obj │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.OSM" │ │ │ │ +}); │ │ │ │ +OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ + key: null, │ │ │ │ + serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625, 4891.9698095703125, 2445.9849047851562, 1222.9924523925781, 611.4962261962891, 305.74811309814453, 152.87405654907226, 76.43702827453613, 38.218514137268066, 19.109257068634033, 9.554628534317017, 4.777314267158508, 2.388657133579254, 1.194328566789627, .5971642833948135, .29858214169740677, .14929107084870338, .07464553542435169], │ │ │ │ + attributionTemplate: '<span class="olBingAttribution ${type}">' + '<div><a target="_blank" href="http://www.bing.com/maps/">' + '<img src="${logo}" /></a></div>${copyrights}' + '<a style="white-space: nowrap" target="_blank" ' + 'href="http://www.microsoft.com/maps/product/terms.html">' + "Terms of Use</a></span>", │ │ │ │ + metadata: null, │ │ │ │ + protocolRegex: /^http:/i, │ │ │ │ + type: "Road", │ │ │ │ + culture: "en-US", │ │ │ │ + metadataParams: null, │ │ │ │ + tileOptions: null, │ │ │ │ + protocol: ~window.location.href.indexOf("http") ? "" : "http:", │ │ │ │ + initialize: function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults({ │ │ │ │ + sphericalMercator: true │ │ │ │ + }, options); │ │ │ │ + var name = options.name || "Bing " + (options.type || this.type); │ │ │ │ + var newArgs = [name, null, options]; │ │ │ │ + OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs); │ │ │ │ + this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ + crossOriginKeyword: "anonymous" │ │ │ │ + }, this.options.tileOptions); │ │ │ │ + this.loadMetadata() │ │ │ │ + }, │ │ │ │ + loadMetadata: function() { │ │ │ │ + this._callbackId = "_callback_" + this.id.replace(/\./g, "_"); │ │ │ │ + window[this._callbackId] = OpenLayers.Function.bind(OpenLayers.Layer.Bing.processMetadata, this); │ │ │ │ + var params = OpenLayers.Util.applyDefaults({ │ │ │ │ + key: this.key, │ │ │ │ + jsonp: this._callbackId, │ │ │ │ + include: "ImageryProviders" │ │ │ │ + }, this.metadataParams); │ │ │ │ + var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" + this.type + "?" + OpenLayers.Util.getParameterString(params); │ │ │ │ + var script = document.createElement("script"); │ │ │ │ + script.type = "text/javascript"; │ │ │ │ + script.src = url; │ │ │ │ + script.id = this._callbackId; │ │ │ │ + document.getElementsByTagName("head")[0].appendChild(script) │ │ │ │ + }, │ │ │ │ + initLayer: function() { │ │ │ │ + var res = this.metadata.resourceSets[0].resources[0]; │ │ │ │ + var url = res.imageUrl.replace("{quadkey}", "${quadkey}"); │ │ │ │ + url = url.replace("{culture}", this.culture); │ │ │ │ + url = url.replace(this.protocolRegex, this.protocol); │ │ │ │ + this.url = []; │ │ │ │ + for (var i = 0; i < res.imageUrlSubdomains.length; ++i) { │ │ │ │ + this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i])) │ │ │ │ + } │ │ │ │ + this.addOptions({ │ │ │ │ + maxResolution: Math.min(this.serverResolutions[res.zoomMin], this.maxResolution || Number.POSITIVE_INFINITY), │ │ │ │ + numZoomLevels: Math.min(res.zoomMax + 1 - res.zoomMin, this.numZoomLevels) │ │ │ │ + }, true); │ │ │ │ + if (!this.isBaseLayer) { │ │ │ │ + this.redraw() │ │ │ │ + } │ │ │ │ + this.updateAttribution() │ │ │ │ + }, │ │ │ │ + getURL: function(bounds) { │ │ │ │ + if (!this.url) { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + var xyz = this.getXYZ(bounds), │ │ │ │ + x = xyz.x, │ │ │ │ + y = xyz.y, │ │ │ │ + z = xyz.z; │ │ │ │ + var quadDigits = []; │ │ │ │ + for (var i = z; i > 0; --i) { │ │ │ │ + var digit = "0"; │ │ │ │ + var mask = 1 << i - 1; │ │ │ │ + if ((x & mask) != 0) { │ │ │ │ + digit++ │ │ │ │ + } │ │ │ │ + if ((y & mask) != 0) { │ │ │ │ + digit++; │ │ │ │ + digit++ │ │ │ │ + } │ │ │ │ + quadDigits.push(digit) │ │ │ │ + } │ │ │ │ + var quadKey = quadDigits.join(""); │ │ │ │ + var url = this.selectUrl("" + x + y + z, this.url); │ │ │ │ + return OpenLayers.String.format(url, { │ │ │ │ + quadkey: quadKey │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + updateAttribution: function() { │ │ │ │ + var metadata = this.metadata; │ │ │ │ + if (!metadata.resourceSets || !this.map || !this.map.center) { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + var res = metadata.resourceSets[0].resources[0]; │ │ │ │ + var extent = this.map.getExtent().transform(this.map.getProjectionObject(), new OpenLayers.Projection("EPSG:4326")); │ │ │ │ + var providers = res.imageryProviders || [], │ │ │ │ + zoom = OpenLayers.Util.indexOf(this.serverResolutions, this.getServerResolution()), │ │ │ │ + copyrights = "", │ │ │ │ + provider, i, ii, j, jj, bbox, coverage; │ │ │ │ + for (i = 0, ii = providers.length; i < ii; ++i) { │ │ │ │ + provider = providers[i]; │ │ │ │ + for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) { │ │ │ │ + coverage = provider.coverageAreas[j]; │ │ │ │ + bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true); │ │ │ │ + if (extent.intersectsBounds(bbox) && zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) { │ │ │ │ + copyrights += provider.attribution + " " │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol); │ │ │ │ + this.attribution = OpenLayers.String.format(this.attributionTemplate, { │ │ │ │ + type: this.type.toLowerCase(), │ │ │ │ + logo: logo, │ │ │ │ + copyrights: copyrights │ │ │ │ + }); │ │ │ │ + this.map && this.map.events.triggerEvent("changelayer", { │ │ │ │ + layer: this, │ │ │ │ + property: "attribution" │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + setMap: function() { │ │ │ │ + OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments); │ │ │ │ + this.map.events.register("moveend", this, this.updateAttribution) │ │ │ │ + }, │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.Bing(this.options) │ │ │ │ + } │ │ │ │ + obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ + return obj │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.map && this.map.events.unregister("moveend", this, this.updateAttribution); │ │ │ │ + OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Bing" │ │ │ │ +}); │ │ │ │ +OpenLayers.Layer.Bing.processMetadata = function(metadata) { │ │ │ │ + this.metadata = metadata; │ │ │ │ + this.initLayer(); │ │ │ │ + var script = document.getElementById(this._callbackId); │ │ │ │ + script.parentNode.removeChild(script); │ │ │ │ + window[this._callbackId] = undefined; │ │ │ │ + delete this._callbackId │ │ │ │ +}; │ │ │ │ +OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ + DEFAULT_PARAMS: { │ │ │ │ + service: "WMS", │ │ │ │ + version: "1.1.1", │ │ │ │ + request: "GetMap", │ │ │ │ + styles: "", │ │ │ │ + format: "image/jpeg" │ │ │ │ + }, │ │ │ │ + isBaseLayer: true, │ │ │ │ + encodeBBOX: false, │ │ │ │ + noMagic: false, │ │ │ │ + yx: {}, │ │ │ │ + initialize: function(name, url, params, options) { │ │ │ │ + var newArguments = []; │ │ │ │ + params = OpenLayers.Util.upperCaseObject(params); │ │ │ │ + if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) { │ │ │ │ + params.EXCEPTIONS = "INIMAGE" │ │ │ │ + } │ │ │ │ + newArguments.push(name, url, params, options); │ │ │ │ + OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ + OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)); │ │ │ │ + if (!this.noMagic && this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == "true") { │ │ │ │ + if (options == null || !options.isBaseLayer) { │ │ │ │ + this.isBaseLayer = false │ │ │ │ + } │ │ │ │ + if (this.params.FORMAT == "image/jpeg") { │ │ │ │ + this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif" : "image/png" │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.WMS(this.name, this.url, this.params, this.getOptions()) │ │ │ │ + } │ │ │ │ + obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ + return obj │ │ │ │ + }, │ │ │ │ + reverseAxisOrder: function() { │ │ │ │ + var projCode = this.projection.getCode(); │ │ │ │ + return parseFloat(this.params.VERSION) >= 1.3 && !!(this.yx[projCode] || OpenLayers.Projection.defaults[projCode] && OpenLayers.Projection.defaults[projCode].yx) │ │ │ │ + }, │ │ │ │ + getURL: function(bounds) { │ │ │ │ + bounds = this.adjustBounds(bounds); │ │ │ │ + var imageSize = this.getImageSize(); │ │ │ │ + var newParams = {}; │ │ │ │ + var reverseAxisOrder = this.reverseAxisOrder(); │ │ │ │ + newParams.BBOX = this.encodeBBOX ? bounds.toBBOX(null, reverseAxisOrder) : bounds.toArray(reverseAxisOrder); │ │ │ │ + newParams.WIDTH = imageSize.w; │ │ │ │ + newParams.HEIGHT = imageSize.h; │ │ │ │ + var requestString = this.getFullRequestString(newParams); │ │ │ │ + return requestString │ │ │ │ + }, │ │ │ │ + mergeNewParams: function(newParams) { │ │ │ │ + var upperParams = OpenLayers.Util.upperCaseObject(newParams); │ │ │ │ + var newArguments = [upperParams]; │ │ │ │ + return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments) │ │ │ │ + }, │ │ │ │ + getFullRequestString: function(newParams, altUrl) { │ │ │ │ + var mapProjection = this.map.getProjectionObject(); │ │ │ │ + var projectionCode = this.projection && this.projection.equals(mapProjection) ? this.projection.getCode() : mapProjection.getCode(); │ │ │ │ + var value = projectionCode == "none" ? null : projectionCode; │ │ │ │ + if (parseFloat(this.params.VERSION) >= 1.3) { │ │ │ │ + this.params.CRS = value │ │ │ │ + } else { │ │ │ │ + this.params.SRS = value │ │ │ │ + } │ │ │ │ + if (typeof this.params.TRANSPARENT == "boolean") { │ │ │ │ + newParams.TRANSPARENT = this.params.TRANSPARENT ? "TRUE" : "FALSE" │ │ │ │ + } │ │ │ │ + return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.WMS" │ │ │ │ +}); │ │ │ │ +OpenLayers.Layer.SphericalMercator = { │ │ │ │ + getExtent: function() { │ │ │ │ + var extent = null; │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + extent = this.map.calculateBounds() │ │ │ │ + } else { │ │ │ │ + extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this) │ │ │ │ + } │ │ │ │ + return extent │ │ │ │ + }, │ │ │ │ + getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ + return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ + return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + initMercatorParameters: function() { │ │ │ │ + this.RESOLUTIONS = []; │ │ │ │ + var maxResolution = 156543.03390625; │ │ │ │ + for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) { │ │ │ │ + this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom) │ │ │ │ + } │ │ │ │ + this.units = "m"; │ │ │ │ + this.projection = this.projection || "EPSG:900913" │ │ │ │ + }, │ │ │ │ + forwardMercator: function() { │ │ │ │ + var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ + return function(lon, lat) { │ │ │ │ + var point = OpenLayers.Projection.transform({ │ │ │ │ + x: lon, │ │ │ │ + y: lat │ │ │ │ + }, gg, sm); │ │ │ │ + return new OpenLayers.LonLat(point.x, point.y) │ │ │ │ + } │ │ │ │ + }(), │ │ │ │ + inverseMercator: function() { │ │ │ │ + var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ + return function(x, y) { │ │ │ │ + var point = OpenLayers.Projection.transform({ │ │ │ │ + x: x, │ │ │ │ + y: y │ │ │ │ + }, sm, gg); │ │ │ │ + return new OpenLayers.LonLat(point.x, point.y) │ │ │ │ + } │ │ │ │ + }() │ │ │ │ +}; │ │ │ │ +OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ + smoothDragPan: true, │ │ │ │ + isBaseLayer: true, │ │ │ │ + isFixed: true, │ │ │ │ + pane: null, │ │ │ │ + mapObject: null, │ │ │ │ + initialize: function(name, options) { │ │ │ │ + OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ + if (this.pane == null) { │ │ │ │ + this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane") │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.mapObject = null; │ │ │ │ + this.pane = null; │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments) │ │ │ │ }, │ │ │ │ - getGeodesicArea: function(projection) { │ │ │ │ - var ring = this; │ │ │ │ - if (projection) { │ │ │ │ - var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ - if (!gg.equals(projection)) { │ │ │ │ - ring = this.clone().transform(projection, gg) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var area = 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 * 6378137 / 2 │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ + this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; │ │ │ │ + this.pane.style.display = this.div.style.display; │ │ │ │ + this.pane.style.width = "100%"; │ │ │ │ + this.pane.style.height = "100%"; │ │ │ │ + if (OpenLayers.BROWSER_NAME == "msie") { │ │ │ │ + this.pane.style.background = "url(" + OpenLayers.Util.getImageLocation("blank.gif") + ")" │ │ │ │ } │ │ │ │ - return area │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ + if (this.isFixed) { │ │ │ │ + this.map.viewPortDiv.appendChild(this.pane) │ │ │ │ + } else { │ │ │ │ + this.map.layerContainerDiv.appendChild(this.pane) │ │ │ │ } │ │ │ │ - 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); │ │ │ │ - if (y1 == y2) { │ │ │ │ - if (py == y1) { │ │ │ │ - if (x1 <= x2 && (px >= x1 && px <= x2) || x1 >= x2 && (px <= x1 && px >= x2)) { │ │ │ │ - crosses = -1; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - continue │ │ │ │ - } │ │ │ │ - cx = approx(getX(py, x1, y1, x2, y2), digs); │ │ │ │ - if (cx == px) { │ │ │ │ - if (y1 < y2 && (py >= y1 && py <= y2) || y1 > y2 && (py <= y1 && py >= y2)) { │ │ │ │ - crosses = -1; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (cx <= px) { │ │ │ │ - continue │ │ │ │ - } │ │ │ │ - if (x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) { │ │ │ │ - continue │ │ │ │ - } │ │ │ │ - if (y1 < y2 && (py >= y1 && py < y2) || y1 > y2 && (py < y1 && py >= y2)) { │ │ │ │ - ++crosses │ │ │ │ - } │ │ │ │ + this.loadMapObject(); │ │ │ │ + if (this.mapObject == null) { │ │ │ │ + this.loadWarningMessage() │ │ │ │ } │ │ │ │ - var contained = crosses == -1 ? 1 : !!(crosses & 1); │ │ │ │ - return contained │ │ │ │ }, │ │ │ │ - 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 { │ │ │ │ - for (var i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ - intersect = geometry.components[i].intersects(this); │ │ │ │ - if (intersect) { │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ + removeMap: function(map) { │ │ │ │ + if (this.pane && this.pane.parentNode) { │ │ │ │ + this.pane.parentNode.removeChild(this.pane) │ │ │ │ } │ │ │ │ - return intersect │ │ │ │ + OpenLayers.Layer.prototype.removeMap.apply(this, arguments) │ │ │ │ }, │ │ │ │ - getVertices: function(nodes) { │ │ │ │ - return nodes === true ? [] : this.components.slice(0, this.components.length - 1) │ │ │ │ + loadWarningMessage: function() { │ │ │ │ + this.div.style.backgroundColor = "darkblue"; │ │ │ │ + var viewSize = this.map.getSize(); │ │ │ │ + var msgW = Math.min(viewSize.w, 300); │ │ │ │ + var msgH = Math.min(viewSize.h, 200); │ │ │ │ + var size = new OpenLayers.Size(msgW, msgH); │ │ │ │ + var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2); │ │ │ │ + var topLeft = centerPx.add(-size.w / 2, -size.h / 2); │ │ │ │ + var div = OpenLayers.Util.createDiv(this.name + "_warning", topLeft, size, null, null, null, "auto"); │ │ │ │ + div.style.padding = "7px"; │ │ │ │ + div.style.backgroundColor = "yellow"; │ │ │ │ + div.innerHTML = this.getWarningHTML(); │ │ │ │ + this.div.appendChild(div) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.LinearRing" │ │ │ │ -}); │ │ │ │ -OpenLayers.Geometry.Polygon = OpenLayers.Class(OpenLayers.Geometry.Collection, { │ │ │ │ - componentTypes: ["OpenLayers.Geometry.LinearRing"], │ │ │ │ - getArea: function() { │ │ │ │ - var area = 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 │ │ │ │ + getWarningHTML: function() { │ │ │ │ + return "" │ │ │ │ }, │ │ │ │ - getGeodesicArea: function(projection) { │ │ │ │ - var area = 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)) │ │ │ │ - } │ │ │ │ + display: function(display) { │ │ │ │ + OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ + this.pane.style.display = this.div.style.display │ │ │ │ + }, │ │ │ │ + setZIndex: function(zIndex) { │ │ │ │ + OpenLayers.Layer.prototype.setZIndex.apply(this, arguments); │ │ │ │ + this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1 │ │ │ │ + }, │ │ │ │ + moveByPx: function(dx, dy) { │ │ │ │ + OpenLayers.Layer.prototype.moveByPx.apply(this, arguments); │ │ │ │ + if (this.dragPanMapObject) { │ │ │ │ + this.dragPanMapObject(dx, -dy) │ │ │ │ + } else { │ │ │ │ + this.moveTo(this.map.getCachedCenter()) │ │ │ │ } │ │ │ │ - return area │ │ │ │ }, │ │ │ │ - containsPoint: function(point) { │ │ │ │ - var numRings = this.components.length; │ │ │ │ - var contained = false; │ │ │ │ - if (numRings > 0) { │ │ │ │ - contained = this.components[0].containsPoint(point); │ │ │ │ - if (contained !== 1) { │ │ │ │ - if (contained && numRings > 1) { │ │ │ │ - var hole; │ │ │ │ - for (var i = 1; i < numRings; ++i) { │ │ │ │ - hole = this.components[i].containsPoint(point); │ │ │ │ - if (hole) { │ │ │ │ - if (hole === 1) { │ │ │ │ - contained = 1 │ │ │ │ - } else { │ │ │ │ - contained = false │ │ │ │ - } │ │ │ │ - break │ │ │ │ - } │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ + if (this.mapObject != null) { │ │ │ │ + var newCenter = this.map.getCenter(); │ │ │ │ + var newZoom = this.map.getZoom(); │ │ │ │ + if (newCenter != null) { │ │ │ │ + var moOldCenter = this.getMapObjectCenter(); │ │ │ │ + var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter); │ │ │ │ + var moOldZoom = this.getMapObjectZoom(); │ │ │ │ + var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom); │ │ │ │ + if (!newCenter.equals(oldCenter) || newZoom != oldZoom) { │ │ │ │ + if (!zoomChanged && oldCenter && this.dragPanMapObject && this.smoothDragPan) { │ │ │ │ + var oldPx = this.map.getViewPortPxFromLonLat(oldCenter); │ │ │ │ + var newPx = this.map.getViewPortPxFromLonLat(newCenter); │ │ │ │ + this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y) │ │ │ │ + } else { │ │ │ │ + var center = this.getMapObjectLonLatFromOLLonLat(newCenter); │ │ │ │ + var zoom = this.getMapObjectZoomFromOLZoom(newZoom); │ │ │ │ + this.setMapObjectCenter(center, zoom, dragging) │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ - return contained │ │ │ │ }, │ │ │ │ - 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") { │ │ │ │ - for (i = 0, len = this.components.length; i < len; ++i) { │ │ │ │ - intersect = geometry.intersects(this.components[i]); │ │ │ │ - if (intersect) { │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (!intersect) { │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ - } │ │ │ │ + getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ + var lonlat = null; │ │ │ │ + if (this.mapObject != null && this.getMapObjectCenter() != null) { │ │ │ │ + var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx); │ │ │ │ + var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel); │ │ │ │ + lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat) │ │ │ │ } │ │ │ │ - if (!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") { │ │ │ │ - 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 lonlat │ │ │ │ + }, │ │ │ │ + getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ + var viewPortPx = null; │ │ │ │ + if (this.mapObject != null && this.getMapObjectCenter() != null) { │ │ │ │ + var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat); │ │ │ │ + var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat); │ │ │ │ + viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel) │ │ │ │ } │ │ │ │ - return intersect │ │ │ │ + return viewPortPx │ │ │ │ }, │ │ │ │ - distanceTo: function(geometry, options) { │ │ │ │ - var edge = !(options && options.edge === false); │ │ │ │ - var result; │ │ │ │ - if (!edge && this.intersects(geometry)) { │ │ │ │ - result = 0 │ │ │ │ - } else { │ │ │ │ - result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(this, [geometry, options]) │ │ │ │ + getOLLonLatFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + var olLonLat = null; │ │ │ │ + if (moLonLat != null) { │ │ │ │ + var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ + var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ + olLonLat = new OpenLayers.LonLat(lon, lat) │ │ │ │ } │ │ │ │ - return result │ │ │ │ + return olLonLat │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.Polygon" │ │ │ │ -}); │ │ │ │ -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 = OpenLayers.Class(OpenLayers.Geometry.Collection, { │ │ │ │ - componentTypes: ["OpenLayers.Geometry.Polygon"], │ │ │ │ - CLASS_NAME: "OpenLayers.Geometry.MultiPolygon" │ │ │ │ -}); │ │ │ │ -OpenLayers.Format.GeoJSON = OpenLayers.Class(OpenLayers.Format.JSON, { │ │ │ │ - ignoreExtraDims: false, │ │ │ │ - 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 │ │ │ │ + getMapObjectLonLatFromOLLonLat: function(olLonLat) { │ │ │ │ + var moLatLng = null; │ │ │ │ + if (olLonLat != null) { │ │ │ │ + moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, olLonLat.lat) │ │ │ │ } │ │ │ │ - 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": │ │ │ │ - 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 moLatLng │ │ │ │ + }, │ │ │ │ + getOLPixelFromMapObjectPixel: function(moPixel) { │ │ │ │ + var olPixel = null; │ │ │ │ + if (moPixel != null) { │ │ │ │ + var x = this.getXFromMapObjectPixel(moPixel); │ │ │ │ + var y = this.getYFromMapObjectPixel(moPixel); │ │ │ │ + olPixel = new OpenLayers.Pixel(x, y) │ │ │ │ } │ │ │ │ - return results │ │ │ │ + return olPixel │ │ │ │ }, │ │ │ │ - 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) { │ │ │ │ - OpenLayers.Console.error("Unsupported geometry type: " + obj.type) │ │ │ │ - } else { │ │ │ │ - valid = true │ │ │ │ - } │ │ │ │ - break; │ │ │ │ - case "FeatureCollection": │ │ │ │ - valid = true; │ │ │ │ - break; │ │ │ │ - default: │ │ │ │ - if (obj.type == type) { │ │ │ │ - valid = true │ │ │ │ - } else { │ │ │ │ - OpenLayers.Console.error("Cannot convert types from " + obj.type + " to " + type) │ │ │ │ - } │ │ │ │ + getMapObjectPixelFromOLPixel: function(olPixel) { │ │ │ │ + var moPixel = null; │ │ │ │ + if (olPixel != null) { │ │ │ │ + moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y) │ │ │ │ } │ │ │ │ - return valid │ │ │ │ + return moPixel │ │ │ │ }, │ │ │ │ - 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) { │ │ │ │ - throw err │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.EventPane" │ │ │ │ +}); │ │ │ │ +OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({ │ │ │ │ + initialize: function() {}, │ │ │ │ + initResolutions: function() { │ │ │ │ + var props = ["minZoomLevel", "maxZoomLevel", "numZoomLevels"]; │ │ │ │ + for (var i = 0, len = props.length; i < len; i++) { │ │ │ │ + var property = props[i]; │ │ │ │ + this[property] = this.options[property] != null ? this.options[property] : this.map[property] │ │ │ │ } │ │ │ │ - feature = new OpenLayers.Feature.Vector(geometry, attributes); │ │ │ │ - if (bbox) { │ │ │ │ - feature.bounds = OpenLayers.Bounds.fromArray(bbox) │ │ │ │ + if (this.minZoomLevel == null || this.minZoomLevel < this.MIN_ZOOM_LEVEL) { │ │ │ │ + this.minZoomLevel = this.MIN_ZOOM_LEVEL │ │ │ │ } │ │ │ │ - if (obj.id) { │ │ │ │ - feature.fid = obj.id │ │ │ │ + var desiredZoomLevels; │ │ │ │ + var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1; │ │ │ │ + if (this.options.numZoomLevels == null && this.options.maxZoomLevel != null || this.numZoomLevels == null && this.maxZoomLevel != null) { │ │ │ │ + desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1 │ │ │ │ + } else { │ │ │ │ + desiredZoomLevels = this.numZoomLevels │ │ │ │ } │ │ │ │ - return feature │ │ │ │ - }, │ │ │ │ - parseGeometry: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - return null │ │ │ │ + if (desiredZoomLevels != null) { │ │ │ │ + this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels) │ │ │ │ + } else { │ │ │ │ + this.numZoomLevels = limitZoomLevels │ │ │ │ } │ │ │ │ - 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]]) │ │ │ │ + this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1; │ │ │ │ + if (this.RESOLUTIONS != null) { │ │ │ │ + var resolutionsIndex = 0; │ │ │ │ + this.resolutions = []; │ │ │ │ + for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) { │ │ │ │ + this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i] │ │ │ │ } │ │ │ │ - geometry = new OpenLayers.Geometry.Collection(components); │ │ │ │ - collection = true │ │ │ │ + this.maxResolution = this.resolutions[0]; │ │ │ │ + this.minResolution = this.resolutions[this.resolutions.length - 1] │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + getResolution: function() { │ │ │ │ + if (this.resolutions != null) { │ │ │ │ + return OpenLayers.Layer.prototype.getResolution.apply(this, arguments) │ │ │ │ } 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) { │ │ │ │ - throw err │ │ │ │ + var resolution = null; │ │ │ │ + var viewSize = this.map.getSize(); │ │ │ │ + var extent = this.getExtent(); │ │ │ │ + if (viewSize != null && extent != null) { │ │ │ │ + resolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h) │ │ │ │ } │ │ │ │ + return resolution │ │ │ │ } │ │ │ │ - if (this.internalProjection && this.externalProjection && !collection) { │ │ │ │ - geometry.transform(this.externalProjection, this.internalProjection) │ │ │ │ - } │ │ │ │ - return geometry │ │ │ │ }, │ │ │ │ - parseCoords: { │ │ │ │ - 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]) │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - 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])])]) │ │ │ │ + getExtent: function() { │ │ │ │ + var size = this.map.getSize(); │ │ │ │ + var tl = this.getLonLatFromViewPortPx({ │ │ │ │ + x: 0, │ │ │ │ + y: 0 │ │ │ │ + }); │ │ │ │ + var br = this.getLonLatFromViewPortPx({ │ │ │ │ + x: size.w, │ │ │ │ + y: size.h │ │ │ │ + }); │ │ │ │ + if (tl != null && br != null) { │ │ │ │ + return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat) │ │ │ │ + } else { │ │ │ │ + return null │ │ │ │ } │ │ │ │ }, │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ + getZoomForResolution: function(resolution) { │ │ │ │ + if (this.resolutions != null) { │ │ │ │ + return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments) │ │ │ │ + } else { │ │ │ │ + var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []); │ │ │ │ + return this.getZoomForExtent(extent) │ │ │ │ } │ │ │ │ - return OpenLayers.Format.JSON.prototype.write.apply(this, [geojson, pretty]) │ │ │ │ }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ - } │ │ │ │ + getOLZoomFromMapObjectZoom: function(moZoom) { │ │ │ │ + var zoom = null; │ │ │ │ + if (moZoom != null) { │ │ │ │ + zoom = moZoom - this.minZoomLevel; │ │ │ │ + if (this.map.baseLayer !== this) { │ │ │ │ + zoom = this.map.baseLayer.getZoomForResolution(this.getResolutionForZoom(zoom)) │ │ │ │ } │ │ │ │ } │ │ │ │ - return crs │ │ │ │ + return zoom │ │ │ │ }, │ │ │ │ - extract: { │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - geometry: function(geometry) { │ │ │ │ - if (geometry == null) { │ │ │ │ - return null │ │ │ │ - } │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - geometry = geometry.clone(); │ │ │ │ - geometry.transform(this.internalProjection, this.externalProjection) │ │ │ │ - } │ │ │ │ - var geometryType = geometry.CLASS_NAME.split(".")[2]; │ │ │ │ - var data = this.extract[geometryType.toLowerCase()].apply(this, [geometry]); │ │ │ │ - var json; │ │ │ │ - if (geometryType == "Collection") { │ │ │ │ - json = { │ │ │ │ - type: "GeometryCollection", │ │ │ │ - geometries: data │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - json = { │ │ │ │ - type: geometryType, │ │ │ │ - coordinates: data │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return json │ │ │ │ - }, │ │ │ │ - point: function(point) { │ │ │ │ - return [point.x, point.y] │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - 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]]) │ │ │ │ + getMapObjectZoomFromOLZoom: function(olZoom) { │ │ │ │ + var zoom = null; │ │ │ │ + if (olZoom != null) { │ │ │ │ + zoom = olZoom + this.minZoomLevel; │ │ │ │ + if (this.map.baseLayer !== this) { │ │ │ │ + zoom = this.getZoomForResolution(this.map.baseLayer.getResolutionForZoom(zoom)) │ │ │ │ } │ │ │ │ - return array │ │ │ │ } │ │ │ │ + return zoom │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Format.GeoJSON" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels" │ │ │ │ }); │ │ │ │ -OpenLayers.Protocol = OpenLayers.Class({ │ │ │ │ - format: null, │ │ │ │ - options: null, │ │ │ │ - autoDestroy: true, │ │ │ │ - defaultFilter: null, │ │ │ │ - initialize: function(options) { │ │ │ │ +OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, { │ │ │ │ + MIN_ZOOM_LEVEL: 0, │ │ │ │ + MAX_ZOOM_LEVEL: 21, │ │ │ │ + RESOLUTIONS: [1.40625, .703125, .3515625, .17578125, .087890625, .0439453125, .02197265625, .010986328125, .0054931640625, .00274658203125, .001373291015625, .0006866455078125, .00034332275390625, .000171661376953125, 858306884765625e-19, 4291534423828125e-20, 2145767211914062e-20, 1072883605957031e-20, 536441802978515e-20, 268220901489257e-20, 1341104507446289e-21, 6.705522537231445e-7], │ │ │ │ + type: null, │ │ │ │ + wrapDateLine: true, │ │ │ │ + sphericalMercator: false, │ │ │ │ + version: null, │ │ │ │ + initialize: function(name, options) { │ │ │ │ options = options || {}; │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.options = options │ │ │ │ - }, │ │ │ │ - mergeWithDefaultFilter: function(filter) { │ │ │ │ - var merged; │ │ │ │ - if (filter && this.defaultFilter) { │ │ │ │ - merged = new OpenLayers.Filter.Logical({ │ │ │ │ - type: OpenLayers.Filter.Logical.AND, │ │ │ │ - filters: [this.defaultFilter, filter] │ │ │ │ - }) │ │ │ │ + if (!options.version) { │ │ │ │ + options.version = typeof GMap2 === "function" ? "2" : "3" │ │ │ │ + } │ │ │ │ + var mixin = OpenLayers.Layer.Google["v" + options.version.replace(/\./g, "_")]; │ │ │ │ + if (mixin) { │ │ │ │ + OpenLayers.Util.applyDefaults(options, mixin) │ │ │ │ } else { │ │ │ │ - merged = filter || this.defaultFilter || undefined │ │ │ │ + throw "Unsupported Google Maps API version: " + options.version │ │ │ │ + } │ │ │ │ + OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS); │ │ │ │ + if (options.maxExtent) { │ │ │ │ + options.maxExtent = options.maxExtent.clone() │ │ │ │ + } │ │ │ │ + OpenLayers.Layer.EventPane.prototype.initialize.apply(this, [name, options]); │ │ │ │ + OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, [name, options]); │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator); │ │ │ │ + this.initMercatorParameters() │ │ │ │ } │ │ │ │ - return merged │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.options = null; │ │ │ │ - this.format = null │ │ │ │ - }, │ │ │ │ - read: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - options.filter = this.mergeWithDefaultFilter(options.filter) │ │ │ │ }, │ │ │ │ - create: function() {}, │ │ │ │ - update: function() {}, │ │ │ │ - delete: function() {}, │ │ │ │ - commit: function() {}, │ │ │ │ - abort: function(response) {}, │ │ │ │ - createCallback: function(method, response, options) { │ │ │ │ - return OpenLayers.Function.bind(function() { │ │ │ │ - method.apply(this, [response, options]) │ │ │ │ - }, this) │ │ │ │ + clone: function() { │ │ │ │ + return new OpenLayers.Layer.Google(this.name, this.getOptions()) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol" │ │ │ │ -}); │ │ │ │ -OpenLayers.Protocol.Response = OpenLayers.Class({ │ │ │ │ - code: null, │ │ │ │ - requestType: null, │ │ │ │ - last: true, │ │ │ │ - features: null, │ │ │ │ - data: null, │ │ │ │ - reqFeatures: null, │ │ │ │ - priv: null, │ │ │ │ - error: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options) │ │ │ │ + setVisibility: function(visible) { │ │ │ │ + var opacity = this.opacity == null ? 1 : this.opacity; │ │ │ │ + OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments); │ │ │ │ + this.setOpacity(opacity) │ │ │ │ }, │ │ │ │ - success: function() { │ │ │ │ - return this.code > 0 │ │ │ │ + display: function(visible) { │ │ │ │ + if (!this._dragging) { │ │ │ │ + this.setGMapVisibility(visible) │ │ │ │ + } │ │ │ │ + OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.Response" │ │ │ │ -}); │ │ │ │ -OpenLayers.Protocol.Response.SUCCESS = 1; │ │ │ │ -OpenLayers.Protocol.Response.FAILURE = 0; │ │ │ │ -OpenLayers.ProxyHost = ""; │ │ │ │ -if (!OpenLayers.Request) { │ │ │ │ - OpenLayers.Request = {} │ │ │ │ -} │ │ │ │ -OpenLayers.Util.extend(OpenLayers.Request, { │ │ │ │ - DEFAULT_CONFIG: { │ │ │ │ - method: "GET", │ │ │ │ - url: window.location.href, │ │ │ │ - async: true, │ │ │ │ - user: undefined, │ │ │ │ - password: undefined, │ │ │ │ - params: null, │ │ │ │ - proxy: OpenLayers.ProxyHost, │ │ │ │ - headers: {}, │ │ │ │ - data: null, │ │ │ │ - callback: function() {}, │ │ │ │ - success: null, │ │ │ │ - failure: null, │ │ │ │ - scope: null │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + this._dragging = dragging; │ │ │ │ + OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments); │ │ │ │ + delete this._dragging │ │ │ │ }, │ │ │ │ - URL_SPLIT_REGEX: /([^:]*:)\/\/([^:]*:?[^@]*@)?([^:\/\?]*):?([^\/\?]*)/, │ │ │ │ - events: new OpenLayers.Events(this), │ │ │ │ - makeSameOrigin: function(url, proxy) { │ │ │ │ - var sameOrigin = url.indexOf("http") !== 0; │ │ │ │ - var urlParts = !sameOrigin && url.match(this.URL_SPLIT_REGEX); │ │ │ │ - if (urlParts) { │ │ │ │ - var location = window.location; │ │ │ │ - sameOrigin = urlParts[1] == location.protocol && urlParts[3] == location.hostname; │ │ │ │ - var uPort = urlParts[4], │ │ │ │ - lPort = location.port; │ │ │ │ - if (uPort != 80 && uPort != "" || lPort != "80" && lPort != "") { │ │ │ │ - sameOrigin = sameOrigin && uPort == lPort │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ + if (opacity !== this.opacity) { │ │ │ │ + if (this.map != null) { │ │ │ │ + this.map.events.triggerEvent("changelayer", { │ │ │ │ + layer: this, │ │ │ │ + property: "opacity" │ │ │ │ + }) │ │ │ │ } │ │ │ │ + this.opacity = opacity │ │ │ │ } │ │ │ │ - if (!sameOrigin) { │ │ │ │ - if (proxy) { │ │ │ │ - if (typeof proxy == "function") { │ │ │ │ - url = proxy(url) │ │ │ │ - } else { │ │ │ │ - url = proxy + encodeURIComponent(url) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + if (this.getVisibility()) { │ │ │ │ + var container = this.getMapContainer(); │ │ │ │ + OpenLayers.Util.modifyDOMElement(container, null, null, null, null, null, null, opacity) │ │ │ │ } │ │ │ │ - return url │ │ │ │ }, │ │ │ │ - issue: function(config) { │ │ │ │ - var defaultConfig = OpenLayers.Util.extend(this.DEFAULT_CONFIG, { │ │ │ │ - proxy: OpenLayers.ProxyHost │ │ │ │ - }); │ │ │ │ - config = config || {}; │ │ │ │ - config.headers = config.headers || {}; │ │ │ │ - config = OpenLayers.Util.applyDefaults(config, defaultConfig); │ │ │ │ - config.headers = OpenLayers.Util.applyDefaults(config.headers, defaultConfig.headers); │ │ │ │ - var customRequestedWithHeader = false, │ │ │ │ - headerKey; │ │ │ │ - for (headerKey in config.headers) { │ │ │ │ - if (config.headers.hasOwnProperty(headerKey)) { │ │ │ │ - if (headerKey.toLowerCase() === "x-requested-with") { │ │ │ │ - customRequestedWithHeader = true │ │ │ │ - } │ │ │ │ + destroy: function() { │ │ │ │ + if (this.map) { │ │ │ │ + this.setGMapVisibility(false); │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache && cache.count <= 1) { │ │ │ │ + this.removeGMapElements() │ │ │ │ } │ │ │ │ } │ │ │ │ - if (customRequestedWithHeader === false) { │ │ │ │ - config.headers["X-Requested-With"] = "XMLHttpRequest" │ │ │ │ - } │ │ │ │ - var request = new OpenLayers.Request.XMLHttpRequest; │ │ │ │ - var url = OpenLayers.Util.urlAppend(config.url, OpenLayers.Util.getParameterString(config.params || {})); │ │ │ │ - url = OpenLayers.Request.makeSameOrigin(url, config.proxy); │ │ │ │ - request.open(config.method, url, config.async, config.user, config.password); │ │ │ │ - for (var header in config.headers) { │ │ │ │ - request.setRequestHeader(header, config.headers[header]) │ │ │ │ - } │ │ │ │ - var events = this.events; │ │ │ │ - var self = this; │ │ │ │ - request.onreadystatechange = function() { │ │ │ │ - if (request.readyState == OpenLayers.Request.XMLHttpRequest.DONE) { │ │ │ │ - var proceed = events.triggerEvent("complete", { │ │ │ │ - request: request, │ │ │ │ - config: config, │ │ │ │ - requestUrl: url │ │ │ │ - }); │ │ │ │ - if (proceed !== false) { │ │ │ │ - self.runCallbacks({ │ │ │ │ - request: request, │ │ │ │ - config: config, │ │ │ │ - requestUrl: url │ │ │ │ - }) │ │ │ │ - } │ │ │ │ + OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + removeGMapElements: function() { │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache) { │ │ │ │ + var container = this.mapObject && this.getMapContainer(); │ │ │ │ + if (container && container.parentNode) { │ │ │ │ + container.parentNode.removeChild(container) │ │ │ │ + } │ │ │ │ + var termsOfUse = cache.termsOfUse; │ │ │ │ + if (termsOfUse && termsOfUse.parentNode) { │ │ │ │ + termsOfUse.parentNode.removeChild(termsOfUse) │ │ │ │ + } │ │ │ │ + var poweredBy = cache.poweredBy; │ │ │ │ + if (poweredBy && poweredBy.parentNode) { │ │ │ │ + poweredBy.parentNode.removeChild(poweredBy) │ │ │ │ + } │ │ │ │ + if (this.mapObject && window.google && google.maps && google.maps.event && google.maps.event.clearListeners) { │ │ │ │ + google.maps.event.clearListeners(this.mapObject, "tilesloaded") │ │ │ │ } │ │ │ │ - }; │ │ │ │ - if (config.async === false) { │ │ │ │ - request.send(config.data) │ │ │ │ - } else { │ │ │ │ - window.setTimeout(function() { │ │ │ │ - if (request.readyState !== 0) { │ │ │ │ - request.send(config.data) │ │ │ │ - } │ │ │ │ - }, 0) │ │ │ │ } │ │ │ │ - return request │ │ │ │ }, │ │ │ │ - runCallbacks: function(options) { │ │ │ │ - var request = options.request; │ │ │ │ - var config = options.config; │ │ │ │ - var complete = config.scope ? OpenLayers.Function.bind(config.callback, config.scope) : config.callback; │ │ │ │ - var success; │ │ │ │ - if (config.success) { │ │ │ │ - success = config.scope ? OpenLayers.Function.bind(config.success, config.scope) : config.success │ │ │ │ - } │ │ │ │ - var failure; │ │ │ │ - if (config.failure) { │ │ │ │ - failure = config.scope ? OpenLayers.Function.bind(config.failure, config.scope) : config.failure │ │ │ │ - } │ │ │ │ - if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" && request.responseText) { │ │ │ │ - request.status = 200 │ │ │ │ + removeMap: function(map) { │ │ │ │ + if (this.visibility && this.mapObject) { │ │ │ │ + this.setGMapVisibility(false) │ │ │ │ } │ │ │ │ - complete(request); │ │ │ │ - if (!request.status || request.status >= 200 && request.status < 300) { │ │ │ │ - this.events.triggerEvent("success", options); │ │ │ │ - if (success) { │ │ │ │ - success(request) │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[map.id]; │ │ │ │ + if (cache) { │ │ │ │ + if (cache.count <= 1) { │ │ │ │ + this.removeGMapElements(); │ │ │ │ + delete OpenLayers.Layer.Google.cache[map.id] │ │ │ │ + } else { │ │ │ │ + --cache.count │ │ │ │ } │ │ │ │ } │ │ │ │ - if (request.status && (request.status < 200 || request.status >= 300)) { │ │ │ │ - this.events.triggerEvent("failure", options); │ │ │ │ - if (failure) { │ │ │ │ - failure(request) │ │ │ │ + delete this.termsOfUse; │ │ │ │ + delete this.poweredBy; │ │ │ │ + delete this.mapObject; │ │ │ │ + delete this.dragObject; │ │ │ │ + OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + getOLBoundsFromMapObjectBounds: function(moBounds) { │ │ │ │ + var olBounds = null; │ │ │ │ + if (moBounds != null) { │ │ │ │ + var sw = moBounds.getSouthWest(); │ │ │ │ + var ne = moBounds.getNorthEast(); │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + sw = this.forwardMercator(sw.lng(), sw.lat()); │ │ │ │ + ne = this.forwardMercator(ne.lng(), ne.lat()) │ │ │ │ + } else { │ │ │ │ + sw = new OpenLayers.LonLat(sw.lng(), sw.lat()); │ │ │ │ + ne = new OpenLayers.LonLat(ne.lng(), ne.lat()) │ │ │ │ } │ │ │ │ + olBounds = new OpenLayers.Bounds(sw.lon, sw.lat, ne.lon, ne.lat) │ │ │ │ } │ │ │ │ + return olBounds │ │ │ │ }, │ │ │ │ - GET: function(config) { │ │ │ │ - config = OpenLayers.Util.extend(config, { │ │ │ │ - method: "GET" │ │ │ │ - }); │ │ │ │ - return OpenLayers.Request.issue(config) │ │ │ │ + getWarningHTML: function() { │ │ │ │ + return OpenLayers.i18n("googleWarning") │ │ │ │ }, │ │ │ │ - POST: function(config) { │ │ │ │ - config = OpenLayers.Util.extend(config, { │ │ │ │ - method: "POST" │ │ │ │ - }); │ │ │ │ - config.headers = config.headers ? config.headers : {}; │ │ │ │ - if (!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) { │ │ │ │ - config.headers["Content-Type"] = "application/xml" │ │ │ │ - } │ │ │ │ - return OpenLayers.Request.issue(config) │ │ │ │ + getMapObjectCenter: function() { │ │ │ │ + return this.mapObject.getCenter() │ │ │ │ }, │ │ │ │ - PUT: function(config) { │ │ │ │ - config = OpenLayers.Util.extend(config, { │ │ │ │ - method: "PUT" │ │ │ │ - }); │ │ │ │ - config.headers = config.headers ? config.headers : {}; │ │ │ │ - if (!("CONTENT-TYPE" in OpenLayers.Util.upperCaseObject(config.headers))) { │ │ │ │ - config.headers["Content-Type"] = "application/xml" │ │ │ │ - } │ │ │ │ - return OpenLayers.Request.issue(config) │ │ │ │ + getMapObjectZoom: function() { │ │ │ │ + return this.mapObject.getZoom() │ │ │ │ }, │ │ │ │ - DELETE: function(config) { │ │ │ │ - config = OpenLayers.Util.extend(config, { │ │ │ │ - method: "DELETE" │ │ │ │ - }); │ │ │ │ - return OpenLayers.Request.issue(config) │ │ │ │ + getLongitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + return this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : moLonLat.lng() │ │ │ │ }, │ │ │ │ - HEAD: function(config) { │ │ │ │ - config = OpenLayers.Util.extend(config, { │ │ │ │ - method: "HEAD" │ │ │ │ - }); │ │ │ │ - return OpenLayers.Request.issue(config) │ │ │ │ + getLatitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + var lat = this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : moLonLat.lat(); │ │ │ │ + return lat │ │ │ │ }, │ │ │ │ - OPTIONS: function(config) { │ │ │ │ - config = OpenLayers.Util.extend(config, { │ │ │ │ - method: "OPTIONS" │ │ │ │ - }); │ │ │ │ - return OpenLayers.Request.issue(config) │ │ │ │ - } │ │ │ │ + getXFromMapObjectPixel: function(moPixel) { │ │ │ │ + return moPixel.x │ │ │ │ + }, │ │ │ │ + getYFromMapObjectPixel: function(moPixel) { │ │ │ │ + return moPixel.y │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Google" │ │ │ │ }); │ │ │ │ -(function() { │ │ │ │ - var oXMLHttpRequest = window.XMLHttpRequest; │ │ │ │ - var bGecko = !!window.controllers, │ │ │ │ - bIE = window.document.all && !window.opera, │ │ │ │ - bIE7 = bIE && window.navigator.userAgent.match(/MSIE 7.0/); │ │ │ │ - │ │ │ │ - function fXMLHttpRequest() { │ │ │ │ - this._object = oXMLHttpRequest && !bIE7 ? new oXMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP"); │ │ │ │ - this._listeners = [] │ │ │ │ - } │ │ │ │ - │ │ │ │ - function cXMLHttpRequest() { │ │ │ │ - return new fXMLHttpRequest │ │ │ │ - } │ │ │ │ - cXMLHttpRequest.prototype = fXMLHttpRequest.prototype; │ │ │ │ - if (bGecko && oXMLHttpRequest.wrapped) cXMLHttpRequest.wrapped = oXMLHttpRequest.wrapped; │ │ │ │ - cXMLHttpRequest.UNSENT = 0; │ │ │ │ - cXMLHttpRequest.OPENED = 1; │ │ │ │ - cXMLHttpRequest.HEADERS_RECEIVED = 2; │ │ │ │ - cXMLHttpRequest.LOADING = 3; │ │ │ │ - cXMLHttpRequest.DONE = 4; │ │ │ │ - cXMLHttpRequest.prototype.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ - cXMLHttpRequest.prototype.responseText = ""; │ │ │ │ - cXMLHttpRequest.prototype.responseXML = null; │ │ │ │ - cXMLHttpRequest.prototype.status = 0; │ │ │ │ - cXMLHttpRequest.prototype.statusText = ""; │ │ │ │ - cXMLHttpRequest.prototype.priority = "NORMAL"; │ │ │ │ - cXMLHttpRequest.prototype.onreadystatechange = null; │ │ │ │ - cXMLHttpRequest.onreadystatechange = null; │ │ │ │ - cXMLHttpRequest.onopen = null; │ │ │ │ - cXMLHttpRequest.onsend = null; │ │ │ │ - cXMLHttpRequest.onabort = null; │ │ │ │ - cXMLHttpRequest.prototype.open = function(sMethod, sUrl, bAsync, sUser, sPassword) { │ │ │ │ - delete this._headers; │ │ │ │ - if (arguments.length < 3) bAsync = true; │ │ │ │ - this._async = bAsync; │ │ │ │ - var oRequest = this, │ │ │ │ - nState = this.readyState, │ │ │ │ - fOnUnload; │ │ │ │ - if (bIE && bAsync) { │ │ │ │ - fOnUnload = function() { │ │ │ │ - if (nState != cXMLHttpRequest.DONE) { │ │ │ │ - fCleanTransport(oRequest); │ │ │ │ - oRequest.abort() │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - window.attachEvent("onunload", fOnUnload) │ │ │ │ +OpenLayers.Layer.Google.cache = {}; │ │ │ │ +OpenLayers.Layer.Google.v2 = { │ │ │ │ + termsOfUse: null, │ │ │ │ + poweredBy: null, │ │ │ │ + dragObject: null, │ │ │ │ + loadMapObject: function() { │ │ │ │ + if (!this.type) { │ │ │ │ + this.type = G_NORMAL_MAP │ │ │ │ } │ │ │ │ - if (cXMLHttpRequest.onopen) cXMLHttpRequest.onopen.apply(this, arguments); │ │ │ │ - if (arguments.length > 4) this._object.open(sMethod, sUrl, bAsync, sUser, sPassword); │ │ │ │ - else if (arguments.length > 3) this._object.open(sMethod, sUrl, bAsync, sUser); │ │ │ │ - else this._object.open(sMethod, sUrl, bAsync); │ │ │ │ - this.readyState = cXMLHttpRequest.OPENED; │ │ │ │ - fReadyStateChange(this); │ │ │ │ - this._object.onreadystatechange = function() { │ │ │ │ - if (bGecko && !bAsync) return; │ │ │ │ - oRequest.readyState = oRequest._object.readyState; │ │ │ │ - fSynchronizeValues(oRequest); │ │ │ │ - if (oRequest._aborted) { │ │ │ │ - oRequest.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ - return │ │ │ │ + var mapObject, termsOfUse, poweredBy; │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache) { │ │ │ │ + mapObject = cache.mapObject; │ │ │ │ + termsOfUse = cache.termsOfUse; │ │ │ │ + poweredBy = cache.poweredBy; │ │ │ │ + ++cache.count │ │ │ │ + } else { │ │ │ │ + var container = this.map.viewPortDiv; │ │ │ │ + var div = document.createElement("div"); │ │ │ │ + div.id = this.map.id + "_GMap2Container"; │ │ │ │ + div.style.position = "absolute"; │ │ │ │ + div.style.width = "100%"; │ │ │ │ + div.style.height = "100%"; │ │ │ │ + container.appendChild(div); │ │ │ │ + try { │ │ │ │ + mapObject = new GMap2(div); │ │ │ │ + termsOfUse = div.lastChild; │ │ │ │ + container.appendChild(termsOfUse); │ │ │ │ + termsOfUse.style.zIndex = "1100"; │ │ │ │ + termsOfUse.style.right = ""; │ │ │ │ + termsOfUse.style.bottom = ""; │ │ │ │ + termsOfUse.className = "olLayerGoogleCopyright"; │ │ │ │ + poweredBy = div.lastChild; │ │ │ │ + container.appendChild(poweredBy); │ │ │ │ + poweredBy.style.zIndex = "1100"; │ │ │ │ + poweredBy.style.right = ""; │ │ │ │ + poweredBy.style.bottom = ""; │ │ │ │ + poweredBy.className = "olLayerGooglePoweredBy gmnoprint" │ │ │ │ + } catch (e) { │ │ │ │ + throw e │ │ │ │ } │ │ │ │ - if (oRequest.readyState == cXMLHttpRequest.DONE) { │ │ │ │ - delete oRequest._data; │ │ │ │ - fCleanTransport(oRequest); │ │ │ │ - if (bIE && bAsync) window.detachEvent("onunload", fOnUnload) │ │ │ │ + OpenLayers.Layer.Google.cache[this.map.id] = { │ │ │ │ + mapObject: mapObject, │ │ │ │ + termsOfUse: termsOfUse, │ │ │ │ + poweredBy: poweredBy, │ │ │ │ + count: 1 │ │ │ │ } │ │ │ │ - if (nState != oRequest.readyState) fReadyStateChange(oRequest); │ │ │ │ - nState = oRequest.readyState │ │ │ │ } │ │ │ │ - }; │ │ │ │ - │ │ │ │ - function fXMLHttpRequest_send(oRequest) { │ │ │ │ - oRequest._object.send(oRequest._data); │ │ │ │ - if (bGecko && !oRequest._async) { │ │ │ │ - oRequest.readyState = cXMLHttpRequest.OPENED; │ │ │ │ - fSynchronizeValues(oRequest); │ │ │ │ - while (oRequest.readyState < cXMLHttpRequest.DONE) { │ │ │ │ - oRequest.readyState++; │ │ │ │ - fReadyStateChange(oRequest); │ │ │ │ - if (oRequest._aborted) return │ │ │ │ - } │ │ │ │ + this.mapObject = mapObject; │ │ │ │ + this.termsOfUse = termsOfUse; │ │ │ │ + this.poweredBy = poweredBy; │ │ │ │ + if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), this.type) === -1) { │ │ │ │ + this.mapObject.addMapType(this.type) │ │ │ │ } │ │ │ │ - } │ │ │ │ - cXMLHttpRequest.prototype.send = function(vData) { │ │ │ │ - if (cXMLHttpRequest.onsend) cXMLHttpRequest.onsend.apply(this, arguments); │ │ │ │ - if (!arguments.length) vData = null; │ │ │ │ - if (vData && vData.nodeType) { │ │ │ │ - vData = window.XMLSerializer ? (new window.XMLSerializer).serializeToString(vData) : vData.xml; │ │ │ │ - if (!this._headers["Content-Type"]) this._object.setRequestHeader("Content-Type", "application/xml") │ │ │ │ + if (typeof mapObject.getDragObject == "function") { │ │ │ │ + this.dragObject = mapObject.getDragObject() │ │ │ │ + } else { │ │ │ │ + this.dragPanMapObject = null │ │ │ │ } │ │ │ │ - this._data = vData; │ │ │ │ - fXMLHttpRequest_send(this) │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.abort = function() { │ │ │ │ - if (cXMLHttpRequest.onabort) cXMLHttpRequest.onabort.apply(this, arguments); │ │ │ │ - if (this.readyState > cXMLHttpRequest.UNSENT) this._aborted = true; │ │ │ │ - this._object.abort(); │ │ │ │ - fCleanTransport(this); │ │ │ │ - this.readyState = cXMLHttpRequest.UNSENT; │ │ │ │ - delete this._data │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.getAllResponseHeaders = function() { │ │ │ │ - return this._object.getAllResponseHeaders() │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.getResponseHeader = function(sName) { │ │ │ │ - return this._object.getResponseHeader(sName) │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.setRequestHeader = function(sName, sValue) { │ │ │ │ - if (!this._headers) this._headers = {}; │ │ │ │ - this._headers[sName] = sValue; │ │ │ │ - return this._object.setRequestHeader(sName, sValue) │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.addEventListener = function(sName, fHandler, bUseCapture) { │ │ │ │ - for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) │ │ │ │ - if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) return; │ │ │ │ - this._listeners.push([sName, fHandler, bUseCapture]) │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.removeEventListener = function(sName, fHandler, bUseCapture) { │ │ │ │ - for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) │ │ │ │ - if (oListener[0] == sName && oListener[1] == fHandler && oListener[2] == bUseCapture) break; │ │ │ │ - if (oListener) this._listeners.splice(nIndex, 1) │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.dispatchEvent = function(oEvent) { │ │ │ │ - var oEventPseudo = { │ │ │ │ - type: oEvent.type, │ │ │ │ - target: this, │ │ │ │ - currentTarget: this, │ │ │ │ - eventPhase: 2, │ │ │ │ - bubbles: oEvent.bubbles, │ │ │ │ - cancelable: oEvent.cancelable, │ │ │ │ - timeStamp: oEvent.timeStamp, │ │ │ │ - stopPropagation: function() {}, │ │ │ │ - preventDefault: function() {}, │ │ │ │ - initEvent: function() {} │ │ │ │ - }; │ │ │ │ - if (oEventPseudo.type == "readystatechange" && this.onreadystatechange)(this.onreadystatechange.handleEvent || this.onreadystatechange).apply(this, [oEventPseudo]); │ │ │ │ - for (var nIndex = 0, oListener; oListener = this._listeners[nIndex]; nIndex++) │ │ │ │ - if (oListener[0] == oEventPseudo.type && !oListener[2])(oListener[1].handleEvent || oListener[1]).apply(this, [oEventPseudo]) │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.prototype.toString = function() { │ │ │ │ - return "[" + "object" + " " + "XMLHttpRequest" + "]" │ │ │ │ - }; │ │ │ │ - cXMLHttpRequest.toString = function() { │ │ │ │ - return "[" + "XMLHttpRequest" + "]" │ │ │ │ - }; │ │ │ │ - │ │ │ │ - function fReadyStateChange(oRequest) { │ │ │ │ - if (cXMLHttpRequest.onreadystatechange) cXMLHttpRequest.onreadystatechange.apply(oRequest); │ │ │ │ - oRequest.dispatchEvent({ │ │ │ │ - type: "readystatechange", │ │ │ │ - bubbles: false, │ │ │ │ - cancelable: false, │ │ │ │ - timeStamp: new Date + 0 │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - │ │ │ │ - function fGetDocument(oRequest) { │ │ │ │ - var oDocument = oRequest.responseXML, │ │ │ │ - sResponse = oRequest.responseText; │ │ │ │ - if (bIE && sResponse && oDocument && !oDocument.documentElement && oRequest.getResponseHeader("Content-Type").match(/[^\/]+\/[^\+]+\+xml/)) { │ │ │ │ - oDocument = new window.ActiveXObject("Microsoft.XMLDOM"); │ │ │ │ - oDocument.async = false; │ │ │ │ - oDocument.validateOnParse = false; │ │ │ │ - oDocument.loadXML(sResponse) │ │ │ │ + if (this.isBaseLayer === false) { │ │ │ │ + this.setGMapVisibility(this.div.style.display !== "none") │ │ │ │ } │ │ │ │ - if (oDocument) │ │ │ │ - if (bIE && oDocument.parseError != 0 || !oDocument.documentElement || oDocument.documentElement && oDocument.documentElement.tagName == "parsererror") return null; │ │ │ │ - return oDocument │ │ │ │ - } │ │ │ │ - │ │ │ │ - function fSynchronizeValues(oRequest) { │ │ │ │ - try { │ │ │ │ - oRequest.responseText = oRequest._object.responseText │ │ │ │ - } catch (e) {} │ │ │ │ - try { │ │ │ │ - oRequest.responseXML = fGetDocument(oRequest._object) │ │ │ │ - } catch (e) {} │ │ │ │ - try { │ │ │ │ - oRequest.status = oRequest._object.status │ │ │ │ - } catch (e) {} │ │ │ │ - try { │ │ │ │ - oRequest.statusText = oRequest._object.statusText │ │ │ │ - } catch (e) {} │ │ │ │ - } │ │ │ │ - │ │ │ │ - function fCleanTransport(oRequest) { │ │ │ │ - oRequest._object.onreadystatechange = new window.Function │ │ │ │ - } │ │ │ │ - if (!window.Function.prototype.apply) { │ │ │ │ - window.Function.prototype.apply = function(oRequest, oArguments) { │ │ │ │ - if (!oArguments) oArguments = []; │ │ │ │ - oRequest.__func = this; │ │ │ │ - oRequest.__func(oArguments[0], oArguments[1], oArguments[2], oArguments[3], oArguments[4]); │ │ │ │ - delete oRequest.__func │ │ │ │ + }, │ │ │ │ + onMapResize: function() { │ │ │ │ + if (this.visibility && this.mapObject.isLoaded()) { │ │ │ │ + this.mapObject.checkResize() │ │ │ │ + } else { │ │ │ │ + if (!this._resized) { │ │ │ │ + var layer = this; │ │ │ │ + var handle = GEvent.addListener(this.mapObject, "load", function() { │ │ │ │ + GEvent.removeListener(handle); │ │ │ │ + delete layer._resized; │ │ │ │ + layer.mapObject.checkResize(); │ │ │ │ + layer.moveTo(layer.map.getCenter(), layer.map.getZoom()) │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + this._resized = true │ │ │ │ } │ │ │ │ - } │ │ │ │ - if (!OpenLayers.Request) { │ │ │ │ - OpenLayers.Request = {} │ │ │ │ - } │ │ │ │ - OpenLayers.Request.XMLHttpRequest = cXMLHttpRequest │ │ │ │ -})(); │ │ │ │ -OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ - url: null, │ │ │ │ - headers: null, │ │ │ │ - params: null, │ │ │ │ - callback: null, │ │ │ │ - scope: null, │ │ │ │ - readWithPOST: false, │ │ │ │ - updateWithPOST: false, │ │ │ │ - deleteWithPOST: false, │ │ │ │ - wildcarded: false, │ │ │ │ - srsInBBOX: false, │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - this.params = {}; │ │ │ │ - this.headers = {}; │ │ │ │ - OpenLayers.Protocol.prototype.initialize.apply(this, arguments); │ │ │ │ - if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { │ │ │ │ - var format = new OpenLayers.Format.QueryStringFilter({ │ │ │ │ - wildcarded: this.wildcarded, │ │ │ │ - srsInBBOX: this.srsInBBOX │ │ │ │ - }); │ │ │ │ - this.filterToParams = function(filter, params) { │ │ │ │ - return format.write(filter, params) │ │ │ │ + }, │ │ │ │ + setGMapVisibility: function(visible) { │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache) { │ │ │ │ + var container = this.mapObject.getContainer(); │ │ │ │ + if (visible === true) { │ │ │ │ + this.mapObject.setMapType(this.type); │ │ │ │ + container.style.display = ""; │ │ │ │ + this.termsOfUse.style.left = ""; │ │ │ │ + this.termsOfUse.style.display = ""; │ │ │ │ + this.poweredBy.style.display = ""; │ │ │ │ + cache.displayed = this.id │ │ │ │ + } else { │ │ │ │ + if (cache.displayed === this.id) { │ │ │ │ + delete cache.displayed │ │ │ │ + } │ │ │ │ + if (!cache.displayed) { │ │ │ │ + container.style.display = "none"; │ │ │ │ + this.termsOfUse.style.display = "none"; │ │ │ │ + this.termsOfUse.style.left = "-9999px"; │ │ │ │ + this.poweredBy.style.display = "none" │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - this.params = null; │ │ │ │ - this.headers = null; │ │ │ │ - OpenLayers.Protocol.prototype.destroy.apply(this) │ │ │ │ + getMapContainer: function() { │ │ │ │ + return this.mapObject.getContainer() │ │ │ │ }, │ │ │ │ - read: function(options) { │ │ │ │ - OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ - options = options || {}; │ │ │ │ - options.params = OpenLayers.Util.applyDefaults(options.params, this.options.params); │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - if (options.filter && this.filterToParams) { │ │ │ │ - options.params = this.filterToParams(options.filter, options.params) │ │ │ │ - } │ │ │ │ - var readWithPOST = options.readWithPOST !== undefined ? options.readWithPOST : this.readWithPOST; │ │ │ │ - var resp = new OpenLayers.Protocol.Response({ │ │ │ │ - requestType: "read" │ │ │ │ - }); │ │ │ │ - if (readWithPOST) { │ │ │ │ - var headers = options.headers || {}; │ │ │ │ - headers["Content-Type"] = "application/x-www-form-urlencoded"; │ │ │ │ - resp.priv = OpenLayers.Request.POST({ │ │ │ │ - url: options.url, │ │ │ │ - callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ - data: OpenLayers.Util.getParameterString(options.params), │ │ │ │ - headers: headers │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - resp.priv = OpenLayers.Request.GET({ │ │ │ │ - url: options.url, │ │ │ │ - callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ - params: options.params, │ │ │ │ - headers: options.headers │ │ │ │ - }) │ │ │ │ + getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ + var moBounds = null; │ │ │ │ + if (olBounds != null) { │ │ │ │ + var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ + var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ + moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), new GLatLng(ne.lat, ne.lon)) │ │ │ │ } │ │ │ │ - return resp │ │ │ │ + return moBounds │ │ │ │ }, │ │ │ │ - handleRead: function(resp, options) { │ │ │ │ - this.handleResponse(resp, options) │ │ │ │ + setMapObjectCenter: function(center, zoom) { │ │ │ │ + this.mapObject.setCenter(center, zoom) │ │ │ │ }, │ │ │ │ - create: function(features, options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - var resp = new OpenLayers.Protocol.Response({ │ │ │ │ - reqFeatures: features, │ │ │ │ - requestType: "create" │ │ │ │ - }); │ │ │ │ - resp.priv = OpenLayers.Request.POST({ │ │ │ │ - url: options.url, │ │ │ │ - callback: this.createCallback(this.handleCreate, resp, options), │ │ │ │ - headers: options.headers, │ │ │ │ - data: this.format.write(features) │ │ │ │ - }); │ │ │ │ - return resp │ │ │ │ + dragPanMapObject: function(dX, dY) { │ │ │ │ + this.dragObject.moveBy(new GSize(-dX, dY)) │ │ │ │ }, │ │ │ │ - handleCreate: function(resp, options) { │ │ │ │ - this.handleResponse(resp, options) │ │ │ │ + getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ + return this.mapObject.fromContainerPixelToLatLng(moPixel) │ │ │ │ }, │ │ │ │ - update: function(feature, options) { │ │ │ │ - options = options || {}; │ │ │ │ - var url = options.url || feature.url || this.options.url + "/" + feature.fid; │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - var resp = new OpenLayers.Protocol.Response({ │ │ │ │ - reqFeatures: feature, │ │ │ │ - requestType: "update" │ │ │ │ - }); │ │ │ │ - var method = this.updateWithPOST ? "POST" : "PUT"; │ │ │ │ - resp.priv = OpenLayers.Request[method]({ │ │ │ │ - url: url, │ │ │ │ - callback: this.createCallback(this.handleUpdate, resp, options), │ │ │ │ - headers: options.headers, │ │ │ │ - data: this.format.write(feature) │ │ │ │ - }); │ │ │ │ - return resp │ │ │ │ + getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + return this.mapObject.fromLatLngToContainerPixel(moLonLat) │ │ │ │ }, │ │ │ │ - handleUpdate: function(resp, options) { │ │ │ │ - this.handleResponse(resp, options) │ │ │ │ + getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ + return this.mapObject.getBoundsZoomLevel(moBounds) │ │ │ │ }, │ │ │ │ - delete: function(feature, options) { │ │ │ │ - options = options || {}; │ │ │ │ - var url = options.url || feature.url || this.options.url + "/" + feature.fid; │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - var resp = new OpenLayers.Protocol.Response({ │ │ │ │ - reqFeatures: feature, │ │ │ │ - requestType: "delete" │ │ │ │ - }); │ │ │ │ - var method = this.deleteWithPOST ? "POST" : "DELETE"; │ │ │ │ - var requestOptions = { │ │ │ │ - url: url, │ │ │ │ - callback: this.createCallback(this.handleDelete, resp, options), │ │ │ │ - headers: options.headers │ │ │ │ - }; │ │ │ │ - if (this.deleteWithPOST) { │ │ │ │ - requestOptions.data = this.format.write(feature) │ │ │ │ + getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ + var gLatLng; │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + var lonlat = this.inverseMercator(lon, lat); │ │ │ │ + gLatLng = new GLatLng(lonlat.lat, lonlat.lon) │ │ │ │ + } else { │ │ │ │ + gLatLng = new GLatLng(lat, lon) │ │ │ │ } │ │ │ │ - resp.priv = OpenLayers.Request[method](requestOptions); │ │ │ │ - return resp │ │ │ │ - }, │ │ │ │ - handleDelete: function(resp, options) { │ │ │ │ - this.handleResponse(resp, options) │ │ │ │ + return gLatLng │ │ │ │ }, │ │ │ │ - handleResponse: function(resp, options) { │ │ │ │ - var request = resp.priv; │ │ │ │ - if (options.callback) { │ │ │ │ - if (request.status >= 200 && request.status < 300) { │ │ │ │ - if (resp.requestType != "delete") { │ │ │ │ - resp.features = this.parseFeatures(request) │ │ │ │ - } │ │ │ │ - resp.code = OpenLayers.Protocol.Response.SUCCESS │ │ │ │ - } else { │ │ │ │ - resp.code = OpenLayers.Protocol.Response.FAILURE │ │ │ │ - } │ │ │ │ - options.callback.call(options.scope, resp) │ │ │ │ - } │ │ │ │ + getMapObjectPixelFromXY: function(x, y) { │ │ │ │ + return new GPoint(x, y) │ │ │ │ + } │ │ │ │ +}; │ │ │ │ +OpenLayers.Layer.Google.v3 = { │ │ │ │ + DEFAULTS: { │ │ │ │ + sphericalMercator: true, │ │ │ │ + projection: "EPSG:900913" │ │ │ │ }, │ │ │ │ - parseFeatures: function(request) { │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText │ │ │ │ + animationEnabled: true, │ │ │ │ + loadMapObject: function() { │ │ │ │ + if (!this.type) { │ │ │ │ + this.type = google.maps.MapTypeId.ROADMAP │ │ │ │ } │ │ │ │ - if (!doc || doc.length <= 0) { │ │ │ │ - return null │ │ │ │ + var mapObject; │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + if (cache) { │ │ │ │ + mapObject = cache.mapObject; │ │ │ │ + ++cache.count │ │ │ │ + } else { │ │ │ │ + var center = this.map.getCenter(); │ │ │ │ + var container = document.createElement("div"); │ │ │ │ + container.className = "olForeignContainer"; │ │ │ │ + container.style.width = "100%"; │ │ │ │ + container.style.height = "100%"; │ │ │ │ + mapObject = new google.maps.Map(container, { │ │ │ │ + center: center ? new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0), │ │ │ │ + zoom: this.map.getZoom() || 0, │ │ │ │ + mapTypeId: this.type, │ │ │ │ + disableDefaultUI: true, │ │ │ │ + keyboardShortcuts: false, │ │ │ │ + draggable: false, │ │ │ │ + disableDoubleClickZoom: true, │ │ │ │ + scrollwheel: false, │ │ │ │ + streetViewControl: false │ │ │ │ + }); │ │ │ │ + var googleControl = document.createElement("div"); │ │ │ │ + googleControl.style.width = "100%"; │ │ │ │ + googleControl.style.height = "100%"; │ │ │ │ + mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl); │ │ │ │ + cache = { │ │ │ │ + googleControl: googleControl, │ │ │ │ + mapObject: mapObject, │ │ │ │ + count: 1 │ │ │ │ + }; │ │ │ │ + OpenLayers.Layer.Google.cache[this.map.id] = cache │ │ │ │ } │ │ │ │ - return this.format.read(doc) │ │ │ │ + this.mapObject = mapObject; │ │ │ │ + this.setGMapVisibility(this.visibility) │ │ │ │ }, │ │ │ │ - commit: function(features, options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - var resp = [], │ │ │ │ - nResponses = 0; │ │ │ │ - var types = {}; │ │ │ │ - types[OpenLayers.State.INSERT] = []; │ │ │ │ - types[OpenLayers.State.UPDATE] = []; │ │ │ │ - types[OpenLayers.State.DELETE] = []; │ │ │ │ - var feature, list, requestFeatures = []; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - feature = features[i]; │ │ │ │ - list = types[feature.state]; │ │ │ │ - if (list) { │ │ │ │ - list.push(feature); │ │ │ │ - requestFeatures.push(feature) │ │ │ │ - } │ │ │ │ + onMapResize: function() { │ │ │ │ + if (this.visibility) { │ │ │ │ + google.maps.event.trigger(this.mapObject, "resize") │ │ │ │ } │ │ │ │ - var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + types[OpenLayers.State.UPDATE].length + types[OpenLayers.State.DELETE].length; │ │ │ │ - var success = true; │ │ │ │ - var finalResponse = new OpenLayers.Protocol.Response({ │ │ │ │ - reqFeatures: requestFeatures │ │ │ │ - }); │ │ │ │ - │ │ │ │ - function insertCallback(response) { │ │ │ │ - var len = response.features ? response.features.length : 0; │ │ │ │ - var fids = new Array(len); │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ - fids[i] = response.features[i].fid │ │ │ │ + }, │ │ │ │ + setGMapVisibility: function(visible) { │ │ │ │ + var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ + var map = this.map; │ │ │ │ + if (cache) { │ │ │ │ + var type = this.type; │ │ │ │ + var layers = map.layers; │ │ │ │ + var layer; │ │ │ │ + for (var i = layers.length - 1; i >= 0; --i) { │ │ │ │ + layer = layers[i]; │ │ │ │ + if (layer instanceof OpenLayers.Layer.Google && layer.visibility === true && layer.inRange === true) { │ │ │ │ + type = layer.type; │ │ │ │ + visible = true; │ │ │ │ + break │ │ │ │ + } │ │ │ │ } │ │ │ │ - finalResponse.insertIds = fids; │ │ │ │ - callback.apply(this, [response]) │ │ │ │ - } │ │ │ │ - │ │ │ │ - function callback(response) { │ │ │ │ - this.callUserCallback(response, options); │ │ │ │ - success = success && response.success(); │ │ │ │ - nResponses++; │ │ │ │ - if (nResponses >= nRequests) { │ │ │ │ - if (options.callback) { │ │ │ │ - finalResponse.code = success ? OpenLayers.Protocol.Response.SUCCESS : OpenLayers.Protocol.Response.FAILURE; │ │ │ │ - options.callback.apply(options.scope, [finalResponse]) │ │ │ │ + var container = this.mapObject.getDiv(); │ │ │ │ + if (visible === true) { │ │ │ │ + if (container.parentNode !== map.div) { │ │ │ │ + if (!cache.rendered) { │ │ │ │ + var me = this; │ │ │ │ + google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function() { │ │ │ │ + cache.rendered = true; │ │ │ │ + me.setGMapVisibility(me.getVisibility()); │ │ │ │ + me.moveTo(me.map.getCenter()) │ │ │ │ + }) │ │ │ │ + } else { │ │ │ │ + map.div.appendChild(container); │ │ │ │ + cache.googleControl.appendChild(map.viewPortDiv); │ │ │ │ + google.maps.event.trigger(this.mapObject, "resize") │ │ │ │ + } │ │ │ │ } │ │ │ │ + this.mapObject.setMapTypeId(type) │ │ │ │ + } else if (cache.googleControl.hasChildNodes()) { │ │ │ │ + map.div.appendChild(map.viewPortDiv); │ │ │ │ + map.div.removeChild(container) │ │ │ │ } │ │ │ │ } │ │ │ │ - var queue = types[OpenLayers.State.INSERT]; │ │ │ │ - if (queue.length > 0) { │ │ │ │ - resp.push(this.create(queue, OpenLayers.Util.applyDefaults({ │ │ │ │ - callback: insertCallback, │ │ │ │ - scope: this │ │ │ │ - }, options.create))) │ │ │ │ - } │ │ │ │ - queue = types[OpenLayers.State.UPDATE]; │ │ │ │ - for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ - resp.push(this.update(queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ - callback: callback, │ │ │ │ - scope: this │ │ │ │ - }, options.update))) │ │ │ │ - } │ │ │ │ - queue = types[OpenLayers.State.DELETE]; │ │ │ │ - for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ - resp.push(this["delete"](queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ - callback: callback, │ │ │ │ - scope: this │ │ │ │ - }, options["delete"]))) │ │ │ │ - } │ │ │ │ - return resp │ │ │ │ }, │ │ │ │ - abort: function(response) { │ │ │ │ - if (response) { │ │ │ │ - response.priv.abort() │ │ │ │ - } │ │ │ │ + getMapContainer: function() { │ │ │ │ + return this.mapObject.getDiv() │ │ │ │ }, │ │ │ │ - callUserCallback: function(resp, options) { │ │ │ │ - var opt = options[resp.requestType]; │ │ │ │ - if (opt && opt.callback) { │ │ │ │ - opt.callback.call(opt.scope, resp) │ │ │ │ + getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ + var moBounds = null; │ │ │ │ + if (olBounds != null) { │ │ │ │ + var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ + var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ + moBounds = new google.maps.LatLngBounds(new google.maps.LatLng(sw.lat, sw.lon), new google.maps.LatLng(ne.lat, ne.lon)) │ │ │ │ } │ │ │ │ + return moBounds │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.HTTP" │ │ │ │ -}); │ │ │ │ -OpenLayers.Filter.Logical = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ - filters: null, │ │ │ │ - type: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - this.filters = []; │ │ │ │ - OpenLayers.Filter.prototype.initialize.apply(this, [options]) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.filters = null; │ │ │ │ - OpenLayers.Filter.prototype.destroy.apply(this) │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ + getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ + var size = this.map.getSize(); │ │ │ │ + var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ + var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ + var res = this.map.getResolution(); │ │ │ │ + var delta_x = moPixel.x - size.w / 2; │ │ │ │ + var delta_y = moPixel.y - size.h / 2; │ │ │ │ + var lonlat = new OpenLayers.LonLat(lon + delta_x * res, lat - delta_y * res); │ │ │ │ + if (this.wrapDateLine) { │ │ │ │ + lonlat = lonlat.wrapDateLine(this.maxExtent) │ │ │ │ } │ │ │ │ - return undefined │ │ │ │ + return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat) │ │ │ │ }, │ │ │ │ - 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 │ │ │ │ - }) │ │ │ │ + getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ + var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ + var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ + var res = this.map.getResolution(); │ │ │ │ + var extent = this.map.getExtent(); │ │ │ │ + return this.getMapObjectPixelFromXY(1 / res * (lon - extent.left), 1 / res * (extent.top - lat)) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Filter.Logical" │ │ │ │ -}); │ │ │ │ -OpenLayers.Filter.Logical.AND = "&&"; │ │ │ │ -OpenLayers.Filter.Logical.OR = "||"; │ │ │ │ -OpenLayers.Filter.Logical.NOT = "!"; │ │ │ │ -OpenLayers.Filter.Comparison = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ - type: null, │ │ │ │ - property: null, │ │ │ │ - value: null, │ │ │ │ - matchCase: true, │ │ │ │ - lowerBoundary: null, │ │ │ │ - upperBoundary: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Filter.prototype.initialize.apply(this, [options]); │ │ │ │ - if (this.type === OpenLayers.Filter.Comparison.LIKE && options.matchCase === undefined) { │ │ │ │ - this.matchCase = null │ │ │ │ + setMapObjectCenter: function(center, zoom) { │ │ │ │ + if (this.animationEnabled === false && zoom != this.mapObject.zoom) { │ │ │ │ + var mapContainer = this.getMapContainer(); │ │ │ │ + google.maps.event.addListenerOnce(this.mapObject, "idle", function() { │ │ │ │ + mapContainer.style.visibility = "" │ │ │ │ + }); │ │ │ │ + mapContainer.style.visibility = "hidden" │ │ │ │ } │ │ │ │ + this.mapObject.setOptions({ │ │ │ │ + center: center, │ │ │ │ + zoom: zoom │ │ │ │ + }) │ │ │ │ }, │ │ │ │ - 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 │ │ │ │ + getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ + return this.mapObject.getBoundsZoomLevel(moBounds) │ │ │ │ }, │ │ │ │ - value2regex: function(wildCard, singleChar, escapeChar) { │ │ │ │ - if (wildCard == ".") { │ │ │ │ - throw new Error("'.' is an unsupported wildCard character for " + "OpenLayers.Filter.Comparison") │ │ │ │ + getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ + var gLatLng; │ │ │ │ + if (this.sphericalMercator) { │ │ │ │ + var lonlat = this.inverseMercator(lon, lat); │ │ │ │ + gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon) │ │ │ │ + } else { │ │ │ │ + gLatLng = new google.maps.LatLng(lat, lon) │ │ │ │ } │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - regex2value: function() { │ │ │ │ - var value = this.value; │ │ │ │ - value = value.replace(/!/g, "!!"); │ │ │ │ - value = value.replace(/(\\)?\\\./g, function($0, $1) { │ │ │ │ - return $1 ? $0 : "!." │ │ │ │ - }); │ │ │ │ - value = value.replace(/(\\)?\\\*/g, function($0, $1) { │ │ │ │ - return $1 ? $0 : "!*" │ │ │ │ - }); │ │ │ │ - value = value.replace(/\\\\/g, "\\"); │ │ │ │ - value = value.replace(/\.\*/g, "*"); │ │ │ │ - return value │ │ │ │ - }, │ │ │ │ - clone: function() { │ │ │ │ - return OpenLayers.Util.extend(new OpenLayers.Filter.Comparison, this) │ │ │ │ + return gLatLng │ │ │ │ }, │ │ │ │ - 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"; │ │ │ │ + getMapObjectPixelFromXY: function(x, y) { │ │ │ │ + return new google.maps.Point(x, y) │ │ │ │ + } │ │ │ │ +}; │ │ │ │ OpenLayers.Popup = OpenLayers.Class({ │ │ │ │ events: null, │ │ │ │ id: "", │ │ │ │ lonlat: null, │ │ │ │ div: null, │ │ │ │ contentSize: null, │ │ │ │ size: null, │ │ │ │ @@ -14880,8 +12635,2253 @@ │ │ │ │ maxSize: new OpenLayers.Size(1200, 660), │ │ │ │ initialize: function(id, lonlat, contentSize, contentHTML, anchor, closeBox, closeBoxCallback) { │ │ │ │ this.imageSrc = OpenLayers.Util.getImageLocation("cloud-popup-relative.png"); │ │ │ │ OpenLayers.Popup.Framed.prototype.initialize.apply(this, arguments); │ │ │ │ this.contentDiv.className = this.contentDisplayClass │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.Popup.FramedCloud" │ │ │ │ +}); │ │ │ │ +OpenLayers.Control = OpenLayers.Class({ │ │ │ │ + id: null, │ │ │ │ + map: null, │ │ │ │ + div: null, │ │ │ │ + type: null, │ │ │ │ + allowSelection: false, │ │ │ │ + displayClass: "", │ │ │ │ + title: "", │ │ │ │ + autoActivate: false, │ │ │ │ + active: null, │ │ │ │ + handlerOptions: null, │ │ │ │ + handler: null, │ │ │ │ + eventListeners: null, │ │ │ │ + events: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + this.displayClass = this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, ""); │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.events = new OpenLayers.Events(this); │ │ │ │ + if (this.eventListeners instanceof Object) { │ │ │ │ + this.events.on(this.eventListeners) │ │ │ │ + } │ │ │ │ + if (this.id == null) { │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_") │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + if (this.events) { │ │ │ │ + if (this.eventListeners) { │ │ │ │ + this.events.un(this.eventListeners) │ │ │ │ + } │ │ │ │ + this.events.destroy(); │ │ │ │ + this.events = null │ │ │ │ + } │ │ │ │ + this.eventListeners = null; │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.destroy(); │ │ │ │ + this.handler = null │ │ │ │ + } │ │ │ │ + if (this.handlers) { │ │ │ │ + for (var key in this.handlers) { │ │ │ │ + if (this.handlers.hasOwnProperty(key) && typeof this.handlers[key].destroy == "function") { │ │ │ │ + this.handlers[key].destroy() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.handlers = null │ │ │ │ + } │ │ │ │ + if (this.map) { │ │ │ │ + this.map.removeControl(this); │ │ │ │ + this.map = null │ │ │ │ + } │ │ │ │ + this.div = null │ │ │ │ + }, │ │ │ │ + setMap: function(map) { │ │ │ │ + this.map = map; │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.setMap(map) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + draw: function(px) { │ │ │ │ + if (this.div == null) { │ │ │ │ + this.div = OpenLayers.Util.createDiv(this.id); │ │ │ │ + this.div.className = this.displayClass; │ │ │ │ + if (!this.allowSelection) { │ │ │ │ + this.div.className += " olControlNoSelect"; │ │ │ │ + this.div.setAttribute("unselectable", "on", 0); │ │ │ │ + this.div.onselectstart = OpenLayers.Function.False │ │ │ │ + } │ │ │ │ + if (this.title != "") { │ │ │ │ + this.div.title = this.title │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (px != null) { │ │ │ │ + this.position = px.clone() │ │ │ │ + } │ │ │ │ + this.moveTo(this.position); │ │ │ │ + return this.div │ │ │ │ + }, │ │ │ │ + moveTo: function(px) { │ │ │ │ + if (px != null && this.div != null) { │ │ │ │ + this.div.style.left = px.x + "px"; │ │ │ │ + this.div.style.top = px.y + "px" │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + activate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.activate() │ │ │ │ + } │ │ │ │ + this.active = true; │ │ │ │ + if (this.map) { │ │ │ │ + OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, "") + "Active") │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("activate"); │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.deactivate() │ │ │ │ + } │ │ │ │ + this.active = false; │ │ │ │ + if (this.map) { │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, "") + "Active") │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("deactivate"); │ │ │ │ + return true │ │ │ │ + } │ │ │ │ + return false │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Control" │ │ │ │ +}); │ │ │ │ +OpenLayers.Control.TYPE_BUTTON = 1; │ │ │ │ +OpenLayers.Control.TYPE_TOGGLE = 2; │ │ │ │ +OpenLayers.Control.TYPE_TOOL = 3; │ │ │ │ +OpenLayers.Handler = OpenLayers.Class({ │ │ │ │ + id: null, │ │ │ │ + control: null, │ │ │ │ + map: null, │ │ │ │ + keyMask: null, │ │ │ │ + active: false, │ │ │ │ + evt: null, │ │ │ │ + touch: false, │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.control = control; │ │ │ │ + this.callbacks = callbacks; │ │ │ │ + var map = this.map || control.map; │ │ │ │ + if (map) { │ │ │ │ + this.setMap(map) │ │ │ │ + } │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_") │ │ │ │ + }, │ │ │ │ + setMap: function(map) { │ │ │ │ + this.map = map │ │ │ │ + }, │ │ │ │ + checkModifiers: function(evt) { │ │ │ │ + if (this.keyMask == null) { │ │ │ │ + return true │ │ │ │ + } │ │ │ │ + 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); │ │ │ │ + return keyModifiers == this.keyMask │ │ │ │ + }, │ │ │ │ + activate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + if (!this.active) { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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]]) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + callback: function(name, args) { │ │ │ │ + if (name && this.callbacks[name]) { │ │ │ │ + this.callbacks[name].apply(this.control, args) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + register: function(name, method) { │ │ │ │ + this.map.events.registerPriority(name, this, method); │ │ │ │ + this.map.events.registerPriority(name, this, this.setEvent) │ │ │ │ + }, │ │ │ │ + unregister: function(name, method) { │ │ │ │ + this.map.events.unregister(name, this, method); │ │ │ │ + this.map.events.unregister(name, this, this.setEvent) │ │ │ │ + }, │ │ │ │ + setEvent: function(evt) { │ │ │ │ + this.evt = evt; │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ + this.control = this.map = null │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Handler" │ │ │ │ +}); │ │ │ │ +OpenLayers.Handler.MOD_NONE = 0; │ │ │ │ +OpenLayers.Handler.MOD_SHIFT = 1; │ │ │ │ +OpenLayers.Handler.MOD_CTRL = 2; │ │ │ │ +OpenLayers.Handler.MOD_ALT = 4; │ │ │ │ +OpenLayers.Handler.MOD_META = 8; │ │ │ │ +OpenLayers.Handler.Feature = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + EVENTMAP: { │ │ │ │ + click: { │ │ │ │ + in: "click", │ │ │ │ + out: "clickout" │ │ │ │ + }, │ │ │ │ + mousemove: { │ │ │ │ + in: "over", │ │ │ │ + out: "out" │ │ │ │ + }, │ │ │ │ + dblclick: { │ │ │ │ + in: "dblclick", │ │ │ │ + out: null │ │ │ │ + }, │ │ │ │ + mousedown: { │ │ │ │ + in: null, │ │ │ │ + out: null │ │ │ │ + }, │ │ │ │ + mouseup: { │ │ │ │ + in: null, │ │ │ │ + out: null │ │ │ │ + }, │ │ │ │ + touchstart: { │ │ │ │ + in: "click", │ │ │ │ + out: "clickout" │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + feature: null, │ │ │ │ + lastFeature: null, │ │ │ │ + down: null, │ │ │ │ + up: null, │ │ │ │ + clickTolerance: 4, │ │ │ │ + geometryTypes: null, │ │ │ │ + stopClick: true, │ │ │ │ + stopDown: true, │ │ │ │ + stopUp: false, │ │ │ │ + initialize: function(control, layer, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, [control, callbacks, options]); │ │ │ │ + this.layer = layer │ │ │ │ + }, │ │ │ │ + touchstart: function(evt) { │ │ │ │ + this.startTouch(); │ │ │ │ + return OpenLayers.Event.isMultiTouch(evt) ? true : this.mousedown(evt) │ │ │ │ + }, │ │ │ │ + touchmove: function(evt) { │ │ │ │ + OpenLayers.Event.preventDefault(evt) │ │ │ │ + }, │ │ │ │ + mousedown: function(evt) { │ │ │ │ + if (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt)) { │ │ │ │ + this.down = evt.xy │ │ │ │ + } │ │ │ │ + return this.handle(evt) ? !this.stopDown : true │ │ │ │ + }, │ │ │ │ + mouseup: function(evt) { │ │ │ │ + this.up = evt.xy; │ │ │ │ + return this.handle(evt) ? !this.stopUp : true │ │ │ │ + }, │ │ │ │ + click: function(evt) { │ │ │ │ + return this.handle(evt) ? !this.stopClick : true │ │ │ │ + }, │ │ │ │ + mousemove: function(evt) { │ │ │ │ + if (!this.callbacks["over"] && !this.callbacks["out"]) { │ │ │ │ + return true │ │ │ │ + } │ │ │ │ + this.handle(evt); │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + dblclick: function(evt) { │ │ │ │ + return !this.handle(evt) │ │ │ │ + }, │ │ │ │ + geometryTypeMatches: function(feature) { │ │ │ │ + return this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1 │ │ │ │ + }, │ │ │ │ + handle: function(evt) { │ │ │ │ + if (this.feature && !this.feature.layer) { │ │ │ │ + this.feature = null │ │ │ │ + } │ │ │ │ + var type = evt.type; │ │ │ │ + var handled = false; │ │ │ │ + var previouslyIn = !!this.feature; │ │ │ │ + var click = type == "click" || type == "dblclick" || type == "touchstart"; │ │ │ │ + this.feature = this.layer.getFeatureFromEvent(evt); │ │ │ │ + if (this.feature && !this.feature.layer) { │ │ │ │ + this.feature = null │ │ │ │ + } │ │ │ │ + if (this.lastFeature && !this.lastFeature.layer) { │ │ │ │ + this.lastFeature = null │ │ │ │ + } │ │ │ │ + if (this.feature) { │ │ │ │ + if (type === "touchstart") { │ │ │ │ + OpenLayers.Event.preventDefault(evt) │ │ │ │ + } │ │ │ │ + var inNew = this.feature != this.lastFeature; │ │ │ │ + if (this.geometryTypeMatches(this.feature)) { │ │ │ │ + if (previouslyIn && inNew) { │ │ │ │ + if (this.lastFeature) { │ │ │ │ + this.triggerCallback(type, "out", [this.lastFeature]) │ │ │ │ + } │ │ │ │ + this.triggerCallback(type, "in", [this.feature]) │ │ │ │ + } else if (!previouslyIn || click) { │ │ │ │ + this.triggerCallback(type, "in", [this.feature]) │ │ │ │ + } │ │ │ │ + this.lastFeature = this.feature; │ │ │ │ + handled = true │ │ │ │ + } else { │ │ │ │ + if (this.lastFeature && (previouslyIn && inNew || click)) { │ │ │ │ + this.triggerCallback(type, "out", [this.lastFeature]) │ │ │ │ + } │ │ │ │ + this.feature = null │ │ │ │ + } │ │ │ │ + } else if (this.lastFeature && (previouslyIn || click)) { │ │ │ │ + this.triggerCallback(type, "out", [this.lastFeature]) │ │ │ │ + } │ │ │ │ + return handled │ │ │ │ + }, │ │ │ │ + triggerCallback: function(type, mode, args) { │ │ │ │ + var key = this.EVENTMAP[type][mode]; │ │ │ │ + if (key) { │ │ │ │ + if (type == "click" && this.up && this.down) { │ │ │ │ + var dpx = Math.sqrt(Math.pow(this.up.x - this.down.x, 2) + Math.pow(this.up.y - this.down.y, 2)); │ │ │ │ + if (dpx <= this.clickTolerance) { │ │ │ │ + this.callback(key, args) │ │ │ │ + } │ │ │ │ + this.up = this.down = null │ │ │ │ + } else { │ │ │ │ + this.callback(key, args) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + activate: function() { │ │ │ │ + var activated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.moveLayerToTop(); │ │ │ │ + this.map.events.on({ │ │ │ │ + removelayer: this.handleMapEvents, │ │ │ │ + changelayer: this.handleMapEvents, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + activated = true │ │ │ │ + } │ │ │ │ + return activated │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.moveLayerBack(); │ │ │ │ + this.feature = null; │ │ │ │ + this.lastFeature = null; │ │ │ │ + this.down = null; │ │ │ │ + this.up = null; │ │ │ │ + this.map.events.un({ │ │ │ │ + removelayer: this.handleMapEvents, │ │ │ │ + changelayer: this.handleMapEvents, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + deactivated = true │ │ │ │ + } │ │ │ │ + return deactivated │ │ │ │ + }, │ │ │ │ + handleMapEvents: function(evt) { │ │ │ │ + if (evt.type == "removelayer" || evt.property == "order") { │ │ │ │ + this.moveLayerToTop() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + moveLayerToTop: function() { │ │ │ │ + var index = Math.max(this.map.Z_INDEX_BASE["Feature"] - 1, this.layer.getZIndex()) + 1; │ │ │ │ + this.layer.setZIndex(index) │ │ │ │ + }, │ │ │ │ + moveLayerBack: function() { │ │ │ │ + var index = this.layer.getZIndex() - 1; │ │ │ │ + if (index >= this.map.Z_INDEX_BASE["Feature"]) { │ │ │ │ + this.layer.setZIndex(index) │ │ │ │ + } else { │ │ │ │ + this.map.setLayerZIndex(this.layer, this.map.getLayerIndex(this.layer)) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Feature" │ │ │ │ +}); │ │ │ │ +OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ + displayInLayerSwitcher: false, │ │ │ │ + layers: null, │ │ │ │ + display: function() {}, │ │ │ │ + getFeatureFromEvent: function(evt) { │ │ │ │ + var layers = this.layers; │ │ │ │ + var feature; │ │ │ │ + for (var i = 0; i < layers.length; i++) { │ │ │ │ + feature = layers[i].getFeatureFromEvent(evt); │ │ │ │ + if (feature) { │ │ │ │ + return feature │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); │ │ │ │ + this.collectRoots(); │ │ │ │ + map.events.register("changelayer", this, this.handleChangeLayer) │ │ │ │ + }, │ │ │ │ + removeMap: function(map) { │ │ │ │ + map.events.unregister("changelayer", this, this.handleChangeLayer); │ │ │ │ + this.resetRoots(); │ │ │ │ + OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + collectRoots: function() { │ │ │ │ + var layer; │ │ │ │ + for (var i = 0; i < this.map.layers.length; ++i) { │ │ │ │ + layer = this.map.layers[i]; │ │ │ │ + if (OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ + layer.renderer.moveRoot(this.renderer) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + resetRoots: function() { │ │ │ │ + var layer; │ │ │ │ + for (var i = 0; i < this.layers.length; ++i) { │ │ │ │ + layer = this.layers[i]; │ │ │ │ + if (this.renderer && layer.renderer.getRenderLayerId() == this.id) { │ │ │ │ + this.renderer.moveRoot(layer.renderer) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + handleChangeLayer: function(evt) { │ │ │ │ + var layer = evt.layer; │ │ │ │ + if (evt.property == "order" && OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ + this.resetRoots(); │ │ │ │ + this.collectRoots() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer" │ │ │ │ +}); │ │ │ │ +OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + multipleKey: null, │ │ │ │ + toggleKey: null, │ │ │ │ + multiple: false, │ │ │ │ + clickout: true, │ │ │ │ + toggle: false, │ │ │ │ + hover: false, │ │ │ │ + highlightOnly: false, │ │ │ │ + box: false, │ │ │ │ + onBeforeSelect: function() {}, │ │ │ │ + onSelect: function() {}, │ │ │ │ + onUnselect: function() {}, │ │ │ │ + scope: null, │ │ │ │ + geometryTypes: null, │ │ │ │ + layer: null, │ │ │ │ + layers: null, │ │ │ │ + callbacks: null, │ │ │ │ + selectStyle: null, │ │ │ │ + renderIntent: "select", │ │ │ │ + handlers: null, │ │ │ │ + initialize: function(layers, options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + if (this.scope === null) { │ │ │ │ + this.scope = this │ │ │ │ + } │ │ │ │ + this.initLayer(layers); │ │ │ │ + var callbacks = { │ │ │ │ + click: this.clickFeature, │ │ │ │ + clickout: this.clickoutFeature │ │ │ │ + }; │ │ │ │ + if (this.hover) { │ │ │ │ + callbacks.over = this.overFeature; │ │ │ │ + callbacks.out = this.outFeature │ │ │ │ + } │ │ │ │ + this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); │ │ │ │ + this.handlers = { │ │ │ │ + feature: new OpenLayers.Handler.Feature(this, this.layer, this.callbacks, { │ │ │ │ + geometryTypes: this.geometryTypes │ │ │ │ + }) │ │ │ │ + }; │ │ │ │ + if (this.box) { │ │ │ │ + this.handlers.box = new OpenLayers.Handler.Box(this, { │ │ │ │ + done: this.selectBox │ │ │ │ + }, { │ │ │ │ + boxDivClassName: "olHandlerBoxSelectFeature" │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + initLayer: function(layers) { │ │ │ │ + if (OpenLayers.Util.isArray(layers)) { │ │ │ │ + this.layers = layers; │ │ │ │ + this.layer = new OpenLayers.Layer.Vector.RootContainer(this.id + "_container", { │ │ │ │ + layers: layers │ │ │ │ + }) │ │ │ │ + } else { │ │ │ │ + this.layer = layers │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + if (this.active && this.layers) { │ │ │ │ + this.map.removeLayer(this.layer) │ │ │ │ + } │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + if (this.layers) { │ │ │ │ + this.layer.destroy() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + activate: function() { │ │ │ │ + if (!this.active) { │ │ │ │ + if (this.layers) { │ │ │ │ + this.map.addLayer(this.layer) │ │ │ │ + } │ │ │ │ + this.handlers.feature.activate(); │ │ │ │ + if (this.box && this.handlers.box) { │ │ │ │ + this.handlers.box.activate() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return OpenLayers.Control.prototype.activate.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + this.handlers.feature.deactivate(); │ │ │ │ + if (this.handlers.box) { │ │ │ │ + this.handlers.box.deactivate() │ │ │ │ + } │ │ │ │ + if (this.layers) { │ │ │ │ + this.map.removeLayer(this.layer) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return OpenLayers.Control.prototype.deactivate.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + unselectAll: function(options) { │ │ │ │ + var layers = this.layers || [this.layer], │ │ │ │ + layer, feature, l, numExcept; │ │ │ │ + for (l = 0; l < layers.length; ++l) { │ │ │ │ + layer = layers[l]; │ │ │ │ + numExcept = 0; │ │ │ │ + if (layer.selectedFeatures != null) { │ │ │ │ + while (layer.selectedFeatures.length > numExcept) { │ │ │ │ + feature = layer.selectedFeatures[numExcept]; │ │ │ │ + if (!options || options.except != feature) { │ │ │ │ + this.unselect(feature) │ │ │ │ + } else { │ │ │ │ + ++numExcept │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + clickFeature: function(feature) { │ │ │ │ + if (!this.hover) { │ │ │ │ + var selected = OpenLayers.Util.indexOf(feature.layer.selectedFeatures, feature) > -1; │ │ │ │ + if (selected) { │ │ │ │ + if (this.toggleSelect()) { │ │ │ │ + this.unselect(feature) │ │ │ │ + } else if (!this.multipleSelect()) { │ │ │ │ + this.unselectAll({ │ │ │ │ + except: feature │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + if (!this.multipleSelect()) { │ │ │ │ + this.unselectAll({ │ │ │ │ + except: feature │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + this.select(feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + multipleSelect: function() { │ │ │ │ + return this.multiple || this.handlers.feature.evt && this.handlers.feature.evt[this.multipleKey] │ │ │ │ + }, │ │ │ │ + toggleSelect: function() { │ │ │ │ + return this.toggle || this.handlers.feature.evt && this.handlers.feature.evt[this.toggleKey] │ │ │ │ + }, │ │ │ │ + clickoutFeature: function(feature) { │ │ │ │ + if (!this.hover && this.clickout) { │ │ │ │ + this.unselectAll() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + overFeature: function(feature) { │ │ │ │ + var layer = feature.layer; │ │ │ │ + if (this.hover) { │ │ │ │ + if (this.highlightOnly) { │ │ │ │ + this.highlight(feature) │ │ │ │ + } else if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { │ │ │ │ + this.select(feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + outFeature: function(feature) { │ │ │ │ + if (this.hover) { │ │ │ │ + if (this.highlightOnly) { │ │ │ │ + if (feature._lastHighlighter == this.id) { │ │ │ │ + if (feature._prevHighlighter && feature._prevHighlighter != this.id) { │ │ │ │ + delete feature._lastHighlighter; │ │ │ │ + var control = this.map.getControl(feature._prevHighlighter); │ │ │ │ + if (control) { │ │ │ │ + control.highlight(feature) │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.unhighlight(feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.unselect(feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + highlight: function(feature) { │ │ │ │ + var layer = feature.layer; │ │ │ │ + var cont = this.events.triggerEvent("beforefeaturehighlighted", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + if (cont !== false) { │ │ │ │ + feature._prevHighlighter = feature._lastHighlighter; │ │ │ │ + feature._lastHighlighter = this.id; │ │ │ │ + var style = this.selectStyle || this.renderIntent; │ │ │ │ + layer.drawFeature(feature, style); │ │ │ │ + this.events.triggerEvent("featurehighlighted", { │ │ │ │ + feature: feature │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + unhighlight: function(feature) { │ │ │ │ + var layer = feature.layer; │ │ │ │ + if (feature._prevHighlighter == undefined) { │ │ │ │ + delete feature._lastHighlighter │ │ │ │ + } else if (feature._prevHighlighter == this.id) { │ │ │ │ + delete feature._prevHighlighter │ │ │ │ + } else { │ │ │ │ + feature._lastHighlighter = feature._prevHighlighter; │ │ │ │ + delete feature._prevHighlighter │ │ │ │ + } │ │ │ │ + layer.drawFeature(feature, feature.style || feature.layer.style || "default"); │ │ │ │ + this.events.triggerEvent("featureunhighlighted", { │ │ │ │ + feature: feature │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + select: function(feature) { │ │ │ │ + var cont = this.onBeforeSelect.call(this.scope, feature); │ │ │ │ + var layer = feature.layer; │ │ │ │ + if (cont !== false) { │ │ │ │ + cont = layer.events.triggerEvent("beforefeatureselected", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + if (cont !== false) { │ │ │ │ + layer.selectedFeatures.push(feature); │ │ │ │ + this.highlight(feature); │ │ │ │ + if (!this.handlers.feature.lastFeature) { │ │ │ │ + this.handlers.feature.lastFeature = layer.selectedFeatures[0] │ │ │ │ + } │ │ │ │ + layer.events.triggerEvent("featureselected", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + this.onSelect.call(this.scope, feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + unselect: function(feature) { │ │ │ │ + var layer = feature.layer; │ │ │ │ + this.unhighlight(feature); │ │ │ │ + OpenLayers.Util.removeItem(layer.selectedFeatures, feature); │ │ │ │ + layer.events.triggerEvent("featureunselected", { │ │ │ │ + feature: feature │ │ │ │ + }); │ │ │ │ + this.onUnselect.call(this.scope, feature) │ │ │ │ + }, │ │ │ │ + selectBox: function(position) { │ │ │ │ + if (position instanceof OpenLayers.Bounds) { │ │ │ │ + var minXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.left, │ │ │ │ + y: position.bottom │ │ │ │ + }); │ │ │ │ + var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.right, │ │ │ │ + y: position.top │ │ │ │ + }); │ │ │ │ + var bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat); │ │ │ │ + if (!this.multipleSelect()) { │ │ │ │ + this.unselectAll() │ │ │ │ + } │ │ │ │ + var prevMultiple = this.multiple; │ │ │ │ + this.multiple = true; │ │ │ │ + var layers = this.layers || [this.layer]; │ │ │ │ + this.events.triggerEvent("boxselectionstart", { │ │ │ │ + layers: layers │ │ │ │ + }); │ │ │ │ + var layer; │ │ │ │ + for (var l = 0; l < layers.length; ++l) { │ │ │ │ + layer = layers[l]; │ │ │ │ + for (var i = 0, len = layer.features.length; i < len; ++i) { │ │ │ │ + var feature = layer.features[i]; │ │ │ │ + if (!feature.getVisibility()) { │ │ │ │ + continue │ │ │ │ + } │ │ │ │ + if (this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1) { │ │ │ │ + if (bounds.toGeometry().intersects(feature.geometry)) { │ │ │ │ + if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { │ │ │ │ + this.select(feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.multiple = prevMultiple; │ │ │ │ + this.events.triggerEvent("boxselectionend", { │ │ │ │ + layers: layers │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + setMap: function(map) { │ │ │ │ + this.handlers.feature.setMap(map); │ │ │ │ + if (this.box) { │ │ │ │ + this.handlers.box.setMap(map) │ │ │ │ + } │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + setLayer: function(layers) { │ │ │ │ + var isActive = this.active; │ │ │ │ + this.unselectAll(); │ │ │ │ + this.deactivate(); │ │ │ │ + if (this.layers) { │ │ │ │ + this.layer.destroy(); │ │ │ │ + this.layers = null │ │ │ │ + } │ │ │ │ + this.initLayer(layers); │ │ │ │ + this.handlers.feature.layer = this.layer; │ │ │ │ + if (isActive) { │ │ │ │ + this.activate() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Control.SelectFeature" │ │ │ │ +}); │ │ │ │ +OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + started: false, │ │ │ │ + stopDown: true, │ │ │ │ + dragging: false, │ │ │ │ + last: null, │ │ │ │ + start: null, │ │ │ │ + lastMoveEvt: null, │ │ │ │ + oldOnselectstart: null, │ │ │ │ + interval: 0, │ │ │ │ + timeoutId: null, │ │ │ │ + documentDrag: false, │ │ │ │ + documentEvents: null, │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ + if (this.documentDrag === true) { │ │ │ │ + var me = this; │ │ │ │ + this._docMove = function(evt) { │ │ │ │ + me.mousemove({ │ │ │ │ + xy: { │ │ │ │ + x: evt.clientX, │ │ │ │ + y: evt.clientY │ │ │ │ + }, │ │ │ │ + element: document │ │ │ │ + }) │ │ │ │ + }; │ │ │ │ + this._docUp = function(evt) { │ │ │ │ + me.mouseup({ │ │ │ │ + xy: { │ │ │ │ + x: evt.clientX, │ │ │ │ + y: evt.clientY │ │ │ │ + } │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + dragstart: function(evt) { │ │ │ │ + var propagate = true; │ │ │ │ + this.dragging = false; │ │ │ │ + if (this.checkModifiers(evt) && (OpenLayers.Event.isLeftClick(evt) || OpenLayers.Event.isSingleTouch(evt))) { │ │ │ │ + this.started = true; │ │ │ │ + this.start = evt.xy; │ │ │ │ + this.last = evt.xy; │ │ │ │ + OpenLayers.Element.addClass(this.map.viewPortDiv, "olDragDown"); │ │ │ │ + this.down(evt); │ │ │ │ + this.callback("down", [evt.xy]); │ │ │ │ + OpenLayers.Event.preventDefault(evt); │ │ │ │ + if (!this.oldOnselectstart) { │ │ │ │ + this.oldOnselectstart = document.onselectstart ? document.onselectstart : OpenLayers.Function.True │ │ │ │ + } │ │ │ │ + document.onselectstart = OpenLayers.Function.False; │ │ │ │ + propagate = !this.stopDown │ │ │ │ + } else { │ │ │ │ + this.started = false; │ │ │ │ + this.start = null; │ │ │ │ + this.last = null │ │ │ │ + } │ │ │ │ + return propagate │ │ │ │ + }, │ │ │ │ + dragmove: function(evt) { │ │ │ │ + this.lastMoveEvt = evt; │ │ │ │ + if (this.started && !this.timeoutId && (evt.xy.x != this.last.x || evt.xy.y != this.last.y)) { │ │ │ │ + if (this.documentDrag === true && this.documentEvents) { │ │ │ │ + if (evt.element === document) { │ │ │ │ + this.adjustXY(evt); │ │ │ │ + this.setEvent(evt) │ │ │ │ + } else { │ │ │ │ + this.removeDocumentEvents() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (this.interval > 0) { │ │ │ │ + this.timeoutId = setTimeout(OpenLayers.Function.bind(this.removeTimeout, this), this.interval) │ │ │ │ + } │ │ │ │ + this.dragging = true; │ │ │ │ + this.move(evt); │ │ │ │ + this.callback("move", [evt.xy]); │ │ │ │ + if (!this.oldOnselectstart) { │ │ │ │ + this.oldOnselectstart = document.onselectstart; │ │ │ │ + document.onselectstart = OpenLayers.Function.False │ │ │ │ + } │ │ │ │ + this.last = evt.xy │ │ │ │ + } │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + dragend: function(evt) { │ │ │ │ + if (this.started) { │ │ │ │ + if (this.documentDrag === true && this.documentEvents) { │ │ │ │ + this.adjustXY(evt); │ │ │ │ + this.removeDocumentEvents() │ │ │ │ + } │ │ │ │ + var dragged = this.start != this.last; │ │ │ │ + this.started = false; │ │ │ │ + this.dragging = false; │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDragDown"); │ │ │ │ + this.up(evt); │ │ │ │ + this.callback("up", [evt.xy]); │ │ │ │ + if (dragged) { │ │ │ │ + this.callback("done", [evt.xy]) │ │ │ │ + } │ │ │ │ + document.onselectstart = this.oldOnselectstart │ │ │ │ + } │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + down: function(evt) {}, │ │ │ │ + move: function(evt) {}, │ │ │ │ + up: function(evt) {}, │ │ │ │ + out: function(evt) {}, │ │ │ │ + mousedown: function(evt) { │ │ │ │ + return this.dragstart(evt) │ │ │ │ + }, │ │ │ │ + touchstart: function(evt) { │ │ │ │ + this.startTouch(); │ │ │ │ + return this.dragstart(evt) │ │ │ │ + }, │ │ │ │ + mousemove: function(evt) { │ │ │ │ + return this.dragmove(evt) │ │ │ │ + }, │ │ │ │ + touchmove: function(evt) { │ │ │ │ + return this.dragmove(evt) │ │ │ │ + }, │ │ │ │ + removeTimeout: function() { │ │ │ │ + this.timeoutId = null; │ │ │ │ + if (this.dragging) { │ │ │ │ + this.mousemove(this.lastMoveEvt) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + mouseup: function(evt) { │ │ │ │ + return this.dragend(evt) │ │ │ │ + }, │ │ │ │ + touchend: function(evt) { │ │ │ │ + evt.xy = this.last; │ │ │ │ + return this.dragend(evt) │ │ │ │ + }, │ │ │ │ + mouseout: function(evt) { │ │ │ │ + if (this.started && OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) { │ │ │ │ + if (this.documentDrag === true) { │ │ │ │ + this.addDocumentEvents() │ │ │ │ + } else { │ │ │ │ + var dragged = this.start != this.last; │ │ │ │ + this.started = false; │ │ │ │ + this.dragging = false; │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDragDown"); │ │ │ │ + this.out(evt); │ │ │ │ + this.callback("out", []); │ │ │ │ + if (dragged) { │ │ │ │ + this.callback("done", [evt.xy]) │ │ │ │ + } │ │ │ │ + if (document.onselectstart) { │ │ │ │ + document.onselectstart = this.oldOnselectstart │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + click: function(evt) { │ │ │ │ + return this.start == this.last │ │ │ │ + }, │ │ │ │ + activate: function() { │ │ │ │ + var activated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.dragging = false; │ │ │ │ + activated = true │ │ │ │ + } │ │ │ │ + return activated │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.started = false; │ │ │ │ + this.dragging = false; │ │ │ │ + this.start = null; │ │ │ │ + this.last = null; │ │ │ │ + deactivated = true; │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDragDown") │ │ │ │ + } │ │ │ │ + return deactivated │ │ │ │ + }, │ │ │ │ + adjustXY: function(evt) { │ │ │ │ + var pos = OpenLayers.Util.pagePosition(this.map.viewPortDiv); │ │ │ │ + evt.xy.x -= pos[0]; │ │ │ │ + evt.xy.y -= pos[1] │ │ │ │ + }, │ │ │ │ + addDocumentEvents: function() { │ │ │ │ + OpenLayers.Element.addClass(document.body, "olDragDown"); │ │ │ │ + this.documentEvents = true; │ │ │ │ + OpenLayers.Event.observe(document, "mousemove", this._docMove); │ │ │ │ + OpenLayers.Event.observe(document, "mouseup", this._docUp) │ │ │ │ + }, │ │ │ │ + removeDocumentEvents: function() { │ │ │ │ + OpenLayers.Element.removeClass(document.body, "olDragDown"); │ │ │ │ + this.documentEvents = false; │ │ │ │ + OpenLayers.Event.stopObserving(document, "mousemove", this._docMove); │ │ │ │ + OpenLayers.Event.stopObserving(document, "mouseup", this._docUp) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Drag" │ │ │ │ +}); │ │ │ │ +OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + dragHandler: null, │ │ │ │ + boxDivClassName: "olHandlerBoxZoomBox", │ │ │ │ + boxOffsets: null, │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ + this.dragHandler = new OpenLayers.Handler.Drag(this, { │ │ │ │ + down: this.startBox, │ │ │ │ + move: this.moveBox, │ │ │ │ + out: this.removeBox, │ │ │ │ + up: this.endBox │ │ │ │ + }, { │ │ │ │ + keyMask: this.keyMask │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ + if (this.dragHandler) { │ │ │ │ + this.dragHandler.destroy(); │ │ │ │ + this.dragHandler = null │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Handler.prototype.setMap.apply(this, arguments); │ │ │ │ + if (this.dragHandler) { │ │ │ │ + this.dragHandler.setMap(map) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + startBox: function(xy) { │ │ │ │ + this.callback("start", []); │ │ │ │ + this.zoomBox = OpenLayers.Util.createDiv("zoomBox", { │ │ │ │ + x: -9999, │ │ │ │ + y: -9999 │ │ │ │ + }); │ │ │ │ + this.zoomBox.className = this.boxDivClassName; │ │ │ │ + this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1; │ │ │ │ + this.map.viewPortDiv.appendChild(this.zoomBox); │ │ │ │ + OpenLayers.Element.addClass(this.map.viewPortDiv, "olDrawBox") │ │ │ │ + }, │ │ │ │ + moveBox: function(xy) { │ │ │ │ + var startX = this.dragHandler.start.x; │ │ │ │ + var startY = this.dragHandler.start.y; │ │ │ │ + var deltaX = Math.abs(startX - xy.x); │ │ │ │ + var deltaY = Math.abs(startY - xy.y); │ │ │ │ + var offset = this.getBoxOffsets(); │ │ │ │ + this.zoomBox.style.width = deltaX + offset.width + 1 + "px"; │ │ │ │ + this.zoomBox.style.height = deltaY + offset.height + 1 + "px"; │ │ │ │ + this.zoomBox.style.left = (xy.x < startX ? startX - deltaX - offset.left : startX - offset.left) + "px"; │ │ │ │ + this.zoomBox.style.top = (xy.y < startY ? startY - deltaY - offset.top : startY - offset.top) + "px" │ │ │ │ + }, │ │ │ │ + endBox: function(end) { │ │ │ │ + var result; │ │ │ │ + if (Math.abs(this.dragHandler.start.x - end.x) > 5 || Math.abs(this.dragHandler.start.y - end.y) > 5) { │ │ │ │ + var start = this.dragHandler.start; │ │ │ │ + var top = Math.min(start.y, end.y); │ │ │ │ + var bottom = Math.max(start.y, end.y); │ │ │ │ + var left = Math.min(start.x, end.x); │ │ │ │ + var right = Math.max(start.x, end.x); │ │ │ │ + result = new OpenLayers.Bounds(left, bottom, right, top) │ │ │ │ + } else { │ │ │ │ + result = this.dragHandler.start.clone() │ │ │ │ + } │ │ │ │ + this.removeBox(); │ │ │ │ + this.callback("done", [result]) │ │ │ │ + }, │ │ │ │ + removeBox: function() { │ │ │ │ + this.map.viewPortDiv.removeChild(this.zoomBox); │ │ │ │ + this.zoomBox = null; │ │ │ │ + this.boxOffsets = null; │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDrawBox") │ │ │ │ + }, │ │ │ │ + activate: function() { │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.dragHandler.activate(); │ │ │ │ + return true │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + if (this.dragHandler.deactivate()) { │ │ │ │ + if (this.zoomBox) { │ │ │ │ + this.removeBox() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return true │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + getBoxOffsets: function() { │ │ │ │ + if (!this.boxOffsets) { │ │ │ │ + var testDiv = document.createElement("div"); │ │ │ │ + testDiv.style.position = "absolute"; │ │ │ │ + testDiv.style.border = "1px solid black"; │ │ │ │ + testDiv.style.width = "3px"; │ │ │ │ + document.body.appendChild(testDiv); │ │ │ │ + var w3cBoxModel = testDiv.clientWidth == 3; │ │ │ │ + document.body.removeChild(testDiv); │ │ │ │ + var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-left-width")); │ │ │ │ + var right = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-right-width")); │ │ │ │ + var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-top-width")); │ │ │ │ + var bottom = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-bottom-width")); │ │ │ │ + this.boxOffsets = { │ │ │ │ + left: left, │ │ │ │ + right: right, │ │ │ │ + top: top, │ │ │ │ + bottom: bottom, │ │ │ │ + width: w3cBoxModel === false ? left + right : 0, │ │ │ │ + height: w3cBoxModel === false ? top + bottom : 0 │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return this.boxOffsets │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Box" │ │ │ │ +}); │ │ │ │ +OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ + out: false, │ │ │ │ + keyMask: null, │ │ │ │ + alwaysZoom: false, │ │ │ │ + zoomOnClick: true, │ │ │ │ + draw: function() { │ │ │ │ + this.handler = new OpenLayers.Handler.Box(this, { │ │ │ │ + done: this.zoomBox │ │ │ │ + }, { │ │ │ │ + keyMask: this.keyMask │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + zoomBox: function(position) { │ │ │ │ + if (position instanceof OpenLayers.Bounds) { │ │ │ │ + var bounds, targetCenterPx = position.getCenterPixel(); │ │ │ │ + if (!this.out) { │ │ │ │ + var minXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.left, │ │ │ │ + y: position.bottom │ │ │ │ + }); │ │ │ │ + var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ + x: position.right, │ │ │ │ + y: position.top │ │ │ │ + }); │ │ │ │ + bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat) │ │ │ │ + } else { │ │ │ │ + var pixWidth = position.right - position.left; │ │ │ │ + var pixHeight = position.bottom - position.top; │ │ │ │ + var zoomFactor = Math.min(this.map.size.h / pixHeight, this.map.size.w / pixWidth); │ │ │ │ + var extent = this.map.getExtent(); │ │ │ │ + var center = this.map.getLonLatFromPixel(targetCenterPx); │ │ │ │ + var xmin = center.lon - extent.getWidth() / 2 * zoomFactor; │ │ │ │ + var xmax = center.lon + extent.getWidth() / 2 * zoomFactor; │ │ │ │ + var ymin = center.lat - extent.getHeight() / 2 * zoomFactor; │ │ │ │ + var ymax = center.lat + extent.getHeight() / 2 * zoomFactor; │ │ │ │ + bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax) │ │ │ │ + } │ │ │ │ + var lastZoom = this.map.getZoom(), │ │ │ │ + size = this.map.getSize(), │ │ │ │ + centerPx = { │ │ │ │ + x: size.w / 2, │ │ │ │ + y: size.h / 2 │ │ │ │ + }, │ │ │ │ + zoom = this.map.getZoomForExtent(bounds), │ │ │ │ + oldRes = this.map.getResolution(), │ │ │ │ + newRes = this.map.getResolutionForZoom(zoom); │ │ │ │ + if (oldRes == newRes) { │ │ │ │ + this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx)) │ │ │ │ + } else { │ │ │ │ + var zoomOriginPx = { │ │ │ │ + x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / (oldRes - newRes), │ │ │ │ + y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / (oldRes - newRes) │ │ │ │ + }; │ │ │ │ + this.map.zoomTo(zoom, zoomOriginPx) │ │ │ │ + } │ │ │ │ + if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) { │ │ │ │ + this.map.zoomTo(lastZoom + (this.out ? -1 : 1)) │ │ │ │ + } │ │ │ │ + } else if (this.zoomOnClick) { │ │ │ │ + if (!this.out) { │ │ │ │ + this.map.zoomTo(this.map.getZoom() + 1, position) │ │ │ │ + } else { │ │ │ │ + this.map.zoomTo(this.map.getZoom() - 1, position) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Control.ZoomBox" │ │ │ │ +}); │ │ │ │ +OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ + panned: false, │ │ │ │ + interval: 0, │ │ │ │ + documentDrag: false, │ │ │ │ + kinetic: null, │ │ │ │ + enableKinetic: true, │ │ │ │ + kineticInterval: 10, │ │ │ │ + draw: function() { │ │ │ │ + if (this.enableKinetic && OpenLayers.Kinetic) { │ │ │ │ + var config = { │ │ │ │ + interval: this.kineticInterval │ │ │ │ + }; │ │ │ │ + if (typeof this.enableKinetic === "object") { │ │ │ │ + config = OpenLayers.Util.extend(config, this.enableKinetic) │ │ │ │ + } │ │ │ │ + this.kinetic = new OpenLayers.Kinetic(config) │ │ │ │ + } │ │ │ │ + this.handler = new OpenLayers.Handler.Drag(this, { │ │ │ │ + move: this.panMap, │ │ │ │ + done: this.panMapDone, │ │ │ │ + down: this.panMapStart │ │ │ │ + }, { │ │ │ │ + interval: this.interval, │ │ │ │ + documentDrag: this.documentDrag │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + panMapStart: function() { │ │ │ │ + if (this.kinetic) { │ │ │ │ + this.kinetic.begin() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + panMap: function(xy) { │ │ │ │ + if (this.kinetic) { │ │ │ │ + this.kinetic.update(xy) │ │ │ │ + } │ │ │ │ + this.panned = true; │ │ │ │ + this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, { │ │ │ │ + dragging: true, │ │ │ │ + animate: false │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + panMapDone: function(xy) { │ │ │ │ + if (this.panned) { │ │ │ │ + var res = null; │ │ │ │ + if (this.kinetic) { │ │ │ │ + res = this.kinetic.end(xy) │ │ │ │ + } │ │ │ │ + this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, { │ │ │ │ + dragging: !!res, │ │ │ │ + animate: false │ │ │ │ + }); │ │ │ │ + if (res) { │ │ │ │ + var self = this; │ │ │ │ + this.kinetic.move(res, function(x, y, end) { │ │ │ │ + self.map.pan(x, y, { │ │ │ │ + dragging: !end, │ │ │ │ + animate: false │ │ │ │ + }) │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + this.panned = false │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Control.DragPan" │ │ │ │ +}); │ │ │ │ +OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + wheelListener: null, │ │ │ │ + interval: 0, │ │ │ │ + maxDelta: Number.POSITIVE_INFINITY, │ │ │ │ + delta: 0, │ │ │ │ + cumulative: true, │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ + this.wheelListener = OpenLayers.Function.bindAsEventListener(this.onWheelEvent, this) │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ + this.wheelListener = null │ │ │ │ + }, │ │ │ │ + onWheelEvent: function(e) { │ │ │ │ + if (!this.map || !this.checkModifiers(e)) { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + var overScrollableDiv = false; │ │ │ │ + var allowScroll = false; │ │ │ │ + var overMapDiv = false; │ │ │ │ + var elem = OpenLayers.Event.element(e); │ │ │ │ + while (elem != null && !overMapDiv && !overScrollableDiv) { │ │ │ │ + if (!overScrollableDiv) { │ │ │ │ + try { │ │ │ │ + var overflow; │ │ │ │ + if (elem.currentStyle) { │ │ │ │ + overflow = elem.currentStyle["overflow"] │ │ │ │ + } else { │ │ │ │ + var style = document.defaultView.getComputedStyle(elem, null); │ │ │ │ + overflow = style.getPropertyValue("overflow") │ │ │ │ + } │ │ │ │ + overScrollableDiv = overflow && overflow == "auto" || overflow == "scroll" │ │ │ │ + } catch (err) {} │ │ │ │ + } │ │ │ │ + if (!allowScroll) { │ │ │ │ + allowScroll = OpenLayers.Element.hasClass(elem, "olScrollable"); │ │ │ │ + if (!allowScroll) { │ │ │ │ + for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + if (elem == layer.div || elem == layer.pane) { │ │ │ │ + allowScroll = true; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + overMapDiv = elem == this.map.div; │ │ │ │ + elem = elem.parentNode │ │ │ │ + } │ │ │ │ + if (!overScrollableDiv && overMapDiv) { │ │ │ │ + if (allowScroll) { │ │ │ │ + var delta = 0; │ │ │ │ + if (e.wheelDelta) { │ │ │ │ + delta = e.wheelDelta; │ │ │ │ + if (delta % 160 === 0) { │ │ │ │ + delta = delta * .75 │ │ │ │ + } │ │ │ │ + delta = delta / 120 │ │ │ │ + } else if (e.detail) { │ │ │ │ + delta = -(e.detail / Math.abs(e.detail)) │ │ │ │ + } │ │ │ │ + this.delta += delta; │ │ │ │ + window.clearTimeout(this._timeoutId); │ │ │ │ + if (this.interval && Math.abs(this.delta) < this.maxDelta) { │ │ │ │ + var evt = OpenLayers.Util.extend({}, e); │ │ │ │ + this._timeoutId = window.setTimeout(OpenLayers.Function.bind(function() { │ │ │ │ + this.wheelZoom(evt) │ │ │ │ + }, this), this.interval) │ │ │ │ + } else { │ │ │ │ + this.wheelZoom(e) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + OpenLayers.Event.stop(e) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + wheelZoom: function(e) { │ │ │ │ + var delta = this.delta; │ │ │ │ + this.delta = 0; │ │ │ │ + if (delta) { │ │ │ │ + e.xy = this.map.events.getMousePosition(e); │ │ │ │ + if (delta < 0) { │ │ │ │ + this.callback("down", [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]) │ │ │ │ + } else { │ │ │ │ + this.callback("up", [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + activate: function(evt) { │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + var wheelListener = this.wheelListener; │ │ │ │ + OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener); │ │ │ │ + OpenLayers.Event.observe(window, "mousewheel", wheelListener); │ │ │ │ + OpenLayers.Event.observe(document, "mousewheel", wheelListener); │ │ │ │ + return true │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + deactivate: function(evt) { │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + var wheelListener = this.wheelListener; │ │ │ │ + OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener); │ │ │ │ + OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener); │ │ │ │ + OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener); │ │ │ │ + return true │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.MouseWheel" │ │ │ │ +}); │ │ │ │ +OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + delay: 300, │ │ │ │ + single: true, │ │ │ │ + double: false, │ │ │ │ + pixelTolerance: 0, │ │ │ │ + dblclickTolerance: 13, │ │ │ │ + stopSingle: false, │ │ │ │ + stopDouble: false, │ │ │ │ + timerId: null, │ │ │ │ + down: null, │ │ │ │ + last: null, │ │ │ │ + first: null, │ │ │ │ + rightclickTimerId: null, │ │ │ │ + touchstart: function(evt) { │ │ │ │ + this.startTouch(); │ │ │ │ + this.down = this.getEventInfo(evt); │ │ │ │ + this.last = this.getEventInfo(evt); │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + touchmove: function(evt) { │ │ │ │ + this.last = this.getEventInfo(evt); │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + touchend: function(evt) { │ │ │ │ + if (this.down) { │ │ │ │ + evt.xy = this.last.xy; │ │ │ │ + evt.lastTouches = this.last.touches; │ │ │ │ + this.handleSingle(evt); │ │ │ │ + this.down = null │ │ │ │ + } │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + mousedown: function(evt) { │ │ │ │ + this.down = this.getEventInfo(evt); │ │ │ │ + this.last = this.getEventInfo(evt); │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + mouseup: function(evt) { │ │ │ │ + var propagate = true; │ │ │ │ + if (this.checkModifiers(evt) && this.control.handleRightClicks && OpenLayers.Event.isRightClick(evt)) { │ │ │ │ + propagate = this.rightclick(evt) │ │ │ │ + } │ │ │ │ + return propagate │ │ │ │ + }, │ │ │ │ + rightclick: function(evt) { │ │ │ │ + if (this.passesTolerance(evt)) { │ │ │ │ + if (this.rightclickTimerId != null) { │ │ │ │ + this.clearTimer(); │ │ │ │ + this.callback("dblrightclick", [evt]); │ │ │ │ + return !this.stopDouble │ │ │ │ + } else { │ │ │ │ + var clickEvent = this["double"] ? OpenLayers.Util.extend({}, evt) : this.callback("rightclick", [evt]); │ │ │ │ + var delayedRightCall = OpenLayers.Function.bind(this.delayedRightCall, this, clickEvent); │ │ │ │ + this.rightclickTimerId = window.setTimeout(delayedRightCall, this.delay) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return !this.stopSingle │ │ │ │ + }, │ │ │ │ + delayedRightCall: function(evt) { │ │ │ │ + this.rightclickTimerId = null; │ │ │ │ + if (evt) { │ │ │ │ + this.callback("rightclick", [evt]) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + click: function(evt) { │ │ │ │ + if (!this.last) { │ │ │ │ + this.last = this.getEventInfo(evt) │ │ │ │ + } │ │ │ │ + this.handleSingle(evt); │ │ │ │ + return !this.stopSingle │ │ │ │ + }, │ │ │ │ + dblclick: function(evt) { │ │ │ │ + this.handleDouble(evt); │ │ │ │ + return !this.stopDouble │ │ │ │ + }, │ │ │ │ + handleDouble: function(evt) { │ │ │ │ + if (this.passesDblclickTolerance(evt)) { │ │ │ │ + if (this["double"]) { │ │ │ │ + this.callback("dblclick", [evt]) │ │ │ │ + } │ │ │ │ + this.clearTimer() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + handleSingle: function(evt) { │ │ │ │ + if (this.passesTolerance(evt)) { │ │ │ │ + if (this.timerId != null) { │ │ │ │ + if (this.last.touches && this.last.touches.length === 1) { │ │ │ │ + if (this["double"]) { │ │ │ │ + OpenLayers.Event.preventDefault(evt) │ │ │ │ + } │ │ │ │ + this.handleDouble(evt) │ │ │ │ + } │ │ │ │ + if (!this.last.touches || this.last.touches.length !== 2) { │ │ │ │ + this.clearTimer() │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.first = this.getEventInfo(evt); │ │ │ │ + var clickEvent = this.single ? OpenLayers.Util.extend({}, evt) : null; │ │ │ │ + this.queuePotentialClick(clickEvent) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + queuePotentialClick: function(evt) { │ │ │ │ + this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay) │ │ │ │ + }, │ │ │ │ + passesTolerance: function(evt) { │ │ │ │ + var passes = true; │ │ │ │ + if (this.pixelTolerance != null && this.down && this.down.xy) { │ │ │ │ + passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy); │ │ │ │ + if (passes && this.touch && this.down.touches.length === this.last.touches.length) { │ │ │ │ + for (var i = 0, ii = this.down.touches.length; i < ii; ++i) { │ │ │ │ + if (this.getTouchDistance(this.down.touches[i], this.last.touches[i]) > this.pixelTolerance) { │ │ │ │ + passes = false; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return passes │ │ │ │ + }, │ │ │ │ + getTouchDistance: function(from, to) { │ │ │ │ + return Math.sqrt(Math.pow(from.clientX - to.clientX, 2) + Math.pow(from.clientY - to.clientY, 2)) │ │ │ │ + }, │ │ │ │ + passesDblclickTolerance: function(evt) { │ │ │ │ + var passes = true; │ │ │ │ + if (this.down && this.first) { │ │ │ │ + passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance │ │ │ │ + } │ │ │ │ + return passes │ │ │ │ + }, │ │ │ │ + clearTimer: function() { │ │ │ │ + if (this.timerId != null) { │ │ │ │ + window.clearTimeout(this.timerId); │ │ │ │ + this.timerId = null │ │ │ │ + } │ │ │ │ + if (this.rightclickTimerId != null) { │ │ │ │ + window.clearTimeout(this.rightclickTimerId); │ │ │ │ + this.rightclickTimerId = null │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + delayedCall: function(evt) { │ │ │ │ + this.timerId = null; │ │ │ │ + if (evt) { │ │ │ │ + this.callback("click", [evt]) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + getEventInfo: function(evt) { │ │ │ │ + var touches; │ │ │ │ + if (evt.touches) { │ │ │ │ + var len = evt.touches.length; │ │ │ │ + touches = new Array(len); │ │ │ │ + var touch; │ │ │ │ + for (var i = 0; i < len; i++) { │ │ │ │ + touch = evt.touches[i]; │ │ │ │ + touches[i] = { │ │ │ │ + clientX: touch.olClientX, │ │ │ │ + clientY: touch.olClientY │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return { │ │ │ │ + xy: evt.xy, │ │ │ │ + touches: touches │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.clearTimer(); │ │ │ │ + this.down = null; │ │ │ │ + this.first = null; │ │ │ │ + this.last = null; │ │ │ │ + deactivated = true │ │ │ │ + } │ │ │ │ + return deactivated │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Click" │ │ │ │ +}); │ │ │ │ +OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + dragPan: null, │ │ │ │ + dragPanOptions: null, │ │ │ │ + pinchZoom: null, │ │ │ │ + pinchZoomOptions: null, │ │ │ │ + documentDrag: false, │ │ │ │ + zoomBox: null, │ │ │ │ + zoomBoxEnabled: true, │ │ │ │ + zoomWheelEnabled: true, │ │ │ │ + mouseWheelOptions: null, │ │ │ │ + handleRightClicks: false, │ │ │ │ + zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT, │ │ │ │ + autoActivate: true, │ │ │ │ + initialize: function(options) { │ │ │ │ + this.handlers = {}; │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ + if (this.dragPan) { │ │ │ │ + this.dragPan.destroy() │ │ │ │ + } │ │ │ │ + this.dragPan = null; │ │ │ │ + if (this.zoomBox) { │ │ │ │ + this.zoomBox.destroy() │ │ │ │ + } │ │ │ │ + this.zoomBox = null; │ │ │ │ + if (this.pinchZoom) { │ │ │ │ + this.pinchZoom.destroy() │ │ │ │ + } │ │ │ │ + this.pinchZoom = null; │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + activate: function() { │ │ │ │ + this.dragPan.activate(); │ │ │ │ + if (this.zoomWheelEnabled) { │ │ │ │ + this.handlers.wheel.activate() │ │ │ │ + } │ │ │ │ + this.handlers.click.activate(); │ │ │ │ + if (this.zoomBoxEnabled) { │ │ │ │ + this.zoomBox.activate() │ │ │ │ + } │ │ │ │ + if (this.pinchZoom) { │ │ │ │ + this.pinchZoom.activate() │ │ │ │ + } │ │ │ │ + return OpenLayers.Control.prototype.activate.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.pinchZoom) { │ │ │ │ + this.pinchZoom.deactivate() │ │ │ │ + } │ │ │ │ + this.zoomBox.deactivate(); │ │ │ │ + this.dragPan.deactivate(); │ │ │ │ + this.handlers.click.deactivate(); │ │ │ │ + this.handlers.wheel.deactivate(); │ │ │ │ + return OpenLayers.Control.prototype.deactivate.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + draw: function() { │ │ │ │ + if (this.handleRightClicks) { │ │ │ │ + this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False │ │ │ │ + } │ │ │ │ + var clickCallbacks = { │ │ │ │ + click: this.defaultClick, │ │ │ │ + dblclick: this.defaultDblClick, │ │ │ │ + dblrightclick: this.defaultDblRightClick │ │ │ │ + }; │ │ │ │ + var clickOptions = { │ │ │ │ + double: true, │ │ │ │ + stopDouble: true │ │ │ │ + }; │ │ │ │ + this.handlers.click = new OpenLayers.Handler.Click(this, clickCallbacks, clickOptions); │ │ │ │ + this.dragPan = new OpenLayers.Control.DragPan(OpenLayers.Util.extend({ │ │ │ │ + map: this.map, │ │ │ │ + documentDrag: this.documentDrag │ │ │ │ + }, this.dragPanOptions)); │ │ │ │ + this.zoomBox = new OpenLayers.Control.ZoomBox({ │ │ │ │ + map: this.map, │ │ │ │ + keyMask: this.zoomBoxKeyMask │ │ │ │ + }); │ │ │ │ + this.dragPan.draw(); │ │ │ │ + this.zoomBox.draw(); │ │ │ │ + var wheelOptions = this.map.fractionalZoom ? {} : { │ │ │ │ + cumulative: false, │ │ │ │ + interval: 50, │ │ │ │ + maxDelta: 6 │ │ │ │ + }; │ │ │ │ + this.handlers.wheel = new OpenLayers.Handler.MouseWheel(this, { │ │ │ │ + up: this.wheelUp, │ │ │ │ + down: this.wheelDown │ │ │ │ + }, OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)); │ │ │ │ + if (OpenLayers.Control.PinchZoom) { │ │ │ │ + this.pinchZoom = new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({ │ │ │ │ + map: this.map │ │ │ │ + }, this.pinchZoomOptions)) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + defaultClick: function(evt) { │ │ │ │ + if (evt.lastTouches && evt.lastTouches.length == 2) { │ │ │ │ + this.map.zoomOut() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + defaultDblClick: function(evt) { │ │ │ │ + this.map.zoomTo(this.map.zoom + 1, evt.xy) │ │ │ │ + }, │ │ │ │ + defaultDblRightClick: function(evt) { │ │ │ │ + this.map.zoomTo(this.map.zoom - 1, evt.xy) │ │ │ │ + }, │ │ │ │ + wheelChange: function(evt, deltaZ) { │ │ │ │ + if (!this.map.fractionalZoom) { │ │ │ │ + deltaZ = Math.round(deltaZ) │ │ │ │ + } │ │ │ │ + var currentZoom = this.map.getZoom(), │ │ │ │ + newZoom = currentZoom + deltaZ; │ │ │ │ + newZoom = Math.max(newZoom, 0); │ │ │ │ + newZoom = Math.min(newZoom, this.map.getNumZoomLevels()); │ │ │ │ + if (newZoom === currentZoom) { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + this.map.zoomTo(newZoom, evt.xy) │ │ │ │ + }, │ │ │ │ + wheelUp: function(evt, delta) { │ │ │ │ + this.wheelChange(evt, delta || 1) │ │ │ │ + }, │ │ │ │ + wheelDown: function(evt, delta) { │ │ │ │ + this.wheelChange(evt, delta || -1) │ │ │ │ + }, │ │ │ │ + disableZoomBox: function() { │ │ │ │ + this.zoomBoxEnabled = false; │ │ │ │ + this.zoomBox.deactivate() │ │ │ │ + }, │ │ │ │ + enableZoomBox: function() { │ │ │ │ + this.zoomBoxEnabled = true; │ │ │ │ + if (this.active) { │ │ │ │ + this.zoomBox.activate() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + disableZoomWheel: function() { │ │ │ │ + this.zoomWheelEnabled = false; │ │ │ │ + this.handlers.wheel.deactivate() │ │ │ │ + }, │ │ │ │ + enableZoomWheel: function() { │ │ │ │ + this.zoomWheelEnabled = true; │ │ │ │ + if (this.active) { │ │ │ │ + this.handlers.wheel.activate() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Navigation" │ │ │ │ +}); │ │ │ │ +OpenLayers.Events.buttonclick = OpenLayers.Class({ │ │ │ │ + target: null, │ │ │ │ + events: ["mousedown", "mouseup", "click", "dblclick", "touchstart", "touchmove", "touchend", "keydown"], │ │ │ │ + startRegEx: /^mousedown|touchstart$/, │ │ │ │ + cancelRegEx: /^touchmove$/, │ │ │ │ + completeRegEx: /^mouseup|touchend$/, │ │ │ │ + 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 │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + for (var i = this.events.length - 1; i >= 0; --i) { │ │ │ │ + this.target.unregister(this.events[i], this, this.buttonClick) │ │ │ │ + } │ │ │ │ + delete this.target │ │ │ │ + }, │ │ │ │ + getPressedButton: function(element) { │ │ │ │ + var depth = 3, │ │ │ │ + button; │ │ │ │ + do { │ │ │ │ + if (OpenLayers.Element.hasClass(element, "olButton")) { │ │ │ │ + button = element; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + element = element.parentNode │ │ │ │ + } while (--depth > 0 && element); │ │ │ │ + return button │ │ │ │ + }, │ │ │ │ + ignore: function(element) { │ │ │ │ + var depth = 3, │ │ │ │ + ignore = false; │ │ │ │ + do { │ │ │ │ + if (element.nodeName.toLowerCase() === "a") { │ │ │ │ + ignore = true; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + element = element.parentNode │ │ │ │ + } while (--depth > 0 && element); │ │ │ │ + return ignore │ │ │ │ + }, │ │ │ │ + buttonClick: function(evt) { │ │ │ │ + var propagate = true, │ │ │ │ + element = OpenLayers.Event.element(evt); │ │ │ │ + if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) { │ │ │ │ + var button = this.getPressedButton(element); │ │ │ │ + if (button) { │ │ │ │ + if (evt.type === "keydown") { │ │ │ │ + switch (evt.keyCode) { │ │ │ │ + case OpenLayers.Event.KEY_RETURN: │ │ │ │ + case OpenLayers.Event.KEY_SPACE: │ │ │ │ + this.target.triggerEvent("buttonclick", { │ │ │ │ + buttonElement: button │ │ │ │ + }); │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + propagate = false; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } else if (this.startEvt) { │ │ │ │ + if (this.completeRegEx.test(evt.type)) { │ │ │ │ + var pos = OpenLayers.Util.pagePosition(button); │ │ │ │ + var viewportElement = OpenLayers.Util.getViewportElement(); │ │ │ │ + var scrollTop = window.pageYOffset || viewportElement.scrollTop; │ │ │ │ + var scrollLeft = window.pageXOffset || viewportElement.scrollLeft; │ │ │ │ + pos[0] = pos[0] - scrollLeft; │ │ │ │ + pos[1] = pos[1] - scrollTop; │ │ │ │ + this.target.triggerEvent("buttonclick", { │ │ │ │ + buttonElement: button, │ │ │ │ + buttonXY: { │ │ │ │ + x: this.startEvt.clientX - pos[0], │ │ │ │ + y: this.startEvt.clientY - pos[1] │ │ │ │ + } │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + if (this.cancelRegEx.test(evt.type)) { │ │ │ │ + delete this.startEvt │ │ │ │ + } │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + propagate = false │ │ │ │ + } │ │ │ │ + if (this.startRegEx.test(evt.type)) { │ │ │ │ + this.startEvt = evt; │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + propagate = false │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + propagate = !this.ignore(OpenLayers.Event.element(evt)); │ │ │ │ + delete this.startEvt │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return propagate │ │ │ │ + } │ │ │ │ +}); │ │ │ │ +OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + layerStates: null, │ │ │ │ + layersDiv: null, │ │ │ │ + baseLayersDiv: null, │ │ │ │ + baseLayers: null, │ │ │ │ + dataLbl: null, │ │ │ │ + dataLayersDiv: null, │ │ │ │ + dataLayers: null, │ │ │ │ + minimizeDiv: null, │ │ │ │ + maximizeDiv: null, │ │ │ │ + ascending: true, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ + this.layerStates = [] │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.clearLayersArray("base"); │ │ │ │ + this.clearLayersArray("data"); │ │ │ │ + this.map.events.un({ │ │ │ │ + buttonclick: this.onButtonClick, │ │ │ │ + addlayer: this.redraw, │ │ │ │ + changelayer: this.redraw, │ │ │ │ + removelayer: this.redraw, │ │ │ │ + changebaselayer: this.redraw, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ + this.map.events.on({ │ │ │ │ + addlayer: this.redraw, │ │ │ │ + changelayer: this.redraw, │ │ │ │ + removelayer: this.redraw, │ │ │ │ + changebaselayer: this.redraw, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + if (this.outsideViewport) { │ │ │ │ + this.events.attachToElement(this.div); │ │ │ │ + this.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ + } else { │ │ │ │ + this.map.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this); │ │ │ │ + this.loadContents(); │ │ │ │ + if (!this.outsideViewport) { │ │ │ │ + this.minimizeControl() │ │ │ │ + } │ │ │ │ + this.redraw(); │ │ │ │ + return this.div │ │ │ │ + }, │ │ │ │ + onButtonClick: function(evt) { │ │ │ │ + var button = evt.buttonElement; │ │ │ │ + if (button === this.minimizeDiv) { │ │ │ │ + this.minimizeControl() │ │ │ │ + } else if (button === this.maximizeDiv) { │ │ │ │ + this.maximizeControl() │ │ │ │ + } else if (button._layerSwitcher === this.id) { │ │ │ │ + if (button["for"]) { │ │ │ │ + button = document.getElementById(button["for"]) │ │ │ │ + } │ │ │ │ + if (!button.disabled) { │ │ │ │ + if (button.type == "radio") { │ │ │ │ + button.checked = true; │ │ │ │ + this.map.setBaseLayer(this.map.getLayer(button._layer)) │ │ │ │ + } else { │ │ │ │ + button.checked = !button.checked; │ │ │ │ + this.updateMap() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + clearLayersArray: function(layersType) { │ │ │ │ + this[layersType + "LayersDiv"].innerHTML = ""; │ │ │ │ + this[layersType + "Layers"] = [] │ │ │ │ + }, │ │ │ │ + checkRedraw: function() { │ │ │ │ + if (!this.layerStates.length || this.map.layers.length != this.layerStates.length) { │ │ │ │ + return true │ │ │ │ + } │ │ │ │ + for (var i = 0, len = this.layerStates.length; i < len; i++) { │ │ │ │ + var layerState = this.layerStates[i]; │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + if (layerState.name != layer.name || layerState.inRange != layer.inRange || layerState.id != layer.id || layerState.visibility != layer.visibility) { │ │ │ │ + return true │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return false │ │ │ │ + }, │ │ │ │ + redraw: function() { │ │ │ │ + if (!this.checkRedraw()) { │ │ │ │ + return this.div │ │ │ │ + } │ │ │ │ + this.clearLayersArray("base"); │ │ │ │ + this.clearLayersArray("data"); │ │ │ │ + var containsOverlays = false; │ │ │ │ + var containsBaseLayers = false; │ │ │ │ + var len = this.map.layers.length; │ │ │ │ + this.layerStates = new Array(len); │ │ │ │ + for (var i = 0; i < len; i++) { │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + this.layerStates[i] = { │ │ │ │ + name: layer.name, │ │ │ │ + visibility: layer.visibility, │ │ │ │ + inRange: layer.inRange, │ │ │ │ + id: layer.id │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var layers = this.map.layers.slice(); │ │ │ │ + if (!this.ascending) { │ │ │ │ + layers.reverse() │ │ │ │ + } │ │ │ │ + for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ + var layer = layers[i]; │ │ │ │ + var baseLayer = layer.isBaseLayer; │ │ │ │ + if (layer.displayInLayerSwitcher) { │ │ │ │ + if (baseLayer) { │ │ │ │ + containsBaseLayers = true │ │ │ │ + } else { │ │ │ │ + containsOverlays = true │ │ │ │ + } │ │ │ │ + var checked = baseLayer ? layer == this.map.baseLayer : layer.getVisibility(); │ │ │ │ + var inputElem = document.createElement("input"), │ │ │ │ + inputId = OpenLayers.Util.createUniqueID(this.id + "_input_"); │ │ │ │ + inputElem.id = inputId; │ │ │ │ + inputElem.name = baseLayer ? this.id + "_baseLayers" : layer.name; │ │ │ │ + inputElem.type = baseLayer ? "radio" : "checkbox"; │ │ │ │ + inputElem.value = layer.name; │ │ │ │ + inputElem.checked = checked; │ │ │ │ + inputElem.defaultChecked = checked; │ │ │ │ + inputElem.className = "olButton"; │ │ │ │ + inputElem._layer = layer.id; │ │ │ │ + inputElem._layerSwitcher = this.id; │ │ │ │ + if (!baseLayer && !layer.inRange) { │ │ │ │ + inputElem.disabled = true │ │ │ │ + } │ │ │ │ + var labelSpan = document.createElement("label"); │ │ │ │ + labelSpan["for"] = inputElem.id; │ │ │ │ + OpenLayers.Element.addClass(labelSpan, "labelSpan olButton"); │ │ │ │ + labelSpan._layer = layer.id; │ │ │ │ + labelSpan._layerSwitcher = this.id; │ │ │ │ + if (!baseLayer && !layer.inRange) { │ │ │ │ + labelSpan.style.color = "gray" │ │ │ │ + } │ │ │ │ + labelSpan.innerHTML = layer.name; │ │ │ │ + labelSpan.style.verticalAlign = baseLayer ? "bottom" : "baseline"; │ │ │ │ + var br = document.createElement("br"); │ │ │ │ + var groupArray = baseLayer ? this.baseLayers : this.dataLayers; │ │ │ │ + groupArray.push({ │ │ │ │ + layer: layer, │ │ │ │ + inputElem: inputElem, │ │ │ │ + labelSpan: labelSpan │ │ │ │ + }); │ │ │ │ + var groupDiv = baseLayer ? this.baseLayersDiv : this.dataLayersDiv; │ │ │ │ + groupDiv.appendChild(inputElem); │ │ │ │ + groupDiv.appendChild(labelSpan); │ │ │ │ + groupDiv.appendChild(br) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.dataLbl.style.display = containsOverlays ? "" : "none"; │ │ │ │ + this.baseLbl.style.display = containsBaseLayers ? "" : "none"; │ │ │ │ + return this.div │ │ │ │ + }, │ │ │ │ + updateMap: function() { │ │ │ │ + for (var i = 0, len = this.baseLayers.length; i < len; i++) { │ │ │ │ + var layerEntry = this.baseLayers[i]; │ │ │ │ + if (layerEntry.inputElem.checked) { │ │ │ │ + this.map.setBaseLayer(layerEntry.layer, false) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + for (var i = 0, len = this.dataLayers.length; i < len; i++) { │ │ │ │ + var layerEntry = this.dataLayers[i]; │ │ │ │ + layerEntry.layer.setVisibility(layerEntry.inputElem.checked) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + maximizeControl: function(e) { │ │ │ │ + this.div.style.width = ""; │ │ │ │ + this.div.style.height = ""; │ │ │ │ + this.showControls(false); │ │ │ │ + if (e != null) { │ │ │ │ + OpenLayers.Event.stop(e) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + minimizeControl: function(e) { │ │ │ │ + this.div.style.width = "0px"; │ │ │ │ + this.div.style.height = "0px"; │ │ │ │ + this.showControls(true); │ │ │ │ + if (e != null) { │ │ │ │ + OpenLayers.Event.stop(e) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + showControls: function(minimize) { │ │ │ │ + this.maximizeDiv.style.display = minimize ? "" : "none"; │ │ │ │ + this.minimizeDiv.style.display = minimize ? "none" : ""; │ │ │ │ + this.layersDiv.style.display = minimize ? "none" : "" │ │ │ │ + }, │ │ │ │ + loadContents: function() { │ │ │ │ + this.layersDiv = document.createElement("div"); │ │ │ │ + this.layersDiv.id = this.id + "_layersDiv"; │ │ │ │ + OpenLayers.Element.addClass(this.layersDiv, "layersDiv"); │ │ │ │ + this.baseLbl = document.createElement("div"); │ │ │ │ + this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer"); │ │ │ │ + OpenLayers.Element.addClass(this.baseLbl, "baseLbl"); │ │ │ │ + this.baseLayersDiv = document.createElement("div"); │ │ │ │ + OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv"); │ │ │ │ + this.dataLbl = document.createElement("div"); │ │ │ │ + this.dataLbl.innerHTML = OpenLayers.i18n("Overlays"); │ │ │ │ + OpenLayers.Element.addClass(this.dataLbl, "dataLbl"); │ │ │ │ + this.dataLayersDiv = document.createElement("div"); │ │ │ │ + OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv"); │ │ │ │ + if (this.ascending) { │ │ │ │ + this.layersDiv.appendChild(this.baseLbl); │ │ │ │ + this.layersDiv.appendChild(this.baseLayersDiv); │ │ │ │ + this.layersDiv.appendChild(this.dataLbl); │ │ │ │ + this.layersDiv.appendChild(this.dataLayersDiv) │ │ │ │ + } else { │ │ │ │ + this.layersDiv.appendChild(this.dataLbl); │ │ │ │ + this.layersDiv.appendChild(this.dataLayersDiv); │ │ │ │ + this.layersDiv.appendChild(this.baseLbl); │ │ │ │ + this.layersDiv.appendChild(this.baseLayersDiv) │ │ │ │ + } │ │ │ │ + this.div.appendChild(this.layersDiv); │ │ │ │ + var img = OpenLayers.Util.getImageLocation("layer-switcher-maximize.png"); │ │ │ │ + this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MaximizeDiv", null, null, img, "absolute"); │ │ │ │ + OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton"); │ │ │ │ + this.maximizeDiv.style.display = "none"; │ │ │ │ + this.div.appendChild(this.maximizeDiv); │ │ │ │ + var img = OpenLayers.Util.getImageLocation("layer-switcher-minimize.png"); │ │ │ │ + this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MinimizeDiv", null, null, img, "absolute"); │ │ │ │ + OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton"); │ │ │ │ + this.minimizeDiv.style.display = "none"; │ │ │ │ + this.div.appendChild(this.minimizeDiv) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Control.LayerSwitcher" │ │ │ │ +}); │ │ │ │ +OpenLayers.Control.Attribution = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + separator: ", ", │ │ │ │ + template: "${layers}", │ │ │ │ + destroy: function() { │ │ │ │ + this.map.events.un({ │ │ │ │ + removelayer: this.updateAttribution, │ │ │ │ + addlayer: this.updateAttribution, │ │ │ │ + changelayer: this.updateAttribution, │ │ │ │ + changebaselayer: this.updateAttribution, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ + this.map.events.on({ │ │ │ │ + changebaselayer: this.updateAttribution, │ │ │ │ + changelayer: this.updateAttribution, │ │ │ │ + addlayer: this.updateAttribution, │ │ │ │ + removelayer: this.updateAttribution, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.updateAttribution(); │ │ │ │ + return this.div │ │ │ │ + }, │ │ │ │ + updateAttribution: function() { │ │ │ │ + var attributions = []; │ │ │ │ + if (this.map && this.map.layers) { │ │ │ │ + for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + if (layer.attribution && layer.getVisibility()) { │ │ │ │ + if (OpenLayers.Util.indexOf(attributions, layer.attribution) === -1) { │ │ │ │ + attributions.push(layer.attribution) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.div.innerHTML = OpenLayers.String.format(this.template, { │ │ │ │ + layers: attributions.join(this.separator) │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Attribution" │ │ │ │ +}); │ │ │ │ +OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + controls: null, │ │ │ │ + autoActivate: true, │ │ │ │ + defaultControl: null, │ │ │ │ + saveState: false, │ │ │ │ + allowDepress: false, │ │ │ │ + activeState: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ + this.controls = []; │ │ │ │ + this.activeState = {} │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + if (this.map) { │ │ │ │ + this.map.events.unregister("buttonclick", this, this.onButtonClick) │ │ │ │ + } │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ + for (var ctl, i = this.controls.length - 1; i >= 0; i--) { │ │ │ │ + ctl = this.controls[i]; │ │ │ │ + if (ctl.events) { │ │ │ │ + ctl.events.un({ │ │ │ │ + activate: this.iconOn, │ │ │ │ + deactivate: this.iconOff │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + ctl.panel_div = null │ │ │ │ + } │ │ │ │ + this.activeState = null │ │ │ │ + }, │ │ │ │ + activate: function() { │ │ │ │ + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ + var control; │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ + control = this.controls[i]; │ │ │ │ + if (control === this.defaultControl || this.saveState && this.activeState[control.id]) { │ │ │ │ + control.activate() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (this.saveState === true) { │ │ │ │ + this.defaultControl = null │ │ │ │ + } │ │ │ │ + this.redraw(); │ │ │ │ + return true │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + var control; │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ + control = this.controls[i]; │ │ │ │ + this.activeState[control.id] = control.deactivate() │ │ │ │ + } │ │ │ │ + this.redraw(); │ │ │ │ + return true │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + draw: function() { │ │ │ │ + OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ + if (this.outsideViewport) { │ │ │ │ + this.events.attachToElement(this.div); │ │ │ │ + this.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ + } else { │ │ │ │ + this.map.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ + } │ │ │ │ + this.addControlsToMap(this.controls); │ │ │ │ + return this.div │ │ │ │ + }, │ │ │ │ + redraw: function() { │ │ │ │ + for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) { │ │ │ │ + this.div.removeChild(this.div.childNodes[i]) │ │ │ │ + } │ │ │ │ + this.div.innerHTML = ""; │ │ │ │ + if (this.active) { │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ + this.div.appendChild(this.controls[i].panel_div) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + activateControl: function(control) { │ │ │ │ + if (!this.active) { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + if (control.type == OpenLayers.Control.TYPE_BUTTON) { │ │ │ │ + control.trigger(); │ │ │ │ + return │ │ │ │ + } │ │ │ │ + if (control.type == OpenLayers.Control.TYPE_TOGGLE) { │ │ │ │ + if (control.active) { │ │ │ │ + control.deactivate() │ │ │ │ + } else { │ │ │ │ + control.activate() │ │ │ │ + } │ │ │ │ + return │ │ │ │ + } │ │ │ │ + if (this.allowDepress && control.active) { │ │ │ │ + control.deactivate() │ │ │ │ + } else { │ │ │ │ + var c; │ │ │ │ + for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ + c = this.controls[i]; │ │ │ │ + if (c != control && (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) { │ │ │ │ + c.deactivate() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + control.activate() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + addControls: function(controls) { │ │ │ │ + if (!OpenLayers.Util.isArray(controls)) { │ │ │ │ + controls = [controls] │ │ │ │ + } │ │ │ │ + this.controls = this.controls.concat(controls); │ │ │ │ + for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ + var control = controls[i], │ │ │ │ + element = this.createControlMarkup(control); │ │ │ │ + OpenLayers.Element.addClass(element, control.displayClass + "ItemInactive"); │ │ │ │ + OpenLayers.Element.addClass(element, "olButton"); │ │ │ │ + if (control.title != "" && !element.title) { │ │ │ │ + element.title = control.title │ │ │ │ + } │ │ │ │ + control.panel_div = element │ │ │ │ + } │ │ │ │ + if (this.map) { │ │ │ │ + this.addControlsToMap(controls); │ │ │ │ + this.redraw() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + createControlMarkup: function(control) { │ │ │ │ + return document.createElement("div") │ │ │ │ + }, │ │ │ │ + addControlsToMap: function(controls) { │ │ │ │ + var control; │ │ │ │ + for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ + control = controls[i]; │ │ │ │ + if (control.autoActivate === true) { │ │ │ │ + control.autoActivate = false; │ │ │ │ + this.map.addControl(control); │ │ │ │ + control.autoActivate = true │ │ │ │ + } else { │ │ │ │ + this.map.addControl(control); │ │ │ │ + control.deactivate() │ │ │ │ + } │ │ │ │ + control.events.on({ │ │ │ │ + activate: this.iconOn, │ │ │ │ + deactivate: this.iconOff │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + iconOn: function() { │ │ │ │ + var d = this.panel_div; │ │ │ │ + var re = new RegExp("\\b(" + this.displayClass + "Item)Inactive\\b"); │ │ │ │ + d.className = d.className.replace(re, "$1Active") │ │ │ │ + }, │ │ │ │ + iconOff: function() { │ │ │ │ + var d = this.panel_div; │ │ │ │ + var re = new RegExp("\\b(" + this.displayClass + "Item)Active\\b"); │ │ │ │ + d.className = d.className.replace(re, "$1Inactive") │ │ │ │ + }, │ │ │ │ + onButtonClick: function(evt) { │ │ │ │ + var controls = this.controls, │ │ │ │ + button = evt.buttonElement; │ │ │ │ + for (var i = controls.length - 1; i >= 0; --i) { │ │ │ │ + if (controls[i].panel_div === button) { │ │ │ │ + this.activateControl(controls[i]); │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + getControlsBy: function(property, match) { │ │ │ │ + var test = typeof match.test == "function"; │ │ │ │ + var found = OpenLayers.Array.filter(this.controls, function(item) { │ │ │ │ + return item[property] == match || test && match.test(item[property]) │ │ │ │ + }); │ │ │ │ + return found │ │ │ │ + }, │ │ │ │ + getControlsByName: function(match) { │ │ │ │ + return this.getControlsBy("name", match) │ │ │ │ + }, │ │ │ │ + getControlsByClass: function(match) { │ │ │ │ + return this.getControlsBy("CLASS_NAME", match) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Panel" │ │ │ │ +}); │ │ │ │ +OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ + zoomInText: "+", │ │ │ │ + zoomInId: "olZoomInLink", │ │ │ │ + zoomOutText: "−", │ │ │ │ + zoomOutId: "olZoomOutLink", │ │ │ │ + draw: function() { │ │ │ │ + var div = OpenLayers.Control.prototype.draw.apply(this), │ │ │ │ + links = this.getOrCreateLinks(div), │ │ │ │ + zoomIn = links.zoomIn, │ │ │ │ + zoomOut = links.zoomOut, │ │ │ │ + eventsInstance = this.map.events; │ │ │ │ + if (zoomOut.parentNode !== div) { │ │ │ │ + eventsInstance = this.events; │ │ │ │ + eventsInstance.attachToElement(zoomOut.parentNode) │ │ │ │ + } │ │ │ │ + eventsInstance.register("buttonclick", this, this.onZoomClick); │ │ │ │ + this.zoomInLink = zoomIn; │ │ │ │ + this.zoomOutLink = zoomOut; │ │ │ │ + return div │ │ │ │ + }, │ │ │ │ + getOrCreateLinks: function(el) { │ │ │ │ + var zoomIn = document.getElementById(this.zoomInId), │ │ │ │ + zoomOut = document.getElementById(this.zoomOutId); │ │ │ │ + if (!zoomIn) { │ │ │ │ + zoomIn = document.createElement("a"); │ │ │ │ + zoomIn.href = "#zoomIn"; │ │ │ │ + zoomIn.appendChild(document.createTextNode(this.zoomInText)); │ │ │ │ + zoomIn.className = "olControlZoomIn"; │ │ │ │ + el.appendChild(zoomIn) │ │ │ │ + } │ │ │ │ + OpenLayers.Element.addClass(zoomIn, "olButton"); │ │ │ │ + if (!zoomOut) { │ │ │ │ + zoomOut = document.createElement("a"); │ │ │ │ + zoomOut.href = "#zoomOut"; │ │ │ │ + zoomOut.appendChild(document.createTextNode(this.zoomOutText)); │ │ │ │ + zoomOut.className = "olControlZoomOut"; │ │ │ │ + el.appendChild(zoomOut) │ │ │ │ + } │ │ │ │ + OpenLayers.Element.addClass(zoomOut, "olButton"); │ │ │ │ + return { │ │ │ │ + zoomIn: zoomIn, │ │ │ │ + zoomOut: zoomOut │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + onZoomClick: function(evt) { │ │ │ │ + var button = evt.buttonElement; │ │ │ │ + if (button === this.zoomInLink) { │ │ │ │ + this.map.zoomIn() │ │ │ │ + } else if (button === this.zoomOutLink) { │ │ │ │ + this.map.zoomOut() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + if (this.map) { │ │ │ │ + this.map.events.unregister("buttonclick", this, this.onZoomClick) │ │ │ │ + } │ │ │ │ + delete this.zoomInLink; │ │ │ │ + delete this.zoomOutLink; │ │ │ │ + OpenLayers.Control.prototype.destroy.apply(this) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Control.Zoom" │ │ │ │ }); │ │ ├── ./usr/share/javascript/openlayers/OpenLayers.min.js │ │ │ ├── js-beautify {} │ │ │ │ @@ -2063,14 +2063,241 @@ │ │ │ │ _isBottomRounded: function() { │ │ │ │ return this._hasString(this.options.corners, "all", "bottom", "bl", "br") │ │ │ │ }, │ │ │ │ _hasSingleTextChild: function(el) { │ │ │ │ return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3 │ │ │ │ } │ │ │ │ }; │ │ │ │ +OpenLayers.Spherical = OpenLayers.Spherical || {}; │ │ │ │ +OpenLayers.Spherical.DEFAULT_RADIUS = 6378137; │ │ │ │ +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)) │ │ │ │ +}; │ │ │ │ +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.Util = OpenLayers.Util || {}; │ │ │ │ +OpenLayers.Util.vendorPrefix = function() { │ │ │ │ + "use strict"; │ │ │ │ + var VENDOR_PREFIXES = ["", "O", "ms", "Moz", "Webkit"], │ │ │ │ + divStyle = document.createElement("div").style, │ │ │ │ + cssCache = {}, │ │ │ │ + jsCache = {}; │ │ │ │ + │ │ │ │ + function domToCss(prefixedDom) { │ │ │ │ + if (!prefixedDom) { │ │ │ │ + return null │ │ │ │ + } │ │ │ │ + return prefixedDom.replace(/([A-Z])/g, function(c) { │ │ │ │ + return "-" + c.toLowerCase() │ │ │ │ + }).replace(/^ms-/, "-ms-") │ │ │ │ + } │ │ │ │ + │ │ │ │ + function css(property) { │ │ │ │ + if (cssCache[property] === undefined) { │ │ │ │ + var domProperty = property.replace(/(-[\s\S])/g, function(c) { │ │ │ │ + return c.charAt(1).toUpperCase() │ │ │ │ + }); │ │ │ │ + var prefixedDom = style(domProperty); │ │ │ │ + cssCache[property] = domToCss(prefixedDom) │ │ │ │ + } │ │ │ │ + return cssCache[property] │ │ │ │ + } │ │ │ │ + │ │ │ │ + function js(obj, property) { │ │ │ │ + if (jsCache[property] === undefined) { │ │ │ │ + var tmpProp, i = 0, │ │ │ │ + l = VENDOR_PREFIXES.length, │ │ │ │ + prefix, isStyleObj = typeof obj.cssText !== "undefined"; │ │ │ │ + jsCache[property] = null; │ │ │ │ + for (; i < l; i++) { │ │ │ │ + prefix = VENDOR_PREFIXES[i]; │ │ │ │ + if (prefix) { │ │ │ │ + if (!isStyleObj) { │ │ │ │ + prefix = prefix.toLowerCase() │ │ │ │ + } │ │ │ │ + tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1) │ │ │ │ + } else { │ │ │ │ + tmpProp = property │ │ │ │ + } │ │ │ │ + if (obj[tmpProp] !== undefined) { │ │ │ │ + jsCache[property] = tmpProp; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return jsCache[property] │ │ │ │ + } │ │ │ │ + │ │ │ │ + function style(property) { │ │ │ │ + return js(divStyle, property) │ │ │ │ + } │ │ │ │ + return { │ │ │ │ + css: css, │ │ │ │ + js: js, │ │ │ │ + style: style, │ │ │ │ + cssCache: cssCache, │ │ │ │ + jsCache: jsCache │ │ │ │ + } │ │ │ │ +}(); │ │ │ │ +OpenLayers.Animation = function(window) { │ │ │ │ + var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame"); │ │ │ │ + var isNative = !!requestAnimationFrame; │ │ │ │ + var requestFrame = function() { │ │ │ │ + var request = window[requestAnimationFrame] || function(callback, element) { │ │ │ │ + window.setTimeout(callback, 16) │ │ │ │ + }; │ │ │ │ + return function(callback, element) { │ │ │ │ + request.apply(window, [callback, element]) │ │ │ │ + } │ │ │ │ + }(); │ │ │ │ + var counter = 0; │ │ │ │ + var loops = {}; │ │ │ │ + │ │ │ │ + function start(callback, duration, element) { │ │ │ │ + duration = duration > 0 ? duration : Number.POSITIVE_INFINITY; │ │ │ │ + var id = ++counter; │ │ │ │ + var start = +new Date; │ │ │ │ + loops[id] = function() { │ │ │ │ + if (loops[id] && +new Date - start <= duration) { │ │ │ │ + callback(); │ │ │ │ + if (loops[id]) { │ │ │ │ + requestFrame(loops[id], element) │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + delete loops[id] │ │ │ │ + } │ │ │ │ + }; │ │ │ │ + requestFrame(loops[id], element); │ │ │ │ + return id │ │ │ │ + } │ │ │ │ + │ │ │ │ + function stop(id) { │ │ │ │ + delete loops[id] │ │ │ │ + } │ │ │ │ + return { │ │ │ │ + isNative: isNative, │ │ │ │ + requestFrame: requestFrame, │ │ │ │ + start: start, │ │ │ │ + stop: stop │ │ │ │ + } │ │ │ │ +}(window); │ │ │ │ +OpenLayers.Tween = OpenLayers.Class({ │ │ │ │ + easing: null, │ │ │ │ + begin: null, │ │ │ │ + finish: null, │ │ │ │ + duration: null, │ │ │ │ + callbacks: null, │ │ │ │ + time: null, │ │ │ │ + minFrameRate: null, │ │ │ │ + startTime: null, │ │ │ │ + animationId: null, │ │ │ │ + playing: false, │ │ │ │ + initialize: function(easing) { │ │ │ │ + this.easing = easing ? easing : OpenLayers.Easing.Expo.easeOut │ │ │ │ + }, │ │ │ │ + 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)) │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + 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) { │ │ │ │ + if (((new Date).getTime() - this.startTime) / this.time <= 1e3 / this.minFrameRate) { │ │ │ │ + this.callbacks.eachStep.call(this, value) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (this.time > this.duration) { │ │ │ │ + this.stop() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Tween" │ │ │ │ +}); │ │ │ │ +OpenLayers.Easing = { │ │ │ │ + CLASS_NAME: "OpenLayers.Easing" │ │ │ │ +}; │ │ │ │ +OpenLayers.Easing.Linear = { │ │ │ │ + easeIn: function(t, b, c, d) { │ │ │ │ + return c * t / d + b │ │ │ │ + }, │ │ │ │ + easeOut: function(t, b, c, d) { │ │ │ │ + return c * t / d + b │ │ │ │ + }, │ │ │ │ + easeInOut: function(t, b, c, d) { │ │ │ │ + return c * t / d + b │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Easing.Linear" │ │ │ │ +}; │ │ │ │ +OpenLayers.Easing.Expo = { │ │ │ │ + easeIn: function(t, b, c, d) { │ │ │ │ + return t == 0 ? b : c * Math.pow(2, 10 * (t / d - 1)) + b │ │ │ │ + }, │ │ │ │ + easeOut: function(t, b, c, d) { │ │ │ │ + return t == d ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b │ │ │ │ + }, │ │ │ │ + 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" │ │ │ │ +}; │ │ │ │ +OpenLayers.Easing.Quad = { │ │ │ │ + easeIn: function(t, b, c, d) { │ │ │ │ + return c * (t /= d) * t + b │ │ │ │ + }, │ │ │ │ + easeOut: function(t, b, c, d) { │ │ │ │ + return -c * (t /= d) * (t - 2) + b │ │ │ │ + }, │ │ │ │ + easeInOut: function(t, b, c, d) { │ │ │ │ + if ((t /= d / 2) < 1) return c / 2 * t * t + b; │ │ │ │ + return -c / 2 * (--t * (t - 2) - 1) + b │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Easing.Quad" │ │ │ │ +}; │ │ │ │ OpenLayers.Feature = OpenLayers.Class({ │ │ │ │ layer: null, │ │ │ │ id: null, │ │ │ │ lonlat: null, │ │ │ │ data: null, │ │ │ │ marker: null, │ │ │ │ popupClass: null, │ │ │ │ @@ -2511,14 +2738,165 @@ │ │ │ │ if (typeof value == "string" && value.indexOf("${") != -1) { │ │ │ │ value = OpenLayers.String.format(value, context, [feature, property]); │ │ │ │ value = isNaN(value) || !value ? value : parseFloat(value) │ │ │ │ } │ │ │ │ return value │ │ │ │ }; │ │ │ │ OpenLayers.Style.SYMBOLIZER_PREFIXES = ["Point", "Line", "Polygon", "Text", "Raster"]; │ │ │ │ +OpenLayers.StyleMap = OpenLayers.Class({ │ │ │ │ + styles: null, │ │ │ │ + extendDefault: true, │ │ │ │ + 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"]) │ │ │ │ + }; │ │ │ │ + if (style instanceof OpenLayers.Style) { │ │ │ │ + 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) { │ │ │ │ + this.styles[key] = style[key] │ │ │ │ + } else if (typeof style[key] == "object") { │ │ │ │ + this.styles[key] = new OpenLayers.Style(style[key]) │ │ │ │ + } else { │ │ │ │ + this.styles["default"] = new OpenLayers.Style(style); │ │ │ │ + this.styles["select"] = new OpenLayers.Style(style); │ │ │ │ + this.styles["temporary"] = new OpenLayers.Style(style); │ │ │ │ + this.styles["delete"] = new OpenLayers.Style(style); │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + OpenLayers.Util.extend(this, options) │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + for (var key in this.styles) { │ │ │ │ + this.styles[key].destroy() │ │ │ │ + } │ │ │ │ + this.styles = null │ │ │ │ + }, │ │ │ │ + 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)) │ │ │ │ + }, │ │ │ │ + addUniqueValueRules: function(renderIntent, property, symbolizers, context) { │ │ │ │ + var rules = []; │ │ │ │ + for (var value in symbolizers) { │ │ │ │ + rules.push(new OpenLayers.Rule({ │ │ │ │ + symbolizer: symbolizers[value], │ │ │ │ + context: context, │ │ │ │ + filter: new OpenLayers.Filter.Comparison({ │ │ │ │ + type: OpenLayers.Filter.Comparison.EQUAL_TO, │ │ │ │ + property: property, │ │ │ │ + value: value │ │ │ │ + }) │ │ │ │ + })) │ │ │ │ + } │ │ │ │ + this.styles[renderIntent].addRules(rules) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.StyleMap" │ │ │ │ +}); │ │ │ │ +OpenLayers.Icon = OpenLayers.Class({ │ │ │ │ + url: null, │ │ │ │ + size: null, │ │ │ │ + offset: null, │ │ │ │ + calculateOffset: null, │ │ │ │ + imageDiv: null, │ │ │ │ + px: null, │ │ │ │ + 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) │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.erase(); │ │ │ │ + OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild); │ │ │ │ + this.imageDiv.innerHTML = ""; │ │ │ │ + this.imageDiv = null │ │ │ │ + }, │ │ │ │ + clone: function() { │ │ │ │ + return new OpenLayers.Icon(this.url, this.size, this.offset, this.calculateOffset) │ │ │ │ + }, │ │ │ │ + setSize: function(size) { │ │ │ │ + if (size != null) { │ │ │ │ + this.size = size │ │ │ │ + } │ │ │ │ + this.draw() │ │ │ │ + }, │ │ │ │ + setUrl: function(url) { │ │ │ │ + if (url != null) { │ │ │ │ + this.url = url │ │ │ │ + } │ │ │ │ + this.draw() │ │ │ │ + }, │ │ │ │ + draw: function(px) { │ │ │ │ + OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, this.size, this.url, "absolute"); │ │ │ │ + this.moveTo(px); │ │ │ │ + return this.imageDiv │ │ │ │ + }, │ │ │ │ + erase: function() { │ │ │ │ + if (this.imageDiv != null && this.imageDiv.parentNode != null) { │ │ │ │ + OpenLayers.Element.remove(this.imageDiv) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ + OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, null, null, null, null, opacity) │ │ │ │ + }, │ │ │ │ + moveTo: function(px) { │ │ │ │ + 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 │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + display: function(display) { │ │ │ │ + this.imageDiv.style.display = display ? "" : "none" │ │ │ │ + }, │ │ │ │ + isDrawn: function() { │ │ │ │ + var isDrawn = this.imageDiv && this.imageDiv.parentNode && this.imageDiv.parentNode.nodeType != 11; │ │ │ │ + return isDrawn │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Icon" │ │ │ │ +}); │ │ │ │ OpenLayers.Rule = OpenLayers.Class({ │ │ │ │ id: null, │ │ │ │ name: null, │ │ │ │ title: null, │ │ │ │ description: null, │ │ │ │ context: null, │ │ │ │ filter: null, │ │ │ │ @@ -2596,227 +2974,107 @@ │ │ │ │ } │ │ │ │ options.filter = this.filter && this.filter.clone(); │ │ │ │ options.context = this.context && OpenLayers.Util.extend({}, this.context); │ │ │ │ return new OpenLayers.Rule(options) │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.Rule" │ │ │ │ }); │ │ │ │ -OpenLayers.Util = OpenLayers.Util || {}; │ │ │ │ -OpenLayers.Util.vendorPrefix = function() { │ │ │ │ - "use strict"; │ │ │ │ - var VENDOR_PREFIXES = ["", "O", "ms", "Moz", "Webkit"], │ │ │ │ - divStyle = document.createElement("div").style, │ │ │ │ - cssCache = {}, │ │ │ │ - jsCache = {}; │ │ │ │ - │ │ │ │ - function domToCss(prefixedDom) { │ │ │ │ - if (!prefixedDom) { │ │ │ │ - return null │ │ │ │ - } │ │ │ │ - return prefixedDom.replace(/([A-Z])/g, function(c) { │ │ │ │ - return "-" + c.toLowerCase() │ │ │ │ - }).replace(/^ms-/, "-ms-") │ │ │ │ - } │ │ │ │ - │ │ │ │ - function css(property) { │ │ │ │ - if (cssCache[property] === undefined) { │ │ │ │ - var domProperty = property.replace(/(-[\s\S])/g, function(c) { │ │ │ │ - return c.charAt(1).toUpperCase() │ │ │ │ - }); │ │ │ │ - var prefixedDom = style(domProperty); │ │ │ │ - cssCache[property] = domToCss(prefixedDom) │ │ │ │ - } │ │ │ │ - return cssCache[property] │ │ │ │ - } │ │ │ │ - │ │ │ │ - function js(obj, property) { │ │ │ │ - if (jsCache[property] === undefined) { │ │ │ │ - var tmpProp, i = 0, │ │ │ │ - l = VENDOR_PREFIXES.length, │ │ │ │ - prefix, isStyleObj = typeof obj.cssText !== "undefined"; │ │ │ │ - jsCache[property] = null; │ │ │ │ - for (; i < l; i++) { │ │ │ │ - prefix = VENDOR_PREFIXES[i]; │ │ │ │ - if (prefix) { │ │ │ │ - if (!isStyleObj) { │ │ │ │ - prefix = prefix.toLowerCase() │ │ │ │ - } │ │ │ │ - tmpProp = prefix + property.charAt(0).toUpperCase() + property.slice(1) │ │ │ │ - } else { │ │ │ │ - tmpProp = property │ │ │ │ - } │ │ │ │ - if (obj[tmpProp] !== undefined) { │ │ │ │ - jsCache[property] = tmpProp; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return jsCache[property] │ │ │ │ - } │ │ │ │ - │ │ │ │ - function style(property) { │ │ │ │ - return js(divStyle, property) │ │ │ │ - } │ │ │ │ - return { │ │ │ │ - css: css, │ │ │ │ - js: js, │ │ │ │ - style: style, │ │ │ │ - cssCache: cssCache, │ │ │ │ - jsCache: jsCache │ │ │ │ - } │ │ │ │ -}(); │ │ │ │ -OpenLayers.Animation = function(window) { │ │ │ │ - var requestAnimationFrame = OpenLayers.Util.vendorPrefix.js(window, "requestAnimationFrame"); │ │ │ │ - var isNative = !!requestAnimationFrame; │ │ │ │ - var requestFrame = function() { │ │ │ │ - var request = window[requestAnimationFrame] || function(callback, element) { │ │ │ │ - window.setTimeout(callback, 16) │ │ │ │ - }; │ │ │ │ - return function(callback, element) { │ │ │ │ - request.apply(window, [callback, element]) │ │ │ │ - } │ │ │ │ - }(); │ │ │ │ - var counter = 0; │ │ │ │ - var loops = {}; │ │ │ │ - │ │ │ │ - function start(callback, duration, element) { │ │ │ │ - duration = duration > 0 ? duration : Number.POSITIVE_INFINITY; │ │ │ │ - var id = ++counter; │ │ │ │ - var start = +new Date; │ │ │ │ - loops[id] = function() { │ │ │ │ - if (loops[id] && +new Date - start <= duration) { │ │ │ │ - callback(); │ │ │ │ - if (loops[id]) { │ │ │ │ - requestFrame(loops[id], element) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - delete loops[id] │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - requestFrame(loops[id], element); │ │ │ │ - return id │ │ │ │ - } │ │ │ │ - │ │ │ │ - function stop(id) { │ │ │ │ - delete loops[id] │ │ │ │ - } │ │ │ │ - return { │ │ │ │ - isNative: isNative, │ │ │ │ - requestFrame: requestFrame, │ │ │ │ - start: start, │ │ │ │ - stop: stop │ │ │ │ - } │ │ │ │ -}(window); │ │ │ │ -OpenLayers.Tween = OpenLayers.Class({ │ │ │ │ - easing: null, │ │ │ │ - begin: null, │ │ │ │ - finish: null, │ │ │ │ - duration: null, │ │ │ │ - callbacks: null, │ │ │ │ - time: null, │ │ │ │ - minFrameRate: null, │ │ │ │ - startTime: null, │ │ │ │ - animationId: null, │ │ │ │ - playing: false, │ │ │ │ - initialize: function(easing) { │ │ │ │ - this.easing = easing ? easing : OpenLayers.Easing.Expo.easeOut │ │ │ │ +OpenLayers.Symbolizer = OpenLayers.Class({ │ │ │ │ + zIndex: 0, │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Util.extend(this, config) │ │ │ │ }, │ │ │ │ - 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)) │ │ │ │ + clone: function() { │ │ │ │ + var Type = eval(this.CLASS_NAME); │ │ │ │ + return new Type(OpenLayers.Util.extend({}, this)) │ │ │ │ }, │ │ │ │ - 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 │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer" │ │ │ │ +}); │ │ │ │ +OpenLayers.Symbolizer.Point = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments) │ │ │ │ }, │ │ │ │ - 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) { │ │ │ │ - if (((new Date).getTime() - this.startTime) / this.time <= 1e3 / this.minFrameRate) { │ │ │ │ - this.callbacks.eachStep.call(this, value) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.time > this.duration) { │ │ │ │ - this.stop() │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Point" │ │ │ │ +}); │ │ │ │ +OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Tween" │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Line" │ │ │ │ }); │ │ │ │ -OpenLayers.Easing = { │ │ │ │ - CLASS_NAME: "OpenLayers.Easing" │ │ │ │ -}; │ │ │ │ -OpenLayers.Easing.Linear = { │ │ │ │ - easeIn: function(t, b, c, d) { │ │ │ │ - return c * t / d + b │ │ │ │ +OpenLayers.Symbolizer.Polygon = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments) │ │ │ │ }, │ │ │ │ - easeOut: function(t, b, c, d) { │ │ │ │ - return c * t / d + b │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Polygon" │ │ │ │ +}); │ │ │ │ +OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments) │ │ │ │ }, │ │ │ │ - easeInOut: function(t, b, c, d) { │ │ │ │ - return c * t / d + b │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Text" │ │ │ │ +}); │ │ │ │ +OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Easing.Linear" │ │ │ │ -}; │ │ │ │ -OpenLayers.Easing.Expo = { │ │ │ │ - easeIn: function(t, b, c, d) { │ │ │ │ - return t == 0 ? b : c * Math.pow(2, 10 * (t / d - 1)) + b │ │ │ │ + CLASS_NAME: "OpenLayers.Symbolizer.Raster" │ │ │ │ +}); │ │ │ │ +OpenLayers.Style2 = OpenLayers.Class({ │ │ │ │ + id: null, │ │ │ │ + name: null, │ │ │ │ + title: null, │ │ │ │ + description: null, │ │ │ │ + layerName: null, │ │ │ │ + isDefault: false, │ │ │ │ + rules: null, │ │ │ │ + initialize: function(config) { │ │ │ │ + OpenLayers.Util.extend(this, config); │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_") │ │ │ │ }, │ │ │ │ - easeOut: function(t, b, c, d) { │ │ │ │ - return t == d ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b │ │ │ │ + destroy: function() { │ │ │ │ + for (var i = 0, len = this.rules.length; i < len; i++) { │ │ │ │ + this.rules[i].destroy() │ │ │ │ + } │ │ │ │ + delete this.rules │ │ │ │ }, │ │ │ │ - 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 │ │ │ │ + clone: function() { │ │ │ │ + var config = OpenLayers.Util.extend({}, this); │ │ │ │ + if (this.rules) { │ │ │ │ + config.rules = []; │ │ │ │ + for (var i = 0, len = this.rules.length; i < len; ++i) { │ │ │ │ + config.rules.push(this.rules[i].clone()) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return new OpenLayers.Style2(config) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Easing.Expo" │ │ │ │ -}; │ │ │ │ -OpenLayers.Easing.Quad = { │ │ │ │ - easeIn: function(t, b, c, d) { │ │ │ │ - return c * (t /= d) * t + b │ │ │ │ + CLASS_NAME: "OpenLayers.Style2" │ │ │ │ +}); │ │ │ │ +OpenLayers.Filter = OpenLayers.Class({ │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options) │ │ │ │ }, │ │ │ │ - easeOut: function(t, b, c, d) { │ │ │ │ - return -c * (t /= d) * (t - 2) + b │ │ │ │ + destroy: function() {}, │ │ │ │ + evaluate: function(context) { │ │ │ │ + return true │ │ │ │ }, │ │ │ │ - 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 │ │ │ │ + clone: function() { │ │ │ │ + return null │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Easing.Quad" │ │ │ │ -}; │ │ │ │ + toString: function() { │ │ │ │ + var string; │ │ │ │ + if (OpenLayers.Format && OpenLayers.Format.CQL) { │ │ │ │ + string = OpenLayers.Format.CQL.prototype.write(this) │ │ │ │ + } else { │ │ │ │ + string = Object.prototype.toString.call(this) │ │ │ │ + } │ │ │ │ + return string │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Filter" │ │ │ │ +}); │ │ │ │ OpenLayers.Event = { │ │ │ │ observers: false, │ │ │ │ KEY_SPACE: 32, │ │ │ │ KEY_BACKSPACE: 8, │ │ │ │ KEY_TAB: 9, │ │ │ │ KEY_RETURN: 13, │ │ │ │ KEY_ESC: 27, │ │ │ │ @@ -5107,88 +5365,157 @@ │ │ │ │ }; │ │ │ │ bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions) │ │ │ │ } │ │ │ │ return bounds │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.Layer" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ - URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2, │ │ │ │ - url: null, │ │ │ │ - params: null, │ │ │ │ - reproject: false, │ │ │ │ - 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) │ │ │ │ +OpenLayers.Marker = OpenLayers.Class({ │ │ │ │ + icon: null, │ │ │ │ + lonlat: null, │ │ │ │ + events: null, │ │ │ │ + map: null, │ │ │ │ + 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) │ │ │ │ }, │ │ │ │ destroy: function() { │ │ │ │ - this.url = null; │ │ │ │ - this.params = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments) │ │ │ │ + this.erase(); │ │ │ │ + this.map = null; │ │ │ │ + this.events.destroy(); │ │ │ │ + this.events = null; │ │ │ │ + if (this.icon != null) { │ │ │ │ + this.icon.destroy(); │ │ │ │ + this.icon = null │ │ │ │ + } │ │ │ │ }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.HTTPRequest(this.name, this.url, this.params, this.getOptions()) │ │ │ │ + draw: function(px) { │ │ │ │ + return this.icon.draw(px) │ │ │ │ + }, │ │ │ │ + erase: function() { │ │ │ │ + if (this.icon != null) { │ │ │ │ + this.icon.erase() │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ }, │ │ │ │ - setUrl: function(newUrl) { │ │ │ │ - this.url = newUrl │ │ │ │ + moveTo: function(px) { │ │ │ │ + if (px != null && this.icon != null) { │ │ │ │ + this.icon.moveTo(px) │ │ │ │ + } │ │ │ │ + this.lonlat = this.map.getLonLatFromLayerPx(px) │ │ │ │ }, │ │ │ │ - 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" │ │ │ │ + isDrawn: function() { │ │ │ │ + var isDrawn = this.icon && this.icon.isDrawn(); │ │ │ │ + return isDrawn │ │ │ │ + }, │ │ │ │ + onScreen: function() { │ │ │ │ + var onScreen = false; │ │ │ │ + if (this.map) { │ │ │ │ + var screenBounds = this.map.getExtent(); │ │ │ │ + onScreen = screenBounds.containsLonLat(this.lonlat) │ │ │ │ + } │ │ │ │ + return onScreen │ │ │ │ + }, │ │ │ │ + inflate: function(inflate) { │ │ │ │ + if (this.icon) { │ │ │ │ + this.icon.setSize({ │ │ │ │ + w: this.icon.size.w * inflate, │ │ │ │ + h: this.icon.size.h * inflate │ │ │ │ }) │ │ │ │ } │ │ │ │ - return ret │ │ │ │ }, │ │ │ │ - redraw: function(force) { │ │ │ │ - if (force) { │ │ │ │ - return this.mergeNewParams({ │ │ │ │ - _olSalt: Math.random() │ │ │ │ + setOpacity: function(opacity) { │ │ │ │ + this.icon.setOpacity(opacity) │ │ │ │ + }, │ │ │ │ + setUrl: function(url) { │ │ │ │ + this.icon.setUrl(url) │ │ │ │ + }, │ │ │ │ + display: function(display) { │ │ │ │ + this.icon.display(display) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Marker" │ │ │ │ +}); │ │ │ │ +OpenLayers.Marker.defaultIcon = function() { │ │ │ │ + return new OpenLayers.Icon(OpenLayers.Util.getImageLocation("marker.png"), { │ │ │ │ + w: 21, │ │ │ │ + h: 25 │ │ │ │ + }, { │ │ │ │ + x: -10.5, │ │ │ │ + y: -25 │ │ │ │ + }) │ │ │ │ +}; │ │ │ │ +OpenLayers.Protocol = OpenLayers.Class({ │ │ │ │ + format: null, │ │ │ │ + options: null, │ │ │ │ + autoDestroy: true, │ │ │ │ + defaultFilter: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.options = options │ │ │ │ + }, │ │ │ │ + mergeWithDefaultFilter: function(filter) { │ │ │ │ + var merged; │ │ │ │ + if (filter && this.defaultFilter) { │ │ │ │ + merged = new OpenLayers.Filter.Logical({ │ │ │ │ + type: OpenLayers.Filter.Logical.AND, │ │ │ │ + filters: [this.defaultFilter, filter] │ │ │ │ }) │ │ │ │ } else { │ │ │ │ - return OpenLayers.Layer.prototype.redraw.apply(this, []) │ │ │ │ + merged = filter || this.defaultFilter || undefined │ │ │ │ } │ │ │ │ + return merged │ │ │ │ }, │ │ │ │ - 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)] │ │ │ │ + destroy: function() { │ │ │ │ + this.options = null; │ │ │ │ + this.format = null │ │ │ │ }, │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ - var url = altUrl || this.url; │ │ │ │ - var allParams = OpenLayers.Util.extend({}, this.params); │ │ │ │ - allParams = OpenLayers.Util.extend(allParams, newParams); │ │ │ │ - var paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(paramsString, url) │ │ │ │ - } │ │ │ │ - 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) │ │ │ │ + read: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + options.filter = this.mergeWithDefaultFilter(options.filter) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.HTTPRequest" │ │ │ │ + create: function() {}, │ │ │ │ + update: function() {}, │ │ │ │ + delete: function() {}, │ │ │ │ + commit: function() {}, │ │ │ │ + abort: function(response) {}, │ │ │ │ + createCallback: function(method, response, options) { │ │ │ │ + return OpenLayers.Function.bind(function() { │ │ │ │ + method.apply(this, [response, options]) │ │ │ │ + }, this) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol" │ │ │ │ +}); │ │ │ │ +OpenLayers.Protocol.Response = OpenLayers.Class({ │ │ │ │ + code: null, │ │ │ │ + requestType: null, │ │ │ │ + last: true, │ │ │ │ + features: null, │ │ │ │ + data: null, │ │ │ │ + reqFeatures: null, │ │ │ │ + priv: null, │ │ │ │ + error: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options) │ │ │ │ + }, │ │ │ │ + success: function() { │ │ │ │ + return this.code > 0 │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.Response" │ │ │ │ }); │ │ │ │ +OpenLayers.Protocol.Response.SUCCESS = 1; │ │ │ │ +OpenLayers.Protocol.Response.FAILURE = 0; │ │ │ │ OpenLayers.Tile = OpenLayers.Class({ │ │ │ │ events: null, │ │ │ │ eventListeners: null, │ │ │ │ id: null, │ │ │ │ layer: null, │ │ │ │ url: null, │ │ │ │ bounds: null, │ │ │ │ @@ -5274,1133 +5601,172 @@ │ │ │ │ if (redraw) { │ │ │ │ this.draw() │ │ │ │ } │ │ │ │ }, │ │ │ │ clear: function(draw) {}, │ │ │ │ CLASS_NAME: "OpenLayers.Tile" │ │ │ │ }); │ │ │ │ -OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { │ │ │ │ - url: null, │ │ │ │ - imgDiv: null, │ │ │ │ - frame: null, │ │ │ │ - imageReloadAttempts: null, │ │ │ │ - layerAlphaHack: null, │ │ │ │ - asyncRequestId: null, │ │ │ │ - maxGetUrlLength: null, │ │ │ │ - canvasContext: null, │ │ │ │ - crossOriginKeyword: null, │ │ │ │ - initialize: function(layer, position, bounds, url, size, options) { │ │ │ │ - OpenLayers.Tile.prototype.initialize.apply(this, arguments); │ │ │ │ - this.url = url; │ │ │ │ - this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack(); │ │ │ │ - if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) { │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ +OpenLayers.Renderer = OpenLayers.Class({ │ │ │ │ + container: null, │ │ │ │ + root: null, │ │ │ │ + extent: null, │ │ │ │ + locked: false, │ │ │ │ + size: null, │ │ │ │ + resolution: null, │ │ │ │ + map: null, │ │ │ │ + featureDx: 0, │ │ │ │ + initialize: function(containerID, options) { │ │ │ │ + this.container = OpenLayers.Util.getElement(containerID); │ │ │ │ + OpenLayers.Util.extend(this, options) │ │ │ │ }, │ │ │ │ destroy: function() { │ │ │ │ - if (this.imgDiv) { │ │ │ │ - this.clear(); │ │ │ │ - this.imgDiv = null; │ │ │ │ - this.frame = null │ │ │ │ - } │ │ │ │ - this.asyncRequestId = null; │ │ │ │ - OpenLayers.Tile.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - draw: function() { │ │ │ │ - var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments); │ │ │ │ - if (shouldDraw) { │ │ │ │ - if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { │ │ │ │ - this.bounds = this.getBoundsFromBaseLayer(this.position) │ │ │ │ - } │ │ │ │ - if (this.isLoading) { │ │ │ │ - this._loadEvent = "reload" │ │ │ │ - } else { │ │ │ │ - this.isLoading = true; │ │ │ │ - this._loadEvent = "loadstart" │ │ │ │ - } │ │ │ │ - this.renderTile(); │ │ │ │ - this.positionTile() │ │ │ │ - } else if (shouldDraw === false) { │ │ │ │ - this.unload() │ │ │ │ - } │ │ │ │ - return shouldDraw │ │ │ │ - }, │ │ │ │ - renderTile: function() { │ │ │ │ - if (this.layer.async) { │ │ │ │ - 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 { │ │ │ │ - this.url = this.layer.getURL(this.bounds); │ │ │ │ - this.initImage() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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" │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - getImage: function() { │ │ │ │ - if (!this.imgDiv) { │ │ │ │ - this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false); │ │ │ │ - var style = this.imgDiv.style; │ │ │ │ - if (this.frame) { │ │ │ │ - var left = 0, │ │ │ │ - top = 0; │ │ │ │ - if (this.layer.gutter) { │ │ │ │ - left = this.layer.gutter / this.layer.tileSize.w * 100; │ │ │ │ - top = this.layer.gutter / this.layer.tileSize.h * 100 │ │ │ │ - } │ │ │ │ - style.left = -left + "%"; │ │ │ │ - style.top = -top + "%"; │ │ │ │ - style.width = 2 * left + 100 + "%"; │ │ │ │ - style.height = 2 * top + 100 + "%" │ │ │ │ - } │ │ │ │ - style.visibility = "hidden"; │ │ │ │ - style.opacity = 0; │ │ │ │ - if (this.layer.opacity < 1) { │ │ │ │ - style.filter = "alpha(opacity=" + this.layer.opacity * 100 + ")" │ │ │ │ - } │ │ │ │ - style.position = "absolute"; │ │ │ │ - if (this.layerAlphaHack) { │ │ │ │ - style.paddingTop = style.height; │ │ │ │ - style.height = "0"; │ │ │ │ - style.width = "100%" │ │ │ │ - } │ │ │ │ - if (this.frame) { │ │ │ │ - this.frame.appendChild(this.imgDiv) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return this.imgDiv │ │ │ │ - }, │ │ │ │ - setImage: function(img) { │ │ │ │ - this.imgDiv = img │ │ │ │ - }, │ │ │ │ - initImage: function() { │ │ │ │ - if (!this.url && !this.imgDiv) { │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - setImgSrc: function(url) { │ │ │ │ - var img = this.imgDiv; │ │ │ │ - if (url) { │ │ │ │ - img.style.visibility = "hidden"; │ │ │ │ - img.style.opacity = 0; │ │ │ │ - if (this.crossOriginKeyword) { │ │ │ │ - if (url.substr(0, 5) !== "data:") { │ │ │ │ - img.setAttribute("crossorigin", this.crossOriginKeyword) │ │ │ │ - } else { │ │ │ │ - img.removeAttribute("crossorigin") │ │ │ │ - } │ │ │ │ - } │ │ │ │ - img.src = url │ │ │ │ - } else { │ │ │ │ - this.stopLoading(); │ │ │ │ - this.imgDiv = null; │ │ │ │ - if (img.parentNode) { │ │ │ │ - img.parentNode.removeChild(img) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getTile: function() { │ │ │ │ - return this.frame ? this.frame : this.getImage() │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - 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')" │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - stopLoading: function() { │ │ │ │ - OpenLayers.Event.stopObservingElement(this.imgDiv); │ │ │ │ - window.clearTimeout(this._loadTimeout); │ │ │ │ - delete this._loadTimeout │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ + this.container = null; │ │ │ │ + this.extent = null; │ │ │ │ + this.size = null; │ │ │ │ + this.resolution = null; │ │ │ │ + this.map = null │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Tile.Image" │ │ │ │ -}); │ │ │ │ -OpenLayers.Tile.Image.IMAGE = function() { │ │ │ │ - var img = new Image; │ │ │ │ - img.className = "olTileImage"; │ │ │ │ - img.galleryImg = "no"; │ │ │ │ - return img │ │ │ │ -}(); │ │ │ │ -OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { │ │ │ │ - tileSize: null, │ │ │ │ - tileOriginCorner: "bl", │ │ │ │ - tileOrigin: null, │ │ │ │ - tileOptions: null, │ │ │ │ - tileClass: OpenLayers.Tile.Image, │ │ │ │ - grid: null, │ │ │ │ - singleTile: false, │ │ │ │ - ratio: 1.5, │ │ │ │ - buffer: 0, │ │ │ │ - transitionEffect: "resize", │ │ │ │ - numLoadingTiles: 0, │ │ │ │ - serverResolutions: null, │ │ │ │ - loading: false, │ │ │ │ - backBuffer: null, │ │ │ │ - gridResolution: null, │ │ │ │ - backBufferResolution: null, │ │ │ │ - backBufferLonLat: null, │ │ │ │ - backBufferTimerId: null, │ │ │ │ - removeBackBufferDelay: null, │ │ │ │ - className: null, │ │ │ │ - gridLayout: null, │ │ │ │ - rowSign: null, │ │ │ │ - transitionendEvents: ["transitionend", "webkitTransitionEnd", "otransitionend", "oTransitionEnd"], │ │ │ │ - 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 │ │ │ │ + supported: function() { │ │ │ │ + return false │ │ │ │ }, │ │ │ │ - initProperties: function() { │ │ │ │ - if (this.options.removeBackBufferDelay === undefined) { │ │ │ │ - this.removeBackBufferDelay = this.singleTile ? 0 : 2500 │ │ │ │ + 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 (this.options.className === undefined) { │ │ │ │ - this.className = this.singleTile ? "olLayerGridSingleTile" : "olLayerGrid" │ │ │ │ + if (resolutionChanged) { │ │ │ │ + this.resolution = null │ │ │ │ } │ │ │ │ + return true │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map); │ │ │ │ - OpenLayers.Element.addClass(this.div, this.className) │ │ │ │ - }, │ │ │ │ - removeMap: function(map) { │ │ │ │ - this.removeBackBuffer() │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.removeBackBuffer(); │ │ │ │ - this.clearGrid(); │ │ │ │ - this.grid = null; │ │ │ │ - this.tileSize = null; │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ + setSize: function(size) { │ │ │ │ + this.size = size.clone(); │ │ │ │ + this.resolution = null │ │ │ │ }, │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ + getResolution: function() { │ │ │ │ + this.resolution = this.resolution || this.map.getResolution(); │ │ │ │ + return this.resolution │ │ │ │ }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Grid(this.name, this.url, this.params, this.getOptions()) │ │ │ │ - } │ │ │ │ - obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]); │ │ │ │ - if (this.tileSize != null) { │ │ │ │ - obj.tileSize = this.tileSize.clone() │ │ │ │ + drawFeature: function(feature, style) { │ │ │ │ + if (style == null) { │ │ │ │ + style = feature.style │ │ │ │ } │ │ │ │ - obj.grid = []; │ │ │ │ - obj.gridResolution = null; │ │ │ │ - obj.backBuffer = null; │ │ │ │ - obj.backBufferTimerId = null; │ │ │ │ - obj.loading = false; │ │ │ │ - obj.numLoadingTiles = 0; │ │ │ │ - return obj │ │ │ │ - }, │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments); │ │ │ │ - bounds = bounds || this.map.getExtent(); │ │ │ │ - if (bounds != null) { │ │ │ │ - var forceReTile = !this.grid.length || zoomChanged; │ │ │ │ - var tilesBounds = this.getTilesBounds(); │ │ │ │ - var resolution = this.map.getResolution(); │ │ │ │ - var serverResolution = this.getServerResolution(resolution); │ │ │ │ - if (this.singleTile) { │ │ │ │ - if (forceReTile || !dragging && !tilesBounds.containsBounds(bounds)) { │ │ │ │ - if (zoomChanged && this.transitionEffect !== "resize") { │ │ │ │ - this.removeBackBuffer() │ │ │ │ - } │ │ │ │ - if (!zoomChanged || this.transitionEffect === "resize") { │ │ │ │ - this.applyBackBuffer(resolution) │ │ │ │ - } │ │ │ │ - this.initSingleTile(bounds) │ │ │ │ + if (feature.geometry) { │ │ │ │ + var bounds = feature.geometry.getBounds(); │ │ │ │ + if (bounds) { │ │ │ │ + var worldBounds; │ │ │ │ + if (this.map.baseLayer && this.map.baseLayer.wrapDateLine) { │ │ │ │ + worldBounds = this.map.getMaxExtent() │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - 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) │ │ │ │ + if (!bounds.intersectsBounds(this.extent, { │ │ │ │ + worldBounds: worldBounds │ │ │ │ + })) { │ │ │ │ + style = { │ │ │ │ + display: "none" │ │ │ │ } │ │ │ │ - this.initGriddedTiles(bounds) │ │ │ │ } else { │ │ │ │ - this.moveGriddedTiles() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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) { │ │ │ │ - if (this.map.baseLayer.wrapDateLine) { │ │ │ │ - var worldWidth = this.map.getMaxExtent().getWidth(); │ │ │ │ - var worldsAway = Math.ceil((left - x) / worldWidth); │ │ │ │ - x += worldWidth * worldsAway │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var dtx = (x - left) / (res * tileWidth); │ │ │ │ - var dty = (top - y) / (res * tileHeight); │ │ │ │ - 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, │ │ │ │ - i: Math.floor((dtx - col) * tileWidth), │ │ │ │ - j: Math.floor((dty - row) * tileHeight) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return data │ │ │ │ - }, │ │ │ │ - destroyTile: function(tile) { │ │ │ │ - this.removeTileMonitoringHooks(tile); │ │ │ │ - tile.destroy() │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ + this.calculateFeatureDx(bounds, worldBounds) │ │ │ │ } │ │ │ │ - distance = newDistance; │ │ │ │ - serverResolution = newResolution │ │ │ │ - } │ │ │ │ - resolution = serverResolution │ │ │ │ - } │ │ │ │ - return resolution │ │ │ │ - }, │ │ │ │ - getServerZoom: function() { │ │ │ │ - var resolution = this.getServerResolution(); │ │ │ │ - return this.serverResolutions ? OpenLayers.Util.indexOf(this.serverResolutions, resolution) : this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0) │ │ │ │ - }, │ │ │ │ - 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; │ │ │ │ - var topLeftTileBounds = this.grid[0][0].bounds; │ │ │ │ - this.backBufferLonLat = { │ │ │ │ - lon: topLeftTileBounds.left, │ │ │ │ - lat: topLeftTileBounds.top │ │ │ │ - }; │ │ │ │ - this.backBufferResolution = this.gridResolution │ │ │ │ - } │ │ │ │ - var ratio = this.backBufferResolution / resolution; │ │ │ │ - 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" │ │ │ │ - } │ │ │ │ - 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" │ │ │ │ - }, │ │ │ │ - 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.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) │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return backBuffer │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - moveByPx: function(dx, dy) { │ │ │ │ - if (!this.singleTile) { │ │ │ │ - this.moveGriddedTiles() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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]) │ │ │ │ - }, │ │ │ │ - getTilesBounds: function() { │ │ │ │ - var bounds = null; │ │ │ │ - var length = this.grid.length; │ │ │ │ - if (length) { │ │ │ │ - var bottomLeftTileBounds = this.grid[length - 1][0].bounds, │ │ │ │ - width = this.grid[0].length * bottomLeftTileBounds.getWidth(), │ │ │ │ - height = this.grid.length * bottomLeftTileBounds.getHeight(); │ │ │ │ - bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, bottomLeftTileBounds.bottom, bottomLeftTileBounds.left + width, bottomLeftTileBounds.bottom + height) │ │ │ │ - } │ │ │ │ - return bounds │ │ │ │ - }, │ │ │ │ - initSingleTile: function(bounds) { │ │ │ │ - this.events.triggerEvent("retile"); │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - this.removeExcessTiles(1, 1); │ │ │ │ - this.gridResolution = this.getServerResolution() │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - initGriddedTiles: function(bounds) { │ │ │ │ - this.events.triggerEvent("retile"); │ │ │ │ - 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) │ │ │ │ + this.drawText(feature.id, style, location) │ │ │ │ } 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); │ │ │ │ - this.removeExcessTiles(rowidx, colidx); │ │ │ │ - var resolution = this.getServerResolution(); │ │ │ │ - this.gridResolution = resolution; │ │ │ │ - tileData.sort(function(a, b) { │ │ │ │ - return a.distance - b.distance │ │ │ │ - }); │ │ │ │ - for (var i = 0, ii = tileData.length; i < ii; ++i) { │ │ │ │ - tileData[i].tile.draw() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getMaxExtent: function() { │ │ │ │ - return this.maxExtent │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - addTileMonitoringHooks: function(tile) { │ │ │ │ - var replacingCls = "olTileReplacing"; │ │ │ │ - tile.onLoadStart = function() { │ │ │ │ - if (this.loading === false) { │ │ │ │ - this.loading = true; │ │ │ │ - this.events.triggerEvent("loadstart") │ │ │ │ - } │ │ │ │ - this.events.triggerEvent("tileloadstart", { │ │ │ │ - tile: tile │ │ │ │ - }); │ │ │ │ - this.numLoadingTiles++; │ │ │ │ - if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ - OpenLayers.Element.addClass(tile.getTile(), replacingCls) │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - tile.onLoadEnd = function(evt) { │ │ │ │ - this.numLoadingTiles--; │ │ │ │ - var aborted = evt.type === "unload"; │ │ │ │ - this.events.triggerEvent("tileloaded", { │ │ │ │ - tile: tile, │ │ │ │ - aborted: aborted │ │ │ │ - }); │ │ │ │ - if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ - var tileDiv = tile.getTile(); │ │ │ │ - if (OpenLayers.Element.getStyle(tileDiv, "display") === "none") { │ │ │ │ - var bufferTile = document.getElementById(tile.id + "_bb"); │ │ │ │ - if (bufferTile) { │ │ │ │ - bufferTile.parentNode.removeChild(bufferTile) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Element.removeClass(tileDiv, replacingCls) │ │ │ │ - } │ │ │ │ - if (this.numLoadingTiles === 0) { │ │ │ │ - if (this.backBuffer) { │ │ │ │ - if (this.backBuffer.childNodes.length === 0) { │ │ │ │ - this.removeBackBuffer() │ │ │ │ - } else { │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - 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 │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - removeTileMonitoringHooks: function(tile) { │ │ │ │ - tile.unload(); │ │ │ │ - tile.events.un({ │ │ │ │ - loadstart: tile.onLoadStart, │ │ │ │ - loadend: tile.onLoadEnd, │ │ │ │ - unload: tile.onLoadEnd, │ │ │ │ - loaderror: tile.onLoadError, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - removeExcessTiles: function(rows, columns) { │ │ │ │ - var i, l; │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - onMapResize: function() { │ │ │ │ - if (this.singleTile) { │ │ │ │ - this.clearGrid(); │ │ │ │ - this.setTileSize() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getTileBounds: function(viewPortPx) { │ │ │ │ - var maxExtent = this.maxExtent; │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var tileMapWidth = resolution * this.tileSize.w; │ │ │ │ - var tileMapHeight = resolution * this.tileSize.h; │ │ │ │ - var mapPoint = this.getLonLatFromViewPortPx(viewPortPx); │ │ │ │ - var tileLeft = maxExtent.left + tileMapWidth * Math.floor((mapPoint.lon - maxExtent.left) / tileMapWidth); │ │ │ │ - var tileBottom = maxExtent.bottom + tileMapHeight * Math.floor((mapPoint.lat - maxExtent.bottom) / tileMapHeight); │ │ │ │ - return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Grid" │ │ │ │ -}); │ │ │ │ -OpenLayers.TileManager = OpenLayers.Class({ │ │ │ │ - cacheSize: 256, │ │ │ │ - tilesPerFrame: 2, │ │ │ │ - frameDelay: 16, │ │ │ │ - moveDelay: 100, │ │ │ │ - zoomDelay: 200, │ │ │ │ - maps: null, │ │ │ │ - tileQueueId: null, │ │ │ │ - tileQueue: null, │ │ │ │ - tileCache: null, │ │ │ │ - tileCacheIndex: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.maps = []; │ │ │ │ - this.tileQueueId = {}; │ │ │ │ - this.tileQueue = {}; │ │ │ │ - this.tileCache = {}; │ │ │ │ - this.tileCacheIndex = [] │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - move: function(evt) { │ │ │ │ - this.updateTimeout(evt.object, this.moveDelay, true) │ │ │ │ - }, │ │ │ │ - zoomEnd: function(evt) { │ │ │ │ - this.updateTimeout(evt.object, this.zoomDelay) │ │ │ │ - }, │ │ │ │ - changeLayer: function(evt) { │ │ │ │ - if (evt.property === "visibility" || evt.property === "params") { │ │ │ │ - this.updateTimeout(evt.object, 0) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }) │ │ │ │ - } │ │ │ │ + this.removeText(feature.id) │ │ │ │ } │ │ │ │ + return rendered │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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 { │ │ │ │ - this.removeLayer({ │ │ │ │ - layer: evt.tile.layer │ │ │ │ - }) │ │ │ │ + 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 │ │ │ │ } │ │ │ │ }, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - 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") { │ │ │ │ - delete this.tileCache[url]; │ │ │ │ - OpenLayers.Util.removeItem(this.tileCacheIndex, url); │ │ │ │ - img = null │ │ │ │ - } │ │ │ │ - if (layer.url && (layer.async || !img)) { │ │ │ │ - var tileQueue = this.tileQueue[layer.map.id]; │ │ │ │ - if (!~OpenLayers.Util.indexOf(tileQueue, tile)) { │ │ │ │ - tileQueue.push(tile) │ │ │ │ - } │ │ │ │ - queued = true │ │ │ │ + drawGeometry: function(geometry, style, featureId) {}, │ │ │ │ + drawText: function(featureId, style, location) {}, │ │ │ │ + removeText: function(featureId) {}, │ │ │ │ + clear: function() {}, │ │ │ │ + getFeatureIdFromEvent: function(evt) {}, │ │ │ │ + eraseFeatures: function(features) { │ │ │ │ + if (!OpenLayers.Util.isArray(features)) { │ │ │ │ + features = [features] │ │ │ │ } │ │ │ │ - return !queued │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + var feature = features[i]; │ │ │ │ + this.eraseGeometry(feature.geometry, feature.id); │ │ │ │ + this.removeText(feature.id) │ │ │ │ } │ │ │ │ }, │ │ │ │ - manageTileCache: function(evt) { │ │ │ │ - var tile = evt.object; │ │ │ │ - var img = this.tileCache[tile.url]; │ │ │ │ - if (img) { │ │ │ │ - if (img.parentNode && OpenLayers.Element.hasClass(img.parentNode, "olBackBuffer")) { │ │ │ │ - img.parentNode.removeChild(img); │ │ │ │ - img.id = null │ │ │ │ - } │ │ │ │ - if (!img.parentNode) { │ │ │ │ - img.style.visibility = "hidden"; │ │ │ │ - img.style.opacity = 0; │ │ │ │ - tile.setImage(img); │ │ │ │ - OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url); │ │ │ │ - this.tileCacheIndex.push(tile.url) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + eraseGeometry: function(geometry, featureId) {}, │ │ │ │ + moveRoot: function(renderer) {}, │ │ │ │ + getRenderLayerId: function() { │ │ │ │ + return this.container.id │ │ │ │ }, │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ + applyDefaultSymbolizer: function(symbolizer) { │ │ │ │ + var result = OpenLayers.Util.extend({}, OpenLayers.Renderer.defaultSymbolizer); │ │ │ │ + if (symbolizer.stroke === false) { │ │ │ │ + delete result.strokeWidth; │ │ │ │ + delete result.strokeColor │ │ │ │ } │ │ │ │ - }, │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ + if (symbolizer.fill === false) { │ │ │ │ + delete result.fillColor │ │ │ │ } │ │ │ │ + OpenLayers.Util.extend(result, symbolizer); │ │ │ │ + return result │ │ │ │ }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer" │ │ │ │ }); │ │ │ │ -OpenLayers.Strategy = OpenLayers.Class({ │ │ │ │ - layer: null, │ │ │ │ +OpenLayers.Renderer.defaultSymbolizer = { │ │ │ │ + fillColor: "#000000", │ │ │ │ + strokeColor: "#000000", │ │ │ │ + strokeWidth: 2, │ │ │ │ + fillOpacity: 1, │ │ │ │ + strokeOpacity: 1, │ │ │ │ + pointRadius: 0, │ │ │ │ + labelAlign: "cm" │ │ │ │ +}; │ │ │ │ +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.Format = OpenLayers.Class({ │ │ │ │ options: null, │ │ │ │ - active: null, │ │ │ │ - autoActivate: true, │ │ │ │ - autoDestroy: true, │ │ │ │ + externalProjection: null, │ │ │ │ + internalProjection: null, │ │ │ │ + data: null, │ │ │ │ + keepData: false, │ │ │ │ initialize: function(options) { │ │ │ │ OpenLayers.Util.extend(this, options); │ │ │ │ - this.options = options; │ │ │ │ - this.active = false │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - this.layer = null; │ │ │ │ - this.options = null │ │ │ │ - }, │ │ │ │ - setLayer: function(layer) { │ │ │ │ - this.layer = layer │ │ │ │ + this.options = options │ │ │ │ }, │ │ │ │ - activate: function() { │ │ │ │ - if (!this.active) { │ │ │ │ - this.active = true; │ │ │ │ - return true │ │ │ │ - } │ │ │ │ - return false │ │ │ │ + destroy: function() {}, │ │ │ │ + read: function(data) { │ │ │ │ + throw new Error("Read not implemented.") │ │ │ │ }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.active = false; │ │ │ │ - return true │ │ │ │ - } │ │ │ │ - return false │ │ │ │ + write: function(object) { │ │ │ │ + throw new Error("Write not implemented.") │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Strategy" │ │ │ │ + CLASS_NAME: "OpenLayers.Format" │ │ │ │ }); │ │ │ │ OpenLayers.Handler = OpenLayers.Class({ │ │ │ │ id: null, │ │ │ │ control: null, │ │ │ │ map: null, │ │ │ │ keyMask: null, │ │ │ │ active: false, │ │ │ │ @@ -6488,14 +5854,130 @@ │ │ │ │ CLASS_NAME: "OpenLayers.Handler" │ │ │ │ }); │ │ │ │ OpenLayers.Handler.MOD_NONE = 0; │ │ │ │ OpenLayers.Handler.MOD_SHIFT = 1; │ │ │ │ OpenLayers.Handler.MOD_CTRL = 2; │ │ │ │ OpenLayers.Handler.MOD_ALT = 4; │ │ │ │ OpenLayers.Handler.MOD_META = 8; │ │ │ │ +OpenLayers.Control = OpenLayers.Class({ │ │ │ │ + id: null, │ │ │ │ + map: null, │ │ │ │ + div: null, │ │ │ │ + type: null, │ │ │ │ + allowSelection: false, │ │ │ │ + displayClass: "", │ │ │ │ + title: "", │ │ │ │ + autoActivate: false, │ │ │ │ + active: null, │ │ │ │ + handlerOptions: null, │ │ │ │ + handler: null, │ │ │ │ + eventListeners: null, │ │ │ │ + events: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + this.displayClass = this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, ""); │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.events = new OpenLayers.Events(this); │ │ │ │ + if (this.eventListeners instanceof Object) { │ │ │ │ + this.events.on(this.eventListeners) │ │ │ │ + } │ │ │ │ + if (this.id == null) { │ │ │ │ + this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_") │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + if (this.events) { │ │ │ │ + if (this.eventListeners) { │ │ │ │ + this.events.un(this.eventListeners) │ │ │ │ + } │ │ │ │ + this.events.destroy(); │ │ │ │ + this.events = null │ │ │ │ + } │ │ │ │ + this.eventListeners = null; │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.destroy(); │ │ │ │ + this.handler = null │ │ │ │ + } │ │ │ │ + if (this.handlers) { │ │ │ │ + for (var key in this.handlers) { │ │ │ │ + if (this.handlers.hasOwnProperty(key) && typeof this.handlers[key].destroy == "function") { │ │ │ │ + this.handlers[key].destroy() │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.handlers = null │ │ │ │ + } │ │ │ │ + if (this.map) { │ │ │ │ + this.map.removeControl(this); │ │ │ │ + this.map = null │ │ │ │ + } │ │ │ │ + this.div = null │ │ │ │ + }, │ │ │ │ + setMap: function(map) { │ │ │ │ + this.map = map; │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.setMap(map) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + draw: function(px) { │ │ │ │ + if (this.div == null) { │ │ │ │ + this.div = OpenLayers.Util.createDiv(this.id); │ │ │ │ + this.div.className = this.displayClass; │ │ │ │ + if (!this.allowSelection) { │ │ │ │ + this.div.className += " olControlNoSelect"; │ │ │ │ + this.div.setAttribute("unselectable", "on", 0); │ │ │ │ + this.div.onselectstart = OpenLayers.Function.False │ │ │ │ + } │ │ │ │ + if (this.title != "") { │ │ │ │ + this.div.title = this.title │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (px != null) { │ │ │ │ + this.position = px.clone() │ │ │ │ + } │ │ │ │ + this.moveTo(this.position); │ │ │ │ + return this.div │ │ │ │ + }, │ │ │ │ + moveTo: function(px) { │ │ │ │ + if (px != null && this.div != null) { │ │ │ │ + this.div.style.left = px.x + "px"; │ │ │ │ + this.div.style.top = px.y + "px" │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + activate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.activate() │ │ │ │ + } │ │ │ │ + this.active = true; │ │ │ │ + if (this.map) { │ │ │ │ + OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, "") + "Active") │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("activate"); │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + if (this.handler) { │ │ │ │ + this.handler.deactivate() │ │ │ │ + } │ │ │ │ + this.active = false; │ │ │ │ + if (this.map) { │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, "") + "Active") │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("deactivate"); │ │ │ │ + return true │ │ │ │ + } │ │ │ │ + return false │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Control" │ │ │ │ +}); │ │ │ │ +OpenLayers.Control.TYPE_BUTTON = 1; │ │ │ │ +OpenLayers.Control.TYPE_TOGGLE = 2; │ │ │ │ +OpenLayers.Control.TYPE_TOOL = 3; │ │ │ │ OpenLayers.Geometry = OpenLayers.Class({ │ │ │ │ id: null, │ │ │ │ parent: null, │ │ │ │ bounds: null, │ │ │ │ initialize: function() { │ │ │ │ this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_") │ │ │ │ }, │ │ │ │ @@ -6692,231 +6174,14 @@ │ │ │ │ return { │ │ │ │ distance: Math.pow(x - x0, 2) + Math.pow(y - y0, 2), │ │ │ │ x: x, │ │ │ │ y: y, │ │ │ │ along: along │ │ │ │ } │ │ │ │ }; │ │ │ │ -OpenLayers.Icon = OpenLayers.Class({ │ │ │ │ - url: null, │ │ │ │ - size: null, │ │ │ │ - offset: null, │ │ │ │ - calculateOffset: null, │ │ │ │ - imageDiv: null, │ │ │ │ - px: null, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.erase(); │ │ │ │ - OpenLayers.Event.stopObservingElement(this.imageDiv.firstChild); │ │ │ │ - this.imageDiv.innerHTML = ""; │ │ │ │ - this.imageDiv = null │ │ │ │ - }, │ │ │ │ - clone: function() { │ │ │ │ - return new OpenLayers.Icon(this.url, this.size, this.offset, this.calculateOffset) │ │ │ │ - }, │ │ │ │ - setSize: function(size) { │ │ │ │ - if (size != null) { │ │ │ │ - this.size = size │ │ │ │ - } │ │ │ │ - this.draw() │ │ │ │ - }, │ │ │ │ - setUrl: function(url) { │ │ │ │ - if (url != null) { │ │ │ │ - this.url = url │ │ │ │ - } │ │ │ │ - this.draw() │ │ │ │ - }, │ │ │ │ - draw: function(px) { │ │ │ │ - OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, this.size, this.url, "absolute"); │ │ │ │ - this.moveTo(px); │ │ │ │ - return this.imageDiv │ │ │ │ - }, │ │ │ │ - erase: function() { │ │ │ │ - if (this.imageDiv != null && this.imageDiv.parentNode != null) { │ │ │ │ - OpenLayers.Element.remove(this.imageDiv) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ - OpenLayers.Util.modifyAlphaImageDiv(this.imageDiv, null, null, null, null, null, null, null, opacity) │ │ │ │ - }, │ │ │ │ - moveTo: function(px) { │ │ │ │ - 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 │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - display: function(display) { │ │ │ │ - this.imageDiv.style.display = display ? "" : "none" │ │ │ │ - }, │ │ │ │ - isDrawn: function() { │ │ │ │ - var isDrawn = this.imageDiv && this.imageDiv.parentNode && this.imageDiv.parentNode.nodeType != 11; │ │ │ │ - return isDrawn │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Icon" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control = OpenLayers.Class({ │ │ │ │ - id: null, │ │ │ │ - map: null, │ │ │ │ - div: null, │ │ │ │ - type: null, │ │ │ │ - allowSelection: false, │ │ │ │ - displayClass: "", │ │ │ │ - title: "", │ │ │ │ - autoActivate: false, │ │ │ │ - active: null, │ │ │ │ - handlerOptions: null, │ │ │ │ - handler: null, │ │ │ │ - eventListeners: null, │ │ │ │ - events: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - this.displayClass = this.CLASS_NAME.replace("OpenLayers.", "ol").replace(/\./g, ""); │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.events = new OpenLayers.Events(this); │ │ │ │ - if (this.eventListeners instanceof Object) { │ │ │ │ - this.events.on(this.eventListeners) │ │ │ │ - } │ │ │ │ - if (this.id == null) { │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_") │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.events) { │ │ │ │ - if (this.eventListeners) { │ │ │ │ - this.events.un(this.eventListeners) │ │ │ │ - } │ │ │ │ - this.events.destroy(); │ │ │ │ - this.events = null │ │ │ │ - } │ │ │ │ - this.eventListeners = null; │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.destroy(); │ │ │ │ - this.handler = null │ │ │ │ - } │ │ │ │ - if (this.handlers) { │ │ │ │ - for (var key in this.handlers) { │ │ │ │ - if (this.handlers.hasOwnProperty(key) && typeof this.handlers[key].destroy == "function") { │ │ │ │ - this.handlers[key].destroy() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.handlers = null │ │ │ │ - } │ │ │ │ - if (this.map) { │ │ │ │ - this.map.removeControl(this); │ │ │ │ - this.map = null │ │ │ │ - } │ │ │ │ - this.div = null │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - this.map = map; │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.setMap(map) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - draw: function(px) { │ │ │ │ - if (this.div == null) { │ │ │ │ - this.div = OpenLayers.Util.createDiv(this.id); │ │ │ │ - this.div.className = this.displayClass; │ │ │ │ - if (!this.allowSelection) { │ │ │ │ - this.div.className += " olControlNoSelect"; │ │ │ │ - this.div.setAttribute("unselectable", "on", 0); │ │ │ │ - this.div.onselectstart = OpenLayers.Function.False │ │ │ │ - } │ │ │ │ - if (this.title != "") { │ │ │ │ - this.div.title = this.title │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (px != null) { │ │ │ │ - this.position = px.clone() │ │ │ │ - } │ │ │ │ - this.moveTo(this.position); │ │ │ │ - return this.div │ │ │ │ - }, │ │ │ │ - moveTo: function(px) { │ │ │ │ - if (px != null && this.div != null) { │ │ │ │ - this.div.style.left = px.x + "px"; │ │ │ │ - this.div.style.top = px.y + "px" │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.activate() │ │ │ │ - } │ │ │ │ - this.active = true; │ │ │ │ - if (this.map) { │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, "") + "Active") │ │ │ │ - } │ │ │ │ - this.events.triggerEvent("activate"); │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.deactivate() │ │ │ │ - } │ │ │ │ - this.active = false; │ │ │ │ - if (this.map) { │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass.replace(/ /g, "") + "Active") │ │ │ │ - } │ │ │ │ - this.events.triggerEvent("deactivate"); │ │ │ │ - return true │ │ │ │ - } │ │ │ │ - return false │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.TYPE_BUTTON = 1; │ │ │ │ -OpenLayers.Control.TYPE_TOGGLE = 2; │ │ │ │ -OpenLayers.Control.TYPE_TOOL = 3; │ │ │ │ -OpenLayers.Format = OpenLayers.Class({ │ │ │ │ - options: null, │ │ │ │ - externalProjection: null, │ │ │ │ - internalProjection: null, │ │ │ │ - data: null, │ │ │ │ - keepData: false, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.options = options │ │ │ │ - }, │ │ │ │ - destroy: function() {}, │ │ │ │ - read: function(data) { │ │ │ │ - throw new Error("Read not implemented.") │ │ │ │ - }, │ │ │ │ - write: function(object) { │ │ │ │ - throw new Error("Write not implemented.") │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Format" │ │ │ │ -}); │ │ │ │ OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, { │ │ │ │ x: null, │ │ │ │ y: null, │ │ │ │ initialize: function(x, y) { │ │ │ │ OpenLayers.Geometry.prototype.initialize.apply(this, arguments); │ │ │ │ this.x = parseFloat(x); │ │ │ │ this.y = parseFloat(y) │ │ │ │ @@ -9874,36 +9139,14 @@ │ │ │ │ throw "Unsupported WFST version: " + options.version │ │ │ │ } │ │ │ │ return new cls(options) │ │ │ │ }; │ │ │ │ OpenLayers.Format.WFST.DEFAULTS = { │ │ │ │ version: "1.0.0" │ │ │ │ }; │ │ │ │ -OpenLayers.Filter = OpenLayers.Class({ │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options) │ │ │ │ - }, │ │ │ │ - destroy: function() {}, │ │ │ │ - evaluate: function(context) { │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - clone: function() { │ │ │ │ - return null │ │ │ │ - }, │ │ │ │ - toString: function() { │ │ │ │ - var string; │ │ │ │ - if (OpenLayers.Format && OpenLayers.Format.CQL) { │ │ │ │ - string = OpenLayers.Format.CQL.prototype.write(this) │ │ │ │ - } else { │ │ │ │ - string = Object.prototype.toString.call(this) │ │ │ │ - } │ │ │ │ - return string │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Filter" │ │ │ │ -}); │ │ │ │ OpenLayers.Filter.Spatial = OpenLayers.Class(OpenLayers.Filter, { │ │ │ │ type: null, │ │ │ │ property: null, │ │ │ │ value: null, │ │ │ │ distance: null, │ │ │ │ distanceUnits: null, │ │ │ │ evaluate: function(feature) { │ │ │ │ @@ -13127,451 +12370,14 @@ │ │ │ │ process: null, │ │ │ │ output: null, │ │ │ │ initialize: function(options) { │ │ │ │ OpenLayers.Util.extend(this, options) │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.WPSProcess.ChainLink" │ │ │ │ }); │ │ │ │ -OpenLayers.Spherical = OpenLayers.Spherical || {}; │ │ │ │ -OpenLayers.Spherical.DEFAULT_RADIUS = 6378137; │ │ │ │ -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)) │ │ │ │ -}; │ │ │ │ -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.Protocol = OpenLayers.Class({ │ │ │ │ - format: null, │ │ │ │ - options: null, │ │ │ │ - autoDestroy: true, │ │ │ │ - defaultFilter: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - OpenLayers.Util.extend(this, options); │ │ │ │ - this.options = options │ │ │ │ - }, │ │ │ │ - mergeWithDefaultFilter: function(filter) { │ │ │ │ - var merged; │ │ │ │ - if (filter && this.defaultFilter) { │ │ │ │ - merged = new OpenLayers.Filter.Logical({ │ │ │ │ - type: OpenLayers.Filter.Logical.AND, │ │ │ │ - filters: [this.defaultFilter, filter] │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - merged = filter || this.defaultFilter || undefined │ │ │ │ - } │ │ │ │ - return merged │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.options = null; │ │ │ │ - this.format = null │ │ │ │ - }, │ │ │ │ - read: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - options.filter = this.mergeWithDefaultFilter(options.filter) │ │ │ │ - }, │ │ │ │ - create: function() {}, │ │ │ │ - update: function() {}, │ │ │ │ - delete: function() {}, │ │ │ │ - commit: function() {}, │ │ │ │ - abort: function(response) {}, │ │ │ │ - createCallback: function(method, response, options) { │ │ │ │ - return OpenLayers.Function.bind(function() { │ │ │ │ - method.apply(this, [response, options]) │ │ │ │ - }, this) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol" │ │ │ │ -}); │ │ │ │ -OpenLayers.Protocol.Response = OpenLayers.Class({ │ │ │ │ - code: null, │ │ │ │ - requestType: null, │ │ │ │ - last: true, │ │ │ │ - features: null, │ │ │ │ - data: null, │ │ │ │ - reqFeatures: null, │ │ │ │ - priv: null, │ │ │ │ - error: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options) │ │ │ │ - }, │ │ │ │ - success: function() { │ │ │ │ - return this.code > 0 │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Protocol.Response" │ │ │ │ -}); │ │ │ │ -OpenLayers.Protocol.Response.SUCCESS = 1; │ │ │ │ -OpenLayers.Protocol.Response.FAILURE = 0; │ │ │ │ -OpenLayers.Renderer = OpenLayers.Class({ │ │ │ │ - container: null, │ │ │ │ - root: null, │ │ │ │ - extent: null, │ │ │ │ - locked: false, │ │ │ │ - size: null, │ │ │ │ - resolution: null, │ │ │ │ - map: null, │ │ │ │ - featureDx: 0, │ │ │ │ - initialize: function(containerID, options) { │ │ │ │ - this.container = OpenLayers.Util.getElement(containerID); │ │ │ │ - OpenLayers.Util.extend(this, options) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.container = null; │ │ │ │ - this.extent = null; │ │ │ │ - this.size = null; │ │ │ │ - this.resolution = null; │ │ │ │ - this.map = null │ │ │ │ - }, │ │ │ │ - supported: function() { │ │ │ │ - return false │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - }, │ │ │ │ - setSize: function(size) { │ │ │ │ - this.size = size.clone(); │ │ │ │ - this.resolution = null │ │ │ │ - }, │ │ │ │ - getResolution: function() { │ │ │ │ - this.resolution = this.resolution || this.map.getResolution(); │ │ │ │ - return this.resolution │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - 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 │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - drawGeometry: function(geometry, style, featureId) {}, │ │ │ │ - drawText: function(featureId, style, location) {}, │ │ │ │ - removeText: function(featureId) {}, │ │ │ │ - clear: function() {}, │ │ │ │ - getFeatureIdFromEvent: function(evt) {}, │ │ │ │ - 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) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - eraseGeometry: function(geometry, featureId) {}, │ │ │ │ - moveRoot: function(renderer) {}, │ │ │ │ - getRenderLayerId: function() { │ │ │ │ - return this.container.id │ │ │ │ - }, │ │ │ │ - applyDefaultSymbolizer: function(symbolizer) { │ │ │ │ - var result = OpenLayers.Util.extend({}, OpenLayers.Renderer.defaultSymbolizer); │ │ │ │ - if (symbolizer.stroke === false) { │ │ │ │ - delete result.strokeWidth; │ │ │ │ - delete result.strokeColor │ │ │ │ - } │ │ │ │ - if (symbolizer.fill === false) { │ │ │ │ - delete result.fillColor │ │ │ │ - } │ │ │ │ - OpenLayers.Util.extend(result, symbolizer); │ │ │ │ - return result │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Renderer" │ │ │ │ -}); │ │ │ │ -OpenLayers.Renderer.defaultSymbolizer = { │ │ │ │ - fillColor: "#000000", │ │ │ │ - strokeColor: "#000000", │ │ │ │ - strokeWidth: 2, │ │ │ │ - fillOpacity: 1, │ │ │ │ - strokeOpacity: 1, │ │ │ │ - pointRadius: 0, │ │ │ │ - labelAlign: "cm" │ │ │ │ -}; │ │ │ │ -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 = OpenLayers.Class({ │ │ │ │ - zIndex: 0, │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Util.extend(this, config) │ │ │ │ - }, │ │ │ │ - clone: function() { │ │ │ │ - var Type = eval(this.CLASS_NAME); │ │ │ │ - return new Type(OpenLayers.Util.extend({}, this)) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer" │ │ │ │ -}); │ │ │ │ -OpenLayers.StyleMap = OpenLayers.Class({ │ │ │ │ - styles: null, │ │ │ │ - extendDefault: true, │ │ │ │ - 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"]) │ │ │ │ - }; │ │ │ │ - if (style instanceof OpenLayers.Style) { │ │ │ │ - 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) { │ │ │ │ - this.styles[key] = style[key] │ │ │ │ - } else if (typeof style[key] == "object") { │ │ │ │ - this.styles[key] = new OpenLayers.Style(style[key]) │ │ │ │ - } else { │ │ │ │ - this.styles["default"] = new OpenLayers.Style(style); │ │ │ │ - this.styles["select"] = new OpenLayers.Style(style); │ │ │ │ - this.styles["temporary"] = new OpenLayers.Style(style); │ │ │ │ - this.styles["delete"] = new OpenLayers.Style(style); │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - OpenLayers.Util.extend(this, options) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - for (var key in this.styles) { │ │ │ │ - this.styles[key].destroy() │ │ │ │ - } │ │ │ │ - this.styles = null │ │ │ │ - }, │ │ │ │ - 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)) │ │ │ │ - }, │ │ │ │ - addUniqueValueRules: function(renderIntent, property, symbolizers, context) { │ │ │ │ - var rules = []; │ │ │ │ - for (var value in symbolizers) { │ │ │ │ - rules.push(new OpenLayers.Rule({ │ │ │ │ - symbolizer: symbolizers[value], │ │ │ │ - context: context, │ │ │ │ - filter: new OpenLayers.Filter.Comparison({ │ │ │ │ - type: OpenLayers.Filter.Comparison.EQUAL_TO, │ │ │ │ - property: property, │ │ │ │ - value: value │ │ │ │ - }) │ │ │ │ - })) │ │ │ │ - } │ │ │ │ - this.styles[renderIntent].addRules(rules) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.StyleMap" │ │ │ │ -}); │ │ │ │ -OpenLayers.Marker = OpenLayers.Class({ │ │ │ │ - icon: null, │ │ │ │ - lonlat: null, │ │ │ │ - events: null, │ │ │ │ - map: null, │ │ │ │ - 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) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.erase(); │ │ │ │ - this.map = null; │ │ │ │ - this.events.destroy(); │ │ │ │ - this.events = null; │ │ │ │ - if (this.icon != null) { │ │ │ │ - this.icon.destroy(); │ │ │ │ - this.icon = null │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - draw: function(px) { │ │ │ │ - return this.icon.draw(px) │ │ │ │ - }, │ │ │ │ - erase: function() { │ │ │ │ - if (this.icon != null) { │ │ │ │ - this.icon.erase() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - moveTo: function(px) { │ │ │ │ - if (px != null && this.icon != null) { │ │ │ │ - this.icon.moveTo(px) │ │ │ │ - } │ │ │ │ - this.lonlat = this.map.getLonLatFromLayerPx(px) │ │ │ │ - }, │ │ │ │ - isDrawn: function() { │ │ │ │ - var isDrawn = this.icon && this.icon.isDrawn(); │ │ │ │ - return isDrawn │ │ │ │ - }, │ │ │ │ - onScreen: function() { │ │ │ │ - var onScreen = false; │ │ │ │ - if (this.map) { │ │ │ │ - var screenBounds = this.map.getExtent(); │ │ │ │ - onScreen = screenBounds.containsLonLat(this.lonlat) │ │ │ │ - } │ │ │ │ - return onScreen │ │ │ │ - }, │ │ │ │ - inflate: function(inflate) { │ │ │ │ - if (this.icon) { │ │ │ │ - this.icon.setSize({ │ │ │ │ - w: this.icon.size.w * inflate, │ │ │ │ - h: this.icon.size.h * inflate │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ - this.icon.setOpacity(opacity) │ │ │ │ - }, │ │ │ │ - setUrl: function(url) { │ │ │ │ - this.icon.setUrl(url) │ │ │ │ - }, │ │ │ │ - display: function(display) { │ │ │ │ - this.icon.display(display) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.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 = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Point" │ │ │ │ -}); │ │ │ │ -OpenLayers.Symbolizer.Line = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Line" │ │ │ │ -}); │ │ │ │ -OpenLayers.Symbolizer.Polygon = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Polygon" │ │ │ │ -}); │ │ │ │ -OpenLayers.Symbolizer.Text = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Text" │ │ │ │ -}); │ │ │ │ -OpenLayers.Symbolizer.Raster = OpenLayers.Class(OpenLayers.Symbolizer, { │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Symbolizer.prototype.initialize.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Symbolizer.Raster" │ │ │ │ -}); │ │ │ │ -OpenLayers.Style2 = OpenLayers.Class({ │ │ │ │ - id: null, │ │ │ │ - name: null, │ │ │ │ - title: null, │ │ │ │ - description: null, │ │ │ │ - layerName: null, │ │ │ │ - isDefault: false, │ │ │ │ - rules: null, │ │ │ │ - initialize: function(config) { │ │ │ │ - OpenLayers.Util.extend(this, config); │ │ │ │ - this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME + "_") │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - for (var i = 0, len = this.rules.length; i < len; i++) { │ │ │ │ - this.rules[i].destroy() │ │ │ │ - } │ │ │ │ - delete this.rules │ │ │ │ - }, │ │ │ │ - clone: function() { │ │ │ │ - var config = OpenLayers.Util.extend({}, this); │ │ │ │ - if (this.rules) { │ │ │ │ - config.rules = []; │ │ │ │ - for (var i = 0, len = this.rules.length; i < len; ++i) { │ │ │ │ - config.rules.push(this.rules[i].clone()) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return new OpenLayers.Style2(config) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Style2" │ │ │ │ -}); │ │ │ │ OpenLayers.Format.WPSDescribeProcess = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ VERSION: "1.0.0", │ │ │ │ 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" │ │ │ │ }, │ │ │ │ @@ -13756,14 +12562,98 @@ │ │ │ │ destroy: function() { │ │ │ │ this.events.destroy(); │ │ │ │ this.events = null; │ │ │ │ this.servers = null │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.WPSClient" │ │ │ │ }); │ │ │ │ +OpenLayers.Kinetic = OpenLayers.Class({ │ │ │ │ + threshold: 0, │ │ │ │ + deceleration: .0035, │ │ │ │ + nbPoints: 100, │ │ │ │ + delay: 200, │ │ │ │ + points: undefined, │ │ │ │ + timerId: undefined, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options) │ │ │ │ + }, │ │ │ │ + begin: function() { │ │ │ │ + OpenLayers.Animation.stop(this.timerId); │ │ │ │ + this.timerId = undefined; │ │ │ │ + this.points = [] │ │ │ │ + }, │ │ │ │ + update: function(xy) { │ │ │ │ + this.points.unshift({ │ │ │ │ + xy: xy, │ │ │ │ + tick: (new Date).getTime() │ │ │ │ + }); │ │ │ │ + if (this.points.length > this.nbPoints) { │ │ │ │ + this.points.pop() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + end: function(xy) { │ │ │ │ + var last, now = (new Date).getTime(); │ │ │ │ + for (var i = 0, l = this.points.length, point; i < l; i++) { │ │ │ │ + point = this.points[i]; │ │ │ │ + if (now - point.tick > this.delay) { │ │ │ │ + break │ │ │ │ + } │ │ │ │ + last = point │ │ │ │ + } │ │ │ │ + if (!last) { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + var time = (new Date).getTime() - last.tick; │ │ │ │ + var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) + Math.pow(xy.y - last.xy.y, 2)); │ │ │ │ + var speed = dist / time; │ │ │ │ + if (speed == 0 || speed < this.threshold) { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + var theta = Math.asin((xy.y - last.xy.y) / dist); │ │ │ │ + if (last.xy.x <= xy.x) { │ │ │ │ + theta = Math.PI - theta │ │ │ │ + } │ │ │ │ + return { │ │ │ │ + speed: speed, │ │ │ │ + theta: theta │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + move: function(info, callback) { │ │ │ │ + var v0 = info.speed; │ │ │ │ + var fx = Math.cos(info.theta); │ │ │ │ + var fy = -Math.sin(info.theta); │ │ │ │ + var initialTime = (new Date).getTime(); │ │ │ │ + var lastX = 0; │ │ │ │ + var lastY = 0; │ │ │ │ + var timerCallback = function() { │ │ │ │ + if (this.timerId == null) { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + var t = (new Date).getTime() - initialTime; │ │ │ │ + var p = -this.deceleration * Math.pow(t, 2) / 2 + 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.Popup = OpenLayers.Class({ │ │ │ │ events: null, │ │ │ │ id: "", │ │ │ │ lonlat: null, │ │ │ │ div: null, │ │ │ │ contentSize: null, │ │ │ │ size: null, │ │ │ │ @@ -14195,2008 +13085,2168 @@ │ │ │ │ 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.Kinetic = OpenLayers.Class({ │ │ │ │ - threshold: 0, │ │ │ │ - deceleration: .0035, │ │ │ │ - nbPoints: 100, │ │ │ │ - delay: 200, │ │ │ │ - points: undefined, │ │ │ │ - timerId: undefined, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Util.extend(this, options) │ │ │ │ +OpenLayers.Layer.HTTPRequest = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ + URL_HASH_FACTOR: (Math.sqrt(5) - 1) / 2, │ │ │ │ + url: null, │ │ │ │ + params: null, │ │ │ │ + reproject: false, │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ }, │ │ │ │ - begin: function() { │ │ │ │ - OpenLayers.Animation.stop(this.timerId); │ │ │ │ - this.timerId = undefined; │ │ │ │ - this.points = [] │ │ │ │ + destroy: function() { │ │ │ │ + this.url = null; │ │ │ │ + this.params = null; │ │ │ │ + OpenLayers.Layer.prototype.destroy.apply(this, arguments) │ │ │ │ }, │ │ │ │ - update: function(xy) { │ │ │ │ - this.points.unshift({ │ │ │ │ - xy: xy, │ │ │ │ - tick: (new Date).getTime() │ │ │ │ - }); │ │ │ │ - if (this.points.length > this.nbPoints) { │ │ │ │ - this.points.pop() │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.HTTPRequest(this.name, this.url, this.params, this.getOptions()) │ │ │ │ } │ │ │ │ + obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ + return obj │ │ │ │ }, │ │ │ │ - end: function(xy) { │ │ │ │ - var last, now = (new Date).getTime(); │ │ │ │ - for (var i = 0, l = this.points.length, point; i < l; i++) { │ │ │ │ - point = this.points[i]; │ │ │ │ - if (now - point.tick > this.delay) { │ │ │ │ - break │ │ │ │ - } │ │ │ │ - last = point │ │ │ │ - } │ │ │ │ - if (!last) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var time = (new Date).getTime() - last.tick; │ │ │ │ - var dist = Math.sqrt(Math.pow(xy.x - last.xy.x, 2) + Math.pow(xy.y - last.xy.y, 2)); │ │ │ │ - var speed = dist / time; │ │ │ │ - if (speed == 0 || speed < this.threshold) { │ │ │ │ - return │ │ │ │ + setUrl: function(newUrl) { │ │ │ │ + this.url = newUrl │ │ │ │ + }, │ │ │ │ + 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" │ │ │ │ + }) │ │ │ │ } │ │ │ │ - var theta = Math.asin((xy.y - last.xy.y) / dist); │ │ │ │ - if (last.xy.x <= xy.x) { │ │ │ │ - theta = Math.PI - theta │ │ │ │ + return ret │ │ │ │ + }, │ │ │ │ + redraw: function(force) { │ │ │ │ + if (force) { │ │ │ │ + return this.mergeNewParams({ │ │ │ │ + _olSalt: Math.random() │ │ │ │ + }) │ │ │ │ + } else { │ │ │ │ + return OpenLayers.Layer.prototype.redraw.apply(this, []) │ │ │ │ } │ │ │ │ - return { │ │ │ │ - speed: speed, │ │ │ │ - theta: theta │ │ │ │ + }, │ │ │ │ + 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)] │ │ │ │ }, │ │ │ │ - move: function(info, callback) { │ │ │ │ - var v0 = info.speed; │ │ │ │ - var fx = Math.cos(info.theta); │ │ │ │ - var fy = -Math.sin(info.theta); │ │ │ │ - var initialTime = (new Date).getTime(); │ │ │ │ - var lastX = 0; │ │ │ │ - var lastY = 0; │ │ │ │ - var timerCallback = function() { │ │ │ │ - if (this.timerId == null) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var t = (new Date).getTime() - initialTime; │ │ │ │ - var p = -this.deceleration * Math.pow(t, 2) / 2 + 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 │ │ │ │ + getFullRequestString: function(newParams, altUrl) { │ │ │ │ + var url = altUrl || this.url; │ │ │ │ + var allParams = OpenLayers.Util.extend({}, this.params); │ │ │ │ + allParams = OpenLayers.Util.extend(allParams, newParams); │ │ │ │ + var paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ + if (OpenLayers.Util.isArray(url)) { │ │ │ │ + url = this.selectUrl(paramsString, url) │ │ │ │ + } │ │ │ │ + var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url)); │ │ │ │ + for (var key in allParams) { │ │ │ │ + if (key.toUpperCase() in urlParams) { │ │ │ │ + delete allParams[key] │ │ │ │ } │ │ │ │ - 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)) │ │ │ │ + } │ │ │ │ + paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ + return OpenLayers.Util.urlAppend(url, paramsString) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Kinetic" │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.HTTPRequest" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.Markers = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ - isBaseLayer: false, │ │ │ │ - markers: null, │ │ │ │ - drawn: false, │ │ │ │ - initialize: function(name, options) { │ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ - this.markers = [] │ │ │ │ +OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { │ │ │ │ + url: null, │ │ │ │ + imgDiv: null, │ │ │ │ + frame: null, │ │ │ │ + imageReloadAttempts: null, │ │ │ │ + layerAlphaHack: null, │ │ │ │ + asyncRequestId: null, │ │ │ │ + maxGetUrlLength: null, │ │ │ │ + canvasContext: null, │ │ │ │ + crossOriginKeyword: null, │ │ │ │ + initialize: function(layer, position, bounds, url, size, options) { │ │ │ │ + OpenLayers.Tile.prototype.initialize.apply(this, arguments); │ │ │ │ + this.url = url; │ │ │ │ + this.layerAlphaHack = this.layer.alpha && OpenLayers.Util.alphaHack(); │ │ │ │ + if (this.maxGetUrlLength != null || this.layer.gutter || this.layerAlphaHack) { │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ }, │ │ │ │ destroy: function() { │ │ │ │ - this.clearMarkers(); │ │ │ │ - this.markers = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ - if (opacity != this.opacity) { │ │ │ │ - this.opacity = opacity; │ │ │ │ - for (var i = 0, len = this.markers.length; i < len; i++) { │ │ │ │ - this.markers[i].setOpacity(this.opacity) │ │ │ │ - } │ │ │ │ + if (this.imgDiv) { │ │ │ │ + this.clear(); │ │ │ │ + this.imgDiv = null; │ │ │ │ + this.frame = null │ │ │ │ } │ │ │ │ + this.asyncRequestId = null; │ │ │ │ + OpenLayers.Tile.prototype.destroy.apply(this, arguments) │ │ │ │ }, │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ - if (zoomChanged || !this.drawn) { │ │ │ │ - for (var i = 0, len = this.markers.length; i < len; i++) { │ │ │ │ - this.drawMarker(this.markers[i]) │ │ │ │ + draw: function() { │ │ │ │ + var shouldDraw = OpenLayers.Tile.prototype.draw.apply(this, arguments); │ │ │ │ + if (shouldDraw) { │ │ │ │ + if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { │ │ │ │ + this.bounds = this.getBoundsFromBaseLayer(this.position) │ │ │ │ } │ │ │ │ - this.drawn = true │ │ │ │ + if (this.isLoading) { │ │ │ │ + this._loadEvent = "reload" │ │ │ │ + } else { │ │ │ │ + this.isLoading = true; │ │ │ │ + this._loadEvent = "loadstart" │ │ │ │ + } │ │ │ │ + this.renderTile(); │ │ │ │ + this.positionTile() │ │ │ │ + } else if (shouldDraw === false) { │ │ │ │ + this.unload() │ │ │ │ } │ │ │ │ + return shouldDraw │ │ │ │ }, │ │ │ │ - addMarker: function(marker) { │ │ │ │ - this.markers.push(marker); │ │ │ │ - if (this.opacity < 1) { │ │ │ │ - marker.setOpacity(this.opacity) │ │ │ │ - } │ │ │ │ - if (this.map && this.map.getExtent()) { │ │ │ │ - marker.map = this.map; │ │ │ │ - this.drawMarker(marker) │ │ │ │ + renderTile: function() { │ │ │ │ + if (this.layer.async) { │ │ │ │ + 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 { │ │ │ │ + this.url = this.layer.getURL(this.bounds); │ │ │ │ + this.initImage() │ │ │ │ } │ │ │ │ }, │ │ │ │ - removeMarker: function(marker) { │ │ │ │ - if (this.markers && this.markers.length) { │ │ │ │ - OpenLayers.Util.removeItem(this.markers, marker); │ │ │ │ - marker.erase() │ │ │ │ + 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" │ │ │ │ }, │ │ │ │ - clearMarkers: function() { │ │ │ │ - if (this.markers != null) { │ │ │ │ - while (this.markers.length > 0) { │ │ │ │ - this.removeMarker(this.markers[0]) │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - drawMarker: function(marker) { │ │ │ │ - var px = this.map.getLayerPxFromLonLat(marker.lonlat); │ │ │ │ - if (px == null) { │ │ │ │ - marker.display(false) │ │ │ │ - } else { │ │ │ │ - if (!marker.isDrawn()) { │ │ │ │ - var markerImg = marker.draw(px); │ │ │ │ - this.div.appendChild(markerImg) │ │ │ │ - } else if (marker.icon) { │ │ │ │ - marker.icon.moveTo(px) │ │ │ │ + this.setImgSrc(); │ │ │ │ + if (this.layerAlphaHack === true) { │ │ │ │ + img.style.filter = "" │ │ │ │ } │ │ │ │ + OpenLayers.Element.removeClass(img, "olImageLoadError") │ │ │ │ } │ │ │ │ + this.canvasContext = null │ │ │ │ }, │ │ │ │ - getDataExtent: function() { │ │ │ │ - var maxExtent = null; │ │ │ │ - if (this.markers && this.markers.length > 0) { │ │ │ │ - var maxExtent = new OpenLayers.Bounds; │ │ │ │ - for (var i = 0, len = this.markers.length; i < len; i++) { │ │ │ │ - var marker = this.markers[i]; │ │ │ │ - maxExtent.extend(marker.lonlat) │ │ │ │ + getImage: function() { │ │ │ │ + if (!this.imgDiv) { │ │ │ │ + this.imgDiv = OpenLayers.Tile.Image.IMAGE.cloneNode(false); │ │ │ │ + var style = this.imgDiv.style; │ │ │ │ + if (this.frame) { │ │ │ │ + var left = 0, │ │ │ │ + top = 0; │ │ │ │ + if (this.layer.gutter) { │ │ │ │ + left = this.layer.gutter / this.layer.tileSize.w * 100; │ │ │ │ + top = this.layer.gutter / this.layer.tileSize.h * 100 │ │ │ │ + } │ │ │ │ + style.left = -left + "%"; │ │ │ │ + style.top = -top + "%"; │ │ │ │ + style.width = 2 * left + 100 + "%"; │ │ │ │ + style.height = 2 * top + 100 + "%" │ │ │ │ + } │ │ │ │ + style.visibility = "hidden"; │ │ │ │ + style.opacity = 0; │ │ │ │ + if (this.layer.opacity < 1) { │ │ │ │ + style.filter = "alpha(opacity=" + this.layer.opacity * 100 + ")" │ │ │ │ + } │ │ │ │ + style.position = "absolute"; │ │ │ │ + if (this.layerAlphaHack) { │ │ │ │ + style.paddingTop = style.height; │ │ │ │ + style.height = "0"; │ │ │ │ + style.width = "100%" │ │ │ │ + } │ │ │ │ + if (this.frame) { │ │ │ │ + this.frame.appendChild(this.imgDiv) │ │ │ │ } │ │ │ │ } │ │ │ │ - return maxExtent │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Markers" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.GeoRSS = OpenLayers.Class(OpenLayers.Layer.Markers, { │ │ │ │ - location: null, │ │ │ │ - features: null, │ │ │ │ - formatOptions: null, │ │ │ │ - selectedFeature: null, │ │ │ │ - icon: null, │ │ │ │ - popupSize: null, │ │ │ │ - useFeedTitle: true, │ │ │ │ - initialize: function(name, location, options) { │ │ │ │ - OpenLayers.Layer.Markers.prototype.initialize.apply(this, [name, options]); │ │ │ │ - this.location = location; │ │ │ │ - this.features = [] │ │ │ │ + return this.imgDiv │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments); │ │ │ │ - this.clearFeatures(); │ │ │ │ - this.features = null │ │ │ │ + setImage: function(img) { │ │ │ │ + this.imgDiv = img │ │ │ │ }, │ │ │ │ - loadRSS: function() { │ │ │ │ - if (!this.loaded) { │ │ │ │ - this.events.triggerEvent("loadstart"); │ │ │ │ - OpenLayers.Request.GET({ │ │ │ │ - url: this.location, │ │ │ │ - success: this.parseData, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.loaded = true │ │ │ │ + initImage: function() { │ │ │ │ + if (!this.url && !this.imgDiv) { │ │ │ │ + this.isLoading = false; │ │ │ │ + return │ │ │ │ } │ │ │ │ - }, │ │ │ │ - moveTo: function(bounds, zoomChanged, minor) { │ │ │ │ - OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments); │ │ │ │ - if (this.visibility && !this.loaded) { │ │ │ │ - this.loadRSS() │ │ │ │ + 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) │ │ │ │ } │ │ │ │ }, │ │ │ │ - parseData: function(ajaxRequest) { │ │ │ │ - var doc = ajaxRequest.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = OpenLayers.Format.XML.prototype.read(ajaxRequest.responseText) │ │ │ │ - } │ │ │ │ - if (this.useFeedTitle) { │ │ │ │ - var name = null; │ │ │ │ - try { │ │ │ │ - name = doc.getElementsByTagNameNS("*", "title")[0].firstChild.nodeValue │ │ │ │ - } catch (e) { │ │ │ │ - try { │ │ │ │ - name = doc.getElementsByTagName("title")[0].firstChild.nodeValue │ │ │ │ - } catch (e) {} │ │ │ │ + setImgSrc: function(url) { │ │ │ │ + var img = this.imgDiv; │ │ │ │ + if (url) { │ │ │ │ + img.style.visibility = "hidden"; │ │ │ │ + img.style.opacity = 0; │ │ │ │ + if (this.crossOriginKeyword) { │ │ │ │ + if (url.substr(0, 5) !== "data:") { │ │ │ │ + img.setAttribute("crossorigin", this.crossOriginKeyword) │ │ │ │ + } else { │ │ │ │ + img.removeAttribute("crossorigin") │ │ │ │ + } │ │ │ │ } │ │ │ │ - if (name) { │ │ │ │ - this.setName(name) │ │ │ │ + img.src = url │ │ │ │ + } else { │ │ │ │ + this.stopLoading(); │ │ │ │ + this.imgDiv = null; │ │ │ │ + if (img.parentNode) { │ │ │ │ + img.parentNode.removeChild(img) │ │ │ │ } │ │ │ │ } │ │ │ │ - var options = {}; │ │ │ │ - OpenLayers.Util.extend(options, this.formatOptions); │ │ │ │ - if (this.map && !this.projection.equals(this.map.getProjectionObject())) { │ │ │ │ - options.externalProjection = this.projection; │ │ │ │ - options.internalProjection = this.map.getProjectionObject() │ │ │ │ + }, │ │ │ │ + getTile: function() { │ │ │ │ + return this.frame ? this.frame : this.getImage() │ │ │ │ + }, │ │ │ │ + createBackBuffer: function() { │ │ │ │ + if (!this.imgDiv || this.isLoading) { │ │ │ │ + return │ │ │ │ } │ │ │ │ - var format = new OpenLayers.Format.GeoRSS(options); │ │ │ │ - var features = format.read(doc); │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - var data = {}; │ │ │ │ - var feature = features[i]; │ │ │ │ - if (!feature.geometry) { │ │ │ │ - continue │ │ │ │ - } │ │ │ │ - var title = feature.attributes.title ? feature.attributes.title : "Untitled"; │ │ │ │ - var description = feature.attributes.description ? feature.attributes.description : "No description."; │ │ │ │ - var link = feature.attributes.link ? feature.attributes.link : ""; │ │ │ │ - var location = feature.geometry.getBounds().getCenterLonLat(); │ │ │ │ - data.icon = this.icon == null ? OpenLayers.Marker.defaultIcon() : this.icon.clone(); │ │ │ │ - data.popupSize = this.popupSize ? this.popupSize.clone() : new OpenLayers.Size(250, 120); │ │ │ │ - if (title || description) { │ │ │ │ - data.title = title; │ │ │ │ - data.description = description; │ │ │ │ - var contentHTML = '<div class="olLayerGeoRSSClose">[x]</div>'; │ │ │ │ - contentHTML += '<div class="olLayerGeoRSSTitle">'; │ │ │ │ - if (link) { │ │ │ │ - contentHTML += '<a class="link" href="' + link + '" target="_blank">' │ │ │ │ - } │ │ │ │ - contentHTML += title; │ │ │ │ - if (link) { │ │ │ │ - contentHTML += "</a>" │ │ │ │ - } │ │ │ │ - contentHTML += "</div>"; │ │ │ │ - contentHTML += '<div style="" class="olLayerGeoRSSDescription">'; │ │ │ │ - contentHTML += description; │ │ │ │ - contentHTML += "</div>"; │ │ │ │ - data["popupContentHTML"] = contentHTML │ │ │ │ - } │ │ │ │ - var feature = new OpenLayers.Feature(this, location, data); │ │ │ │ - this.features.push(feature); │ │ │ │ - var marker = feature.createMarker(); │ │ │ │ - marker.events.register("click", feature, this.markerClick); │ │ │ │ - this.addMarker(marker) │ │ │ │ + var backBuffer; │ │ │ │ + if (this.frame) { │ │ │ │ + backBuffer = this.frame.cloneNode(false); │ │ │ │ + backBuffer.appendChild(this.imgDiv) │ │ │ │ + } else { │ │ │ │ + backBuffer = this.imgDiv │ │ │ │ } │ │ │ │ - this.events.triggerEvent("loadend") │ │ │ │ + this.imgDiv = null; │ │ │ │ + return backBuffer │ │ │ │ }, │ │ │ │ - markerClick: function(evt) { │ │ │ │ - var sameMarkerClicked = this == this.layer.selectedFeature; │ │ │ │ - this.layer.selectedFeature = !sameMarkerClicked ? this : null; │ │ │ │ - for (var i = 0, len = this.layer.map.popups.length; i < len; i++) { │ │ │ │ - this.layer.map.removePopup(this.layer.map.popups[i]) │ │ │ │ + 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')" │ │ │ │ } │ │ │ │ - if (!sameMarkerClicked) { │ │ │ │ - var popup = this.createPopup(); │ │ │ │ - OpenLayers.Event.observe(popup.div, "click", OpenLayers.Function.bind(function() { │ │ │ │ - for (var i = 0, len = this.layer.map.popups.length; i < len; i++) { │ │ │ │ - this.layer.map.removePopup(this.layer.map.popups[i]) │ │ │ │ - } │ │ │ │ - }, this)); │ │ │ │ - this.layer.map.addPopup(popup) │ │ │ │ + }, │ │ │ │ + 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() │ │ │ │ + } │ │ │ │ } │ │ │ │ - OpenLayers.Event.stop(evt) │ │ │ │ }, │ │ │ │ - clearFeatures: function() { │ │ │ │ - if (this.features != null) { │ │ │ │ - while (this.features.length > 0) { │ │ │ │ - var feature = this.features[0]; │ │ │ │ - OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ - feature.destroy() │ │ │ │ + stopLoading: function() { │ │ │ │ + OpenLayers.Event.stopObservingElement(this.imgDiv); │ │ │ │ + window.clearTimeout(this._loadTimeout); │ │ │ │ + delete this._loadTimeout │ │ │ │ + }, │ │ │ │ + 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.Layer.GeoRSS" │ │ │ │ + CLASS_NAME: "OpenLayers.Tile.Image" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - isBaseLayer: true, │ │ │ │ - sphericalMercator: false, │ │ │ │ - zoomOffset: 0, │ │ │ │ +OpenLayers.Tile.Image.IMAGE = function() { │ │ │ │ + var img = new Image; │ │ │ │ + img.className = "olTileImage"; │ │ │ │ + img.galleryImg = "no"; │ │ │ │ + return img │ │ │ │ +}(); │ │ │ │ +OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { │ │ │ │ + tileSize: null, │ │ │ │ + tileOriginCorner: "bl", │ │ │ │ + tileOrigin: null, │ │ │ │ + tileOptions: null, │ │ │ │ + tileClass: OpenLayers.Tile.Image, │ │ │ │ + grid: null, │ │ │ │ + singleTile: false, │ │ │ │ + ratio: 1.5, │ │ │ │ + buffer: 0, │ │ │ │ + transitionEffect: "resize", │ │ │ │ + numLoadingTiles: 0, │ │ │ │ serverResolutions: null, │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - if (options && options.sphericalMercator || this.sphericalMercator) { │ │ │ │ - options = OpenLayers.Util.extend({ │ │ │ │ - projection: "EPSG:900913", │ │ │ │ - numZoomLevels: 19 │ │ │ │ - }, options) │ │ │ │ + loading: false, │ │ │ │ + backBuffer: null, │ │ │ │ + gridResolution: null, │ │ │ │ + backBufferResolution: null, │ │ │ │ + backBufferLonLat: null, │ │ │ │ + backBufferTimerId: null, │ │ │ │ + removeBackBufferDelay: null, │ │ │ │ + className: null, │ │ │ │ + gridLayout: null, │ │ │ │ + rowSign: null, │ │ │ │ + transitionendEvents: ["transitionend", "webkitTransitionEnd", "otransitionend", "oTransitionEnd"], │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + initProperties: function() { │ │ │ │ + if (this.options.removeBackBufferDelay === undefined) { │ │ │ │ + this.removeBackBufferDelay = this.singleTile ? 0 : 2500 │ │ │ │ + } │ │ │ │ + if (this.options.className === undefined) { │ │ │ │ + this.className = this.singleTile ? "olLayerGridSingleTile" : "olLayerGrid" │ │ │ │ } │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name || this.name, url || this.url, {}, options]) │ │ │ │ }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.XYZ(this.name, this.url, this.getOptions()) │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.setMap.call(this, map); │ │ │ │ + OpenLayers.Element.addClass(this.div, this.className) │ │ │ │ + }, │ │ │ │ + removeMap: function(map) { │ │ │ │ + this.removeBackBuffer() │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.removeBackBuffer(); │ │ │ │ + this.clearGrid(); │ │ │ │ + this.grid = null; │ │ │ │ + this.tileSize = null; │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - var xyz = this.getXYZ(bounds); │ │ │ │ - var url = this.url; │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - var s = "" + xyz.x + xyz.y + xyz.z; │ │ │ │ - url = this.selectUrl(s, url) │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - return OpenLayers.String.format(url, xyz) │ │ │ │ }, │ │ │ │ - getXYZ: function(bounds) { │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w)); │ │ │ │ - var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h)); │ │ │ │ - var z = this.getServerZoom(); │ │ │ │ - if (this.wrapDateLine) { │ │ │ │ - var limit = Math.pow(2, z); │ │ │ │ - x = (x % limit + limit) % limit │ │ │ │ + clone: function(obj) { │ │ │ │ + if (obj == null) { │ │ │ │ + obj = new OpenLayers.Layer.Grid(this.name, this.url, this.params, this.getOptions()) │ │ │ │ } │ │ │ │ - return { │ │ │ │ - x: x, │ │ │ │ - y: y, │ │ │ │ - z: z │ │ │ │ + obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]); │ │ │ │ + if (this.tileSize != null) { │ │ │ │ + obj.tileSize = this.tileSize.clone() │ │ │ │ } │ │ │ │ + obj.grid = []; │ │ │ │ + obj.gridResolution = null; │ │ │ │ + obj.backBuffer = null; │ │ │ │ + obj.backBufferTimerId = null; │ │ │ │ + obj.loading = false; │ │ │ │ + obj.numLoadingTiles = 0; │ │ │ │ + return obj │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ - if (!this.tileOrigin) { │ │ │ │ - this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.bottom) │ │ │ │ + moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments); │ │ │ │ + bounds = bounds || this.map.getExtent(); │ │ │ │ + if (bounds != null) { │ │ │ │ + var forceReTile = !this.grid.length || zoomChanged; │ │ │ │ + var tilesBounds = this.getTilesBounds(); │ │ │ │ + var resolution = this.map.getResolution(); │ │ │ │ + var serverResolution = this.getServerResolution(resolution); │ │ │ │ + if (this.singleTile) { │ │ │ │ + if (forceReTile || !dragging && !tilesBounds.containsBounds(bounds)) { │ │ │ │ + if (zoomChanged && this.transitionEffect !== "resize") { │ │ │ │ + this.removeBackBuffer() │ │ │ │ + } │ │ │ │ + if (!zoomChanged || this.transitionEffect === "resize") { │ │ │ │ + this.applyBackBuffer(resolution) │ │ │ │ + } │ │ │ │ + this.initSingleTile(bounds) │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + 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() │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.XYZ" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.ArcGISCache = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ - url: null, │ │ │ │ - tileOrigin: null, │ │ │ │ - tileSize: new OpenLayers.Size(256, 256), │ │ │ │ - useArcGISServer: true, │ │ │ │ - type: "png", │ │ │ │ - useScales: false, │ │ │ │ - overrideDPI: false, │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); │ │ │ │ - if (this.resolutions) { │ │ │ │ - this.serverResolutions = this.resolutions; │ │ │ │ - this.maxExtent = this.getMaxExtentForResolution(this.resolutions[0]) │ │ │ │ - } │ │ │ │ - if (this.layerInfo) { │ │ │ │ - var info = this.layerInfo; │ │ │ │ - var startingTileExtent = new OpenLayers.Bounds(info.fullExtent.xmin, info.fullExtent.ymin, info.fullExtent.xmax, info.fullExtent.ymax); │ │ │ │ - this.projection = "EPSG:" + info.spatialReference.wkid; │ │ │ │ - this.sphericalMercator = info.spatialReference.wkid == 102100; │ │ │ │ - this.units = info.units == "esriFeet" ? "ft" : "m"; │ │ │ │ - if (!!info.tileInfo) { │ │ │ │ - this.tileSize = new OpenLayers.Size(info.tileInfo.width || info.tileInfo.cols, info.tileInfo.height || info.tileInfo.rows); │ │ │ │ - this.tileOrigin = new OpenLayers.LonLat(info.tileInfo.origin.x, info.tileInfo.origin.y); │ │ │ │ - var upperLeft = new OpenLayers.Geometry.Point(startingTileExtent.left, startingTileExtent.top); │ │ │ │ - var bottomRight = new OpenLayers.Geometry.Point(startingTileExtent.right, startingTileExtent.bottom); │ │ │ │ - if (this.useScales) { │ │ │ │ - this.scales = [] │ │ │ │ - } else { │ │ │ │ - this.resolutions = [] │ │ │ │ + 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) { │ │ │ │ + if (this.map.baseLayer.wrapDateLine) { │ │ │ │ + var worldWidth = this.map.getMaxExtent().getWidth(); │ │ │ │ + var worldsAway = Math.ceil((left - x) / worldWidth); │ │ │ │ + x += worldWidth * worldsAway │ │ │ │ } │ │ │ │ - this.lods = []; │ │ │ │ - for (var key in info.tileInfo.lods) { │ │ │ │ - if (info.tileInfo.lods.hasOwnProperty(key)) { │ │ │ │ - var lod = info.tileInfo.lods[key]; │ │ │ │ - if (this.useScales) { │ │ │ │ - this.scales.push(lod.scale) │ │ │ │ - } else { │ │ │ │ - this.resolutions.push(lod.resolution) │ │ │ │ - } │ │ │ │ - var start = this.getContainingTileCoords(upperLeft, lod.resolution); │ │ │ │ - lod.startTileCol = start.x; │ │ │ │ - lod.startTileRow = start.y; │ │ │ │ - var end = this.getContainingTileCoords(bottomRight, lod.resolution); │ │ │ │ - lod.endTileCol = end.x; │ │ │ │ - lod.endTileRow = end.y; │ │ │ │ - this.lods.push(lod) │ │ │ │ + } │ │ │ │ + var dtx = (x - left) / (res * tileWidth); │ │ │ │ + var dty = (top - y) / (res * tileHeight); │ │ │ │ + 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, │ │ │ │ + i: Math.floor((dtx - col) * tileWidth), │ │ │ │ + j: Math.floor((dty - row) * tileHeight) │ │ │ │ } │ │ │ │ } │ │ │ │ - this.maxExtent = this.calculateMaxExtentWithLOD(this.lods[0]); │ │ │ │ - this.serverResolutions = this.resolutions; │ │ │ │ - if (this.overrideDPI && info.tileInfo.dpi) { │ │ │ │ - OpenLayers.DOTS_PER_INCH = info.tileInfo.dpi │ │ │ │ - } │ │ │ │ } │ │ │ │ } │ │ │ │ + return data │ │ │ │ }, │ │ │ │ - getContainingTileCoords: function(point, res) { │ │ │ │ - return new OpenLayers.Pixel(Math.max(Math.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)), 0), Math.max(Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res)), 0)) │ │ │ │ - }, │ │ │ │ - calculateMaxExtentWithLOD: function(lod) { │ │ │ │ - var numTileCols = lod.endTileCol - lod.startTileCol + 1; │ │ │ │ - var numTileRows = lod.endTileRow - lod.startTileRow + 1; │ │ │ │ - var minX = this.tileOrigin.lon + lod.startTileCol * this.tileSize.w * lod.resolution; │ │ │ │ - var maxX = minX + numTileCols * this.tileSize.w * lod.resolution; │ │ │ │ - var maxY = this.tileOrigin.lat - lod.startTileRow * this.tileSize.h * lod.resolution; │ │ │ │ - var minY = maxY - numTileRows * this.tileSize.h * lod.resolution; │ │ │ │ - return new OpenLayers.Bounds(minX, minY, maxX, maxY) │ │ │ │ - }, │ │ │ │ - calculateMaxExtentWithExtent: function(extent, res) { │ │ │ │ - var upperLeft = new OpenLayers.Geometry.Point(extent.left, extent.top); │ │ │ │ - var bottomRight = new OpenLayers.Geometry.Point(extent.right, extent.bottom); │ │ │ │ - var start = this.getContainingTileCoords(upperLeft, res); │ │ │ │ - var end = this.getContainingTileCoords(bottomRight, res); │ │ │ │ - var lod = { │ │ │ │ - resolution: res, │ │ │ │ - startTileCol: start.x, │ │ │ │ - startTileRow: start.y, │ │ │ │ - endTileCol: end.x, │ │ │ │ - endTileRow: end.y │ │ │ │ - }; │ │ │ │ - return this.calculateMaxExtentWithLOD(lod) │ │ │ │ - }, │ │ │ │ - getUpperLeftTileCoord: function(res) { │ │ │ │ - var upperLeft = new OpenLayers.Geometry.Point(this.maxExtent.left, this.maxExtent.top); │ │ │ │ - return this.getContainingTileCoords(upperLeft, res) │ │ │ │ - }, │ │ │ │ - getLowerRightTileCoord: function(res) { │ │ │ │ - var bottomRight = new OpenLayers.Geometry.Point(this.maxExtent.right, this.maxExtent.bottom); │ │ │ │ - return this.getContainingTileCoords(bottomRight, res) │ │ │ │ - }, │ │ │ │ - getMaxExtentForResolution: function(res) { │ │ │ │ - var start = this.getUpperLeftTileCoord(res); │ │ │ │ - var end = this.getLowerRightTileCoord(res); │ │ │ │ - var numTileCols = end.x - start.x + 1; │ │ │ │ - var numTileRows = end.y - start.y + 1; │ │ │ │ - var minX = this.tileOrigin.lon + start.x * this.tileSize.w * res; │ │ │ │ - var maxX = minX + numTileCols * this.tileSize.w * res; │ │ │ │ - var maxY = this.tileOrigin.lat - start.y * this.tileSize.h * res; │ │ │ │ - var minY = maxY - numTileRows * this.tileSize.h * res; │ │ │ │ - return new OpenLayers.Bounds(minX, minY, maxX, maxY) │ │ │ │ + destroyTile: function(tile) { │ │ │ │ + this.removeTileMonitoringHooks(tile); │ │ │ │ + tile.destroy() │ │ │ │ }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.ArcGISCache(this.name, this.url, this.options) │ │ │ │ + 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 OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]) │ │ │ │ - }, │ │ │ │ - initGriddedTiles: function(bounds) { │ │ │ │ - delete this._tileOrigin; │ │ │ │ - OpenLayers.Layer.XYZ.prototype.initGriddedTiles.apply(this, arguments) │ │ │ │ + return resolution │ │ │ │ }, │ │ │ │ - getMaxExtent: function() { │ │ │ │ - var resolution = this.map.getResolution(); │ │ │ │ - return this.maxExtent = this.getMaxExtentForResolution(resolution) │ │ │ │ + getServerZoom: function() { │ │ │ │ + var resolution = this.getServerResolution(); │ │ │ │ + return this.serverResolutions ? OpenLayers.Util.indexOf(this.serverResolutions, resolution) : this.map.getZoomForResolution(resolution) + (this.zoomOffset || 0) │ │ │ │ }, │ │ │ │ - getTileOrigin: function() { │ │ │ │ - if (!this._tileOrigin) { │ │ │ │ - var extent = this.getMaxExtent(); │ │ │ │ - this._tileOrigin = new OpenLayers.LonLat(extent.left, extent.bottom) │ │ │ │ + applyBackBuffer: function(resolution) { │ │ │ │ + if (this.backBufferTimerId !== null) { │ │ │ │ + this.removeBackBuffer() │ │ │ │ } │ │ │ │ - return this._tileOrigin │ │ │ │ - }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - var res = this.getResolution(); │ │ │ │ - var originTileX = this.tileOrigin.lon + res * this.tileSize.w / 2; │ │ │ │ - var originTileY = this.tileOrigin.lat - res * this.tileSize.h / 2; │ │ │ │ - var center = bounds.getCenterLonLat(); │ │ │ │ - var point = { │ │ │ │ - x: center.lon, │ │ │ │ - y: center.lat │ │ │ │ - }; │ │ │ │ - var x = Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w))); │ │ │ │ - var y = Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h))); │ │ │ │ - var z = this.map.getZoom(); │ │ │ │ - if (this.lods) { │ │ │ │ - var lod = this.lods[this.map.getZoom()]; │ │ │ │ - if (x < lod.startTileCol || x > lod.endTileCol || (y < lod.startTileRow || y > lod.endTileRow)) { │ │ │ │ - return null │ │ │ │ + var backBuffer = this.backBuffer; │ │ │ │ + if (!backBuffer) { │ │ │ │ + backBuffer = this.createBackBuffer(); │ │ │ │ + if (!backBuffer) { │ │ │ │ + return │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - var start = this.getUpperLeftTileCoord(res); │ │ │ │ - var end = this.getLowerRightTileCoord(res); │ │ │ │ - if (x < start.x || x >= end.x || (y < start.y || y >= end.y)) { │ │ │ │ - return null │ │ │ │ + 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; │ │ │ │ + var topLeftTileBounds = this.grid[0][0].bounds; │ │ │ │ + this.backBufferLonLat = { │ │ │ │ + lon: topLeftTileBounds.left, │ │ │ │ + lat: topLeftTileBounds.top │ │ │ │ + }; │ │ │ │ + this.backBufferResolution = this.gridResolution │ │ │ │ } │ │ │ │ - var url = this.url; │ │ │ │ - var s = "" + x + y + z; │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(s, url) │ │ │ │ - } │ │ │ │ - if (this.useArcGISServer) { │ │ │ │ - url = url + "/tile/${z}/${y}/${x}" │ │ │ │ - } else { │ │ │ │ - x = "C" + OpenLayers.Number.zeroPad(x, 8, 16); │ │ │ │ - y = "R" + OpenLayers.Number.zeroPad(y, 8, 16); │ │ │ │ - z = "L" + OpenLayers.Number.zeroPad(z, 2, 10); │ │ │ │ - url = url + "/${z}/${y}/${x}." + this.type │ │ │ │ + var ratio = this.backBufferResolution / resolution; │ │ │ │ + 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" │ │ │ │ } │ │ │ │ - url = OpenLayers.String.format(url, { │ │ │ │ - x: x, │ │ │ │ - y: y, │ │ │ │ - z: z │ │ │ │ - }); │ │ │ │ - return OpenLayers.Util.urlAppend(url, OpenLayers.Util.getParameterString(this.params)) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.ArcGISCache" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ - key: null, │ │ │ │ - serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625, 19567.87923828125, 9783.939619140625, 4891.9698095703125, 2445.9849047851562, 1222.9924523925781, 611.4962261962891, 305.74811309814453, 152.87405654907226, 76.43702827453613, 38.218514137268066, 19.109257068634033, 9.554628534317017, 4.777314267158508, 2.388657133579254, 1.194328566789627, .5971642833948135, .29858214169740677, .14929107084870338, .07464553542435169], │ │ │ │ - attributionTemplate: '<span class="olBingAttribution ${type}">' + '<div><a target="_blank" href="http://www.bing.com/maps/">' + '<img src="${logo}" /></a></div>${copyrights}' + '<a style="white-space: nowrap" target="_blank" ' + 'href="http://www.microsoft.com/maps/product/terms.html">' + "Terms of Use</a></span>", │ │ │ │ - metadata: null, │ │ │ │ - protocolRegex: /^http:/i, │ │ │ │ - type: "Road", │ │ │ │ - culture: "en-US", │ │ │ │ - metadataParams: null, │ │ │ │ - tileOptions: null, │ │ │ │ - protocol: ~window.location.href.indexOf("http") ? "" : "http:", │ │ │ │ - initialize: function(options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults({ │ │ │ │ - sphericalMercator: true │ │ │ │ - }, options); │ │ │ │ - var name = options.name || "Bing " + (options.type || this.type); │ │ │ │ - var newArgs = [name, null, options]; │ │ │ │ - OpenLayers.Layer.XYZ.prototype.initialize.apply(this, newArgs); │ │ │ │ - this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ - crossOriginKeyword: "anonymous" │ │ │ │ - }, this.options.tileOptions); │ │ │ │ - this.loadMetadata() │ │ │ │ - }, │ │ │ │ - loadMetadata: function() { │ │ │ │ - this._callbackId = "_callback_" + this.id.replace(/\./g, "_"); │ │ │ │ - window[this._callbackId] = OpenLayers.Function.bind(OpenLayers.Layer.Bing.processMetadata, this); │ │ │ │ - var params = OpenLayers.Util.applyDefaults({ │ │ │ │ - key: this.key, │ │ │ │ - jsonp: this._callbackId, │ │ │ │ - include: "ImageryProviders" │ │ │ │ - }, this.metadataParams); │ │ │ │ - var url = this.protocol + "//dev.virtualearth.net/REST/v1/Imagery/Metadata/" + this.type + "?" + OpenLayers.Util.getParameterString(params); │ │ │ │ - var script = document.createElement("script"); │ │ │ │ - script.type = "text/javascript"; │ │ │ │ - script.src = url; │ │ │ │ - script.id = this._callbackId; │ │ │ │ - document.getElementsByTagName("head")[0].appendChild(script) │ │ │ │ + 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" │ │ │ │ }, │ │ │ │ - initLayer: function() { │ │ │ │ - var res = this.metadata.resourceSets[0].resources[0]; │ │ │ │ - var url = res.imageUrl.replace("{quadkey}", "${quadkey}"); │ │ │ │ - url = url.replace("{culture}", this.culture); │ │ │ │ - url = url.replace(this.protocolRegex, this.protocol); │ │ │ │ - this.url = []; │ │ │ │ - for (var i = 0; i < res.imageUrlSubdomains.length; ++i) { │ │ │ │ - this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i])) │ │ │ │ - } │ │ │ │ - this.addOptions({ │ │ │ │ - maxResolution: Math.min(this.serverResolutions[res.zoomMin], this.maxResolution || Number.POSITIVE_INFINITY), │ │ │ │ - numZoomLevels: Math.min(res.zoomMax + 1 - res.zoomMin, this.numZoomLevels) │ │ │ │ - }, true); │ │ │ │ - if (!this.isBaseLayer) { │ │ │ │ - this.redraw() │ │ │ │ + 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.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) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.updateAttribution() │ │ │ │ + return backBuffer │ │ │ │ }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - if (!this.url) { │ │ │ │ - return │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - var xyz = this.getXYZ(bounds), │ │ │ │ - x = xyz.x, │ │ │ │ - y = xyz.y, │ │ │ │ - z = xyz.z; │ │ │ │ - var quadDigits = []; │ │ │ │ - for (var i = z; i > 0; --i) { │ │ │ │ - var digit = "0"; │ │ │ │ - var mask = 1 << i - 1; │ │ │ │ - if ((x & mask) != 0) { │ │ │ │ - digit++ │ │ │ │ + if (this.backBuffer) { │ │ │ │ + if (this.backBuffer.parentNode) { │ │ │ │ + this.backBuffer.parentNode.removeChild(this.backBuffer) │ │ │ │ } │ │ │ │ - if ((y & mask) != 0) { │ │ │ │ - digit++; │ │ │ │ - digit++ │ │ │ │ + this.backBuffer = null; │ │ │ │ + this.backBufferResolution = null; │ │ │ │ + if (this.backBufferTimerId !== null) { │ │ │ │ + window.clearTimeout(this.backBufferTimerId); │ │ │ │ + this.backBufferTimerId = null │ │ │ │ } │ │ │ │ - quadDigits.push(digit) │ │ │ │ } │ │ │ │ - var quadKey = quadDigits.join(""); │ │ │ │ - var url = this.selectUrl("" + x + y + z, this.url); │ │ │ │ - return OpenLayers.String.format(url, { │ │ │ │ - quadkey: quadKey │ │ │ │ - }) │ │ │ │ }, │ │ │ │ - updateAttribution: function() { │ │ │ │ - var metadata = this.metadata; │ │ │ │ - if (!metadata.resourceSets || !this.map || !this.map.center) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var res = metadata.resourceSets[0].resources[0]; │ │ │ │ - var extent = this.map.getExtent().transform(this.map.getProjectionObject(), new OpenLayers.Projection("EPSG:4326")); │ │ │ │ - var providers = res.imageryProviders || [], │ │ │ │ - zoom = OpenLayers.Util.indexOf(this.serverResolutions, this.getServerResolution()), │ │ │ │ - copyrights = "", │ │ │ │ - provider, i, ii, j, jj, bbox, coverage; │ │ │ │ - for (i = 0, ii = providers.length; i < ii; ++i) { │ │ │ │ - provider = providers[i]; │ │ │ │ - for (j = 0, jj = provider.coverageAreas.length; j < jj; ++j) { │ │ │ │ - coverage = provider.coverageAreas[j]; │ │ │ │ - bbox = OpenLayers.Bounds.fromArray(coverage.bbox, true); │ │ │ │ - if (extent.intersectsBounds(bbox) && zoom <= coverage.zoomMax && zoom >= coverage.zoomMin) { │ │ │ │ - copyrights += provider.attribution + " " │ │ │ │ - } │ │ │ │ - } │ │ │ │ + moveByPx: function(dx, dy) { │ │ │ │ + if (!this.singleTile) { │ │ │ │ + this.moveGriddedTiles() │ │ │ │ } │ │ │ │ - var logo = metadata.brandLogoUri.replace(this.protocolRegex, this.protocol); │ │ │ │ - this.attribution = OpenLayers.String.format(this.attributionTemplate, { │ │ │ │ - type: this.type.toLowerCase(), │ │ │ │ - logo: logo, │ │ │ │ - copyrights: copyrights │ │ │ │ - }); │ │ │ │ - this.map && this.map.events.triggerEvent("changelayer", { │ │ │ │ - layer: this, │ │ │ │ - property: "attribution" │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - setMap: function() { │ │ │ │ - OpenLayers.Layer.XYZ.prototype.setMap.apply(this, arguments); │ │ │ │ - this.map.events.register("moveend", this, this.updateAttribution) │ │ │ │ }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Bing(this.options) │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ + OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]) │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - this.map && this.map.events.unregister("moveend", this, this.updateAttribution); │ │ │ │ - OpenLayers.Layer.XYZ.prototype.destroy.apply(this, arguments) │ │ │ │ + getTilesBounds: function() { │ │ │ │ + var bounds = null; │ │ │ │ + var length = this.grid.length; │ │ │ │ + if (length) { │ │ │ │ + var bottomLeftTileBounds = this.grid[length - 1][0].bounds, │ │ │ │ + width = this.grid[0].length * bottomLeftTileBounds.getWidth(), │ │ │ │ + height = this.grid.length * bottomLeftTileBounds.getHeight(); │ │ │ │ + bounds = new OpenLayers.Bounds(bottomLeftTileBounds.left, bottomLeftTileBounds.bottom, bottomLeftTileBounds.left + width, bottomLeftTileBounds.bottom + height) │ │ │ │ + } │ │ │ │ + return bounds │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Bing" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.Bing.processMetadata = function(metadata) { │ │ │ │ - this.metadata = metadata; │ │ │ │ - this.initLayer(); │ │ │ │ - var script = document.getElementById(this._callbackId); │ │ │ │ - script.parentNode.removeChild(script); │ │ │ │ - window[this._callbackId] = undefined; │ │ │ │ - delete this._callbackId │ │ │ │ -}; │ │ │ │ -OpenLayers.Layer.SphericalMercator = { │ │ │ │ - getExtent: function() { │ │ │ │ - var extent = null; │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - extent = this.map.calculateBounds() │ │ │ │ + initSingleTile: function(bounds) { │ │ │ │ + this.events.triggerEvent("retile"); │ │ │ │ + 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 { │ │ │ │ - extent = OpenLayers.Layer.FixedZoomLevels.prototype.getExtent.apply(this) │ │ │ │ + tile.moveTo(tileBounds, px) │ │ │ │ } │ │ │ │ - return extent │ │ │ │ - }, │ │ │ │ - getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ - return OpenLayers.Layer.prototype.getLonLatFromViewPortPx.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ - return OpenLayers.Layer.prototype.getViewPortPxFromLonLat.apply(this, arguments) │ │ │ │ + this.removeExcessTiles(1, 1); │ │ │ │ + this.gridResolution = this.getServerResolution() │ │ │ │ }, │ │ │ │ - initMercatorParameters: function() { │ │ │ │ - this.RESOLUTIONS = []; │ │ │ │ - var maxResolution = 156543.03390625; │ │ │ │ - for (var zoom = 0; zoom <= this.MAX_ZOOM_LEVEL; ++zoom) { │ │ │ │ - this.RESOLUTIONS[zoom] = maxResolution / Math.pow(2, zoom) │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - this.units = "m"; │ │ │ │ - this.projection = this.projection || "EPSG:900913" │ │ │ │ }, │ │ │ │ - forwardMercator: function() { │ │ │ │ - var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ - var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ - return function(lon, lat) { │ │ │ │ - var point = OpenLayers.Projection.transform({ │ │ │ │ - x: lon, │ │ │ │ - y: lat │ │ │ │ - }, gg, sm); │ │ │ │ - return new OpenLayers.LonLat(point.x, point.y) │ │ │ │ - } │ │ │ │ - }(), │ │ │ │ - inverseMercator: function() { │ │ │ │ - var gg = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ - var sm = new OpenLayers.Projection("EPSG:900913"); │ │ │ │ - return function(x, y) { │ │ │ │ - var point = OpenLayers.Projection.transform({ │ │ │ │ - x: x, │ │ │ │ - y: y │ │ │ │ - }, sm, gg); │ │ │ │ - return new OpenLayers.LonLat(point.x, point.y) │ │ │ │ - } │ │ │ │ - }() │ │ │ │ -}; │ │ │ │ -OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ - smoothDragPan: true, │ │ │ │ - isBaseLayer: true, │ │ │ │ - isFixed: true, │ │ │ │ - pane: null, │ │ │ │ - mapObject: null, │ │ │ │ - initialize: function(name, options) { │ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ - if (this.pane == null) { │ │ │ │ - this.pane = OpenLayers.Util.createDiv(this.div.id + "_EventPane") │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.mapObject = null; │ │ │ │ - this.pane = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ - this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; │ │ │ │ - this.pane.style.display = this.div.style.display; │ │ │ │ - this.pane.style.width = "100%"; │ │ │ │ - this.pane.style.height = "100%"; │ │ │ │ - if (OpenLayers.BROWSER_NAME == "msie") { │ │ │ │ - this.pane.style.background = "url(" + OpenLayers.Util.getImageLocation("blank.gif") + ")" │ │ │ │ - } │ │ │ │ - if (this.isFixed) { │ │ │ │ - this.map.viewPortDiv.appendChild(this.pane) │ │ │ │ - } else { │ │ │ │ - this.map.layerContainerDiv.appendChild(this.pane) │ │ │ │ - } │ │ │ │ - this.loadMapObject(); │ │ │ │ - if (this.mapObject == null) { │ │ │ │ - this.loadWarningMessage() │ │ │ │ + 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 │ │ │ │ }, │ │ │ │ - removeMap: function(map) { │ │ │ │ - if (this.pane && this.pane.parentNode) { │ │ │ │ - this.pane.parentNode.removeChild(this.pane) │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.prototype.removeMap.apply(this, arguments) │ │ │ │ + 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) │ │ │ │ }, │ │ │ │ - loadWarningMessage: function() { │ │ │ │ - this.div.style.backgroundColor = "darkblue"; │ │ │ │ + initGriddedTiles: function(bounds) { │ │ │ │ + this.events.triggerEvent("retile"); │ │ │ │ var viewSize = this.map.getSize(); │ │ │ │ - var msgW = Math.min(viewSize.w, 300); │ │ │ │ - var msgH = Math.min(viewSize.h, 200); │ │ │ │ - var size = new OpenLayers.Size(msgW, msgH); │ │ │ │ - var centerPx = new OpenLayers.Pixel(viewSize.w / 2, viewSize.h / 2); │ │ │ │ - var topLeft = centerPx.add(-size.w / 2, -size.h / 2); │ │ │ │ - var div = OpenLayers.Util.createDiv(this.name + "_warning", topLeft, size, null, null, null, "auto"); │ │ │ │ - div.style.padding = "7px"; │ │ │ │ - div.style.backgroundColor = "yellow"; │ │ │ │ - div.innerHTML = this.getWarningHTML(); │ │ │ │ - this.div.appendChild(div) │ │ │ │ - }, │ │ │ │ - getWarningHTML: function() { │ │ │ │ - return "" │ │ │ │ - }, │ │ │ │ - display: function(display) { │ │ │ │ - OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ - this.pane.style.display = this.div.style.display │ │ │ │ + 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); │ │ │ │ + this.removeExcessTiles(rowidx, colidx); │ │ │ │ + var resolution = this.getServerResolution(); │ │ │ │ + this.gridResolution = resolution; │ │ │ │ + tileData.sort(function(a, b) { │ │ │ │ + return a.distance - b.distance │ │ │ │ + }); │ │ │ │ + for (var i = 0, ii = tileData.length; i < ii; ++i) { │ │ │ │ + tileData[i].tile.draw() │ │ │ │ + } │ │ │ │ }, │ │ │ │ - setZIndex: function(zIndex) { │ │ │ │ - OpenLayers.Layer.prototype.setZIndex.apply(this, arguments); │ │ │ │ - this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1 │ │ │ │ + getMaxExtent: function() { │ │ │ │ + return this.maxExtent │ │ │ │ }, │ │ │ │ - moveByPx: function(dx, dy) { │ │ │ │ - OpenLayers.Layer.prototype.moveByPx.apply(this, arguments); │ │ │ │ - if (this.dragPanMapObject) { │ │ │ │ - this.dragPanMapObject(dx, -dy) │ │ │ │ - } else { │ │ │ │ - this.moveTo(this.map.getCachedCenter()) │ │ │ │ - } │ │ │ │ + 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 │ │ │ │ }, │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ - if (this.mapObject != null) { │ │ │ │ - var newCenter = this.map.getCenter(); │ │ │ │ - var newZoom = this.map.getZoom(); │ │ │ │ - if (newCenter != null) { │ │ │ │ - var moOldCenter = this.getMapObjectCenter(); │ │ │ │ - var oldCenter = this.getOLLonLatFromMapObjectLonLat(moOldCenter); │ │ │ │ - var moOldZoom = this.getMapObjectZoom(); │ │ │ │ - var oldZoom = this.getOLZoomFromMapObjectZoom(moOldZoom); │ │ │ │ - if (!newCenter.equals(oldCenter) || newZoom != oldZoom) { │ │ │ │ - if (!zoomChanged && oldCenter && this.dragPanMapObject && this.smoothDragPan) { │ │ │ │ - var oldPx = this.map.getViewPortPxFromLonLat(oldCenter); │ │ │ │ - var newPx = this.map.getViewPortPxFromLonLat(newCenter); │ │ │ │ - this.dragPanMapObject(newPx.x - oldPx.x, oldPx.y - newPx.y) │ │ │ │ + addTileMonitoringHooks: function(tile) { │ │ │ │ + var replacingCls = "olTileReplacing"; │ │ │ │ + tile.onLoadStart = function() { │ │ │ │ + if (this.loading === false) { │ │ │ │ + this.loading = true; │ │ │ │ + this.events.triggerEvent("loadstart") │ │ │ │ + } │ │ │ │ + this.events.triggerEvent("tileloadstart", { │ │ │ │ + tile: tile │ │ │ │ + }); │ │ │ │ + this.numLoadingTiles++; │ │ │ │ + if (!this.singleTile && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ + OpenLayers.Element.addClass(tile.getTile(), replacingCls) │ │ │ │ + } │ │ │ │ + }; │ │ │ │ + tile.onLoadEnd = function(evt) { │ │ │ │ + this.numLoadingTiles--; │ │ │ │ + var aborted = evt.type === "unload"; │ │ │ │ + this.events.triggerEvent("tileloaded", { │ │ │ │ + tile: tile, │ │ │ │ + aborted: aborted │ │ │ │ + }); │ │ │ │ + if (!this.singleTile && !aborted && this.backBuffer && this.gridResolution === this.backBufferResolution) { │ │ │ │ + var tileDiv = tile.getTile(); │ │ │ │ + if (OpenLayers.Element.getStyle(tileDiv, "display") === "none") { │ │ │ │ + var bufferTile = document.getElementById(tile.id + "_bb"); │ │ │ │ + if (bufferTile) { │ │ │ │ + bufferTile.parentNode.removeChild(bufferTile) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + OpenLayers.Element.removeClass(tileDiv, replacingCls) │ │ │ │ + } │ │ │ │ + if (this.numLoadingTiles === 0) { │ │ │ │ + if (this.backBuffer) { │ │ │ │ + if (this.backBuffer.childNodes.length === 0) { │ │ │ │ + this.removeBackBuffer() │ │ │ │ } else { │ │ │ │ - var center = this.getMapObjectLonLatFromOLLonLat(newCenter); │ │ │ │ - var zoom = this.getMapObjectZoomFromOLZoom(newZoom); │ │ │ │ - this.setMapObjectCenter(center, zoom, dragging) │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + 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 │ │ │ │ + }) │ │ │ │ }, │ │ │ │ - getLonLatFromViewPortPx: function(viewPortPx) { │ │ │ │ - var lonlat = null; │ │ │ │ - if (this.mapObject != null && this.getMapObjectCenter() != null) { │ │ │ │ - var moPixel = this.getMapObjectPixelFromOLPixel(viewPortPx); │ │ │ │ - var moLonLat = this.getMapObjectLonLatFromMapObjectPixel(moPixel); │ │ │ │ - lonlat = this.getOLLonLatFromMapObjectLonLat(moLonLat) │ │ │ │ - } │ │ │ │ - return lonlat │ │ │ │ + removeTileMonitoringHooks: function(tile) { │ │ │ │ + tile.unload(); │ │ │ │ + tile.events.un({ │ │ │ │ + loadstart: tile.onLoadStart, │ │ │ │ + loadend: tile.onLoadEnd, │ │ │ │ + unload: tile.onLoadEnd, │ │ │ │ + loaderror: tile.onLoadError, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ }, │ │ │ │ - getViewPortPxFromLonLat: function(lonlat) { │ │ │ │ - var viewPortPx = null; │ │ │ │ - if (this.mapObject != null && this.getMapObjectCenter() != null) { │ │ │ │ - var moLonLat = this.getMapObjectLonLatFromOLLonLat(lonlat); │ │ │ │ - var moPixel = this.getMapObjectPixelFromMapObjectLonLat(moLonLat); │ │ │ │ - viewPortPx = this.getOLPixelFromMapObjectPixel(moPixel) │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ } │ │ │ │ - return viewPortPx │ │ │ │ }, │ │ │ │ - getOLLonLatFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - var olLonLat = null; │ │ │ │ - if (moLonLat != null) { │ │ │ │ - var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - olLonLat = new OpenLayers.LonLat(lon, lat) │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - return olLonLat │ │ │ │ + grid[prepend ? "unshift" : "push"](row) │ │ │ │ }, │ │ │ │ - getMapObjectLonLatFromOLLonLat: function(olLonLat) { │ │ │ │ - var moLatLng = null; │ │ │ │ - if (olLonLat != null) { │ │ │ │ - moLatLng = this.getMapObjectLonLatFromLonLat(olLonLat.lon, olLonLat.lat) │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - return moLatLng │ │ │ │ }, │ │ │ │ - getOLPixelFromMapObjectPixel: function(moPixel) { │ │ │ │ - var olPixel = null; │ │ │ │ - if (moPixel != null) { │ │ │ │ - var x = this.getXFromMapObjectPixel(moPixel); │ │ │ │ - var y = this.getYFromMapObjectPixel(moPixel); │ │ │ │ - olPixel = new OpenLayers.Pixel(x, y) │ │ │ │ + removeExcessTiles: function(rows, columns) { │ │ │ │ + var i, l; │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ } │ │ │ │ - return olPixel │ │ │ │ }, │ │ │ │ - getMapObjectPixelFromOLPixel: function(olPixel) { │ │ │ │ - var moPixel = null; │ │ │ │ - if (olPixel != null) { │ │ │ │ - moPixel = this.getMapObjectPixelFromXY(olPixel.x, olPixel.y) │ │ │ │ + onMapResize: function() { │ │ │ │ + if (this.singleTile) { │ │ │ │ + this.clearGrid(); │ │ │ │ + this.setTileSize() │ │ │ │ } │ │ │ │ - return moPixel │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.EventPane" │ │ │ │ + getTileBounds: function(viewPortPx) { │ │ │ │ + var maxExtent = this.maxExtent; │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var tileMapWidth = resolution * this.tileSize.w; │ │ │ │ + var tileMapHeight = resolution * this.tileSize.h; │ │ │ │ + var mapPoint = this.getLonLatFromViewPortPx(viewPortPx); │ │ │ │ + var tileLeft = maxExtent.left + tileMapWidth * Math.floor((mapPoint.lon - maxExtent.left) / tileMapWidth); │ │ │ │ + var tileBottom = maxExtent.bottom + tileMapHeight * Math.floor((mapPoint.lat - maxExtent.bottom) / tileMapHeight); │ │ │ │ + return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Layer.Grid" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.FixedZoomLevels = OpenLayers.Class({ │ │ │ │ - initialize: function() {}, │ │ │ │ - initResolutions: function() { │ │ │ │ - var props = ["minZoomLevel", "maxZoomLevel", "numZoomLevels"]; │ │ │ │ - for (var i = 0, len = props.length; i < len; i++) { │ │ │ │ - var property = props[i]; │ │ │ │ - this[property] = this.options[property] != null ? this.options[property] : this.map[property] │ │ │ │ - } │ │ │ │ - if (this.minZoomLevel == null || this.minZoomLevel < this.MIN_ZOOM_LEVEL) { │ │ │ │ - this.minZoomLevel = this.MIN_ZOOM_LEVEL │ │ │ │ +OpenLayers.TileManager = OpenLayers.Class({ │ │ │ │ + cacheSize: 256, │ │ │ │ + tilesPerFrame: 2, │ │ │ │ + frameDelay: 16, │ │ │ │ + moveDelay: 100, │ │ │ │ + zoomDelay: 200, │ │ │ │ + maps: null, │ │ │ │ + tileQueueId: null, │ │ │ │ + tileQueue: null, │ │ │ │ + tileCache: null, │ │ │ │ + tileCacheIndex: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.maps = []; │ │ │ │ + this.tileQueueId = {}; │ │ │ │ + this.tileQueue = {}; │ │ │ │ + this.tileCache = {}; │ │ │ │ + this.tileCacheIndex = [] │ │ │ │ + }, │ │ │ │ + addMap: function(map) { │ │ │ │ + if (this._destroyed || !OpenLayers.Layer.Grid) { │ │ │ │ + return │ │ │ │ } │ │ │ │ - var desiredZoomLevels; │ │ │ │ - var limitZoomLevels = this.MAX_ZOOM_LEVEL - this.minZoomLevel + 1; │ │ │ │ - if (this.options.numZoomLevels == null && this.options.maxZoomLevel != null || this.numZoomLevels == null && this.maxZoomLevel != null) { │ │ │ │ - desiredZoomLevels = this.maxZoomLevel - this.minZoomLevel + 1 │ │ │ │ - } else { │ │ │ │ - desiredZoomLevels = this.numZoomLevels │ │ │ │ + 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] │ │ │ │ + }) │ │ │ │ } │ │ │ │ - if (desiredZoomLevels != null) { │ │ │ │ - this.numZoomLevels = Math.min(desiredZoomLevels, limitZoomLevels) │ │ │ │ - } else { │ │ │ │ - this.numZoomLevels = limitZoomLevels │ │ │ │ + map.events.on({ │ │ │ │ + move: this.move, │ │ │ │ + zoomend: this.zoomEnd, │ │ │ │ + changelayer: this.changeLayer, │ │ │ │ + addlayer: this.addLayer, │ │ │ │ + preremovelayer: this.removeLayer, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + removeMap: function(map) { │ │ │ │ + if (this._destroyed || !OpenLayers.Layer.Grid) { │ │ │ │ + return │ │ │ │ } │ │ │ │ - this.maxZoomLevel = this.minZoomLevel + this.numZoomLevels - 1; │ │ │ │ - if (this.RESOLUTIONS != null) { │ │ │ │ - var resolutionsIndex = 0; │ │ │ │ - this.resolutions = []; │ │ │ │ - for (var i = this.minZoomLevel; i <= this.maxZoomLevel; i++) { │ │ │ │ - this.resolutions[resolutionsIndex++] = this.RESOLUTIONS[i] │ │ │ │ + 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] │ │ │ │ + }) │ │ │ │ } │ │ │ │ - this.maxResolution = this.resolutions[0]; │ │ │ │ - this.minResolution = this.resolutions[this.resolutions.length - 1] │ │ │ │ } │ │ │ │ - }, │ │ │ │ - getResolution: function() { │ │ │ │ - if (this.resolutions != null) { │ │ │ │ - return OpenLayers.Layer.prototype.getResolution.apply(this, arguments) │ │ │ │ - } else { │ │ │ │ - var resolution = null; │ │ │ │ - var viewSize = this.map.getSize(); │ │ │ │ - var extent = this.getExtent(); │ │ │ │ - if (viewSize != null && extent != null) { │ │ │ │ - resolution = Math.max(extent.getWidth() / viewSize.w, extent.getHeight() / viewSize.h) │ │ │ │ - } │ │ │ │ - return resolution │ │ │ │ + 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) │ │ │ │ }, │ │ │ │ - getExtent: function() { │ │ │ │ - var size = this.map.getSize(); │ │ │ │ - var tl = this.getLonLatFromViewPortPx({ │ │ │ │ - x: 0, │ │ │ │ - y: 0 │ │ │ │ - }); │ │ │ │ - var br = this.getLonLatFromViewPortPx({ │ │ │ │ - x: size.w, │ │ │ │ - y: size.h │ │ │ │ - }); │ │ │ │ - if (tl != null && br != null) { │ │ │ │ - return new OpenLayers.Bounds(tl.lon, br.lat, br.lon, tl.lat) │ │ │ │ - } else { │ │ │ │ - return null │ │ │ │ - } │ │ │ │ + move: function(evt) { │ │ │ │ + this.updateTimeout(evt.object, this.moveDelay, true) │ │ │ │ }, │ │ │ │ - getZoomForResolution: function(resolution) { │ │ │ │ - if (this.resolutions != null) { │ │ │ │ - return OpenLayers.Layer.prototype.getZoomForResolution.apply(this, arguments) │ │ │ │ - } else { │ │ │ │ - var extent = OpenLayers.Layer.prototype.getExtent.apply(this, []); │ │ │ │ - return this.getZoomForExtent(extent) │ │ │ │ + zoomEnd: function(evt) { │ │ │ │ + this.updateTimeout(evt.object, this.zoomDelay) │ │ │ │ + }, │ │ │ │ + changeLayer: function(evt) { │ │ │ │ + if (evt.property === "visibility" || evt.property === "params") { │ │ │ │ + this.updateTimeout(evt.object, 0) │ │ │ │ } │ │ │ │ }, │ │ │ │ - getOLZoomFromMapObjectZoom: function(moZoom) { │ │ │ │ - var zoom = null; │ │ │ │ - if (moZoom != null) { │ │ │ │ - zoom = moZoom - this.minZoomLevel; │ │ │ │ - if (this.map.baseLayer !== this) { │ │ │ │ - zoom = this.map.baseLayer.getZoomForResolution(this.getResolutionForZoom(zoom)) │ │ │ │ + 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 │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ - return zoom │ │ │ │ }, │ │ │ │ - getMapObjectZoomFromOLZoom: function(olZoom) { │ │ │ │ - var zoom = null; │ │ │ │ - if (olZoom != null) { │ │ │ │ - zoom = olZoom + this.minZoomLevel; │ │ │ │ - if (this.map.baseLayer !== this) { │ │ │ │ - zoom = this.getZoomForResolution(this.map.baseLayer.getResolutionForZoom(zoom)) │ │ │ │ + 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 zoom │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.FixedZoomLevels" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.Google = OpenLayers.Class(OpenLayers.Layer.EventPane, OpenLayers.Layer.FixedZoomLevels, { │ │ │ │ - MIN_ZOOM_LEVEL: 0, │ │ │ │ - MAX_ZOOM_LEVEL: 21, │ │ │ │ - RESOLUTIONS: [1.40625, .703125, .3515625, .17578125, .087890625, .0439453125, .02197265625, .010986328125, .0054931640625, .00274658203125, .001373291015625, .0006866455078125, .00034332275390625, .000171661376953125, 858306884765625e-19, 4291534423828125e-20, 2145767211914062e-20, 1072883605957031e-20, 536441802978515e-20, 268220901489257e-20, 1341104507446289e-21, 6.705522537231445e-7], │ │ │ │ - type: null, │ │ │ │ - wrapDateLine: true, │ │ │ │ - sphericalMercator: false, │ │ │ │ - version: null, │ │ │ │ - initialize: function(name, options) { │ │ │ │ - options = options || {}; │ │ │ │ - if (!options.version) { │ │ │ │ - options.version = typeof GMap2 === "function" ? "2" : "3" │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - var mixin = OpenLayers.Layer.Google["v" + options.version.replace(/\./g, "_")]; │ │ │ │ - if (mixin) { │ │ │ │ - OpenLayers.Util.applyDefaults(options, mixin) │ │ │ │ + }, │ │ │ │ + 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 { │ │ │ │ - throw "Unsupported Google Maps API version: " + options.version │ │ │ │ - } │ │ │ │ - OpenLayers.Util.applyDefaults(options, mixin.DEFAULTS); │ │ │ │ - if (options.maxExtent) { │ │ │ │ - options.maxExtent = options.maxExtent.clone() │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.EventPane.prototype.initialize.apply(this, [name, options]); │ │ │ │ - OpenLayers.Layer.FixedZoomLevels.prototype.initialize.apply(this, [name, options]); │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - OpenLayers.Util.extend(this, OpenLayers.Layer.SphericalMercator); │ │ │ │ - this.initMercatorParameters() │ │ │ │ + this.removeLayer({ │ │ │ │ + layer: evt.tile.layer │ │ │ │ + }) │ │ │ │ } │ │ │ │ }, │ │ │ │ - clone: function() { │ │ │ │ - return new OpenLayers.Layer.Google(this.name, this.getOptions()) │ │ │ │ - }, │ │ │ │ - setVisibility: function(visible) { │ │ │ │ - var opacity = this.opacity == null ? 1 : this.opacity; │ │ │ │ - OpenLayers.Layer.EventPane.prototype.setVisibility.apply(this, arguments); │ │ │ │ - this.setOpacity(opacity) │ │ │ │ + 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) │ │ │ │ }, │ │ │ │ - display: function(visible) { │ │ │ │ - if (!this._dragging) { │ │ │ │ - this.setGMapVisibility(visible) │ │ │ │ + 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") { │ │ │ │ + delete this.tileCache[url]; │ │ │ │ + OpenLayers.Util.removeItem(this.tileCacheIndex, url); │ │ │ │ + img = null │ │ │ │ } │ │ │ │ - OpenLayers.Layer.EventPane.prototype.display.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - this._dragging = dragging; │ │ │ │ - OpenLayers.Layer.EventPane.prototype.moveTo.apply(this, arguments); │ │ │ │ - delete this._dragging │ │ │ │ - }, │ │ │ │ - setOpacity: function(opacity) { │ │ │ │ - if (opacity !== this.opacity) { │ │ │ │ - if (this.map != null) { │ │ │ │ - this.map.events.triggerEvent("changelayer", { │ │ │ │ - layer: this, │ │ │ │ - property: "opacity" │ │ │ │ - }) │ │ │ │ + if (layer.url && (layer.async || !img)) { │ │ │ │ + var tileQueue = this.tileQueue[layer.map.id]; │ │ │ │ + if (!~OpenLayers.Util.indexOf(tileQueue, tile)) { │ │ │ │ + tileQueue.push(tile) │ │ │ │ } │ │ │ │ - this.opacity = opacity │ │ │ │ - } │ │ │ │ - if (this.getVisibility()) { │ │ │ │ - var container = this.getMapContainer(); │ │ │ │ - OpenLayers.Util.modifyDOMElement(container, null, null, null, null, null, null, opacity) │ │ │ │ + queued = true │ │ │ │ } │ │ │ │ + return !queued │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.setGMapVisibility(false); │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache && cache.count <= 1) { │ │ │ │ - this.removeGMapElements() │ │ │ │ - } │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - OpenLayers.Layer.EventPane.prototype.destroy.apply(this, arguments) │ │ │ │ }, │ │ │ │ - removeGMapElements: function() { │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - var container = this.mapObject && this.getMapContainer(); │ │ │ │ - if (container && container.parentNode) { │ │ │ │ - container.parentNode.removeChild(container) │ │ │ │ - } │ │ │ │ - var termsOfUse = cache.termsOfUse; │ │ │ │ - if (termsOfUse && termsOfUse.parentNode) { │ │ │ │ - termsOfUse.parentNode.removeChild(termsOfUse) │ │ │ │ - } │ │ │ │ - var poweredBy = cache.poweredBy; │ │ │ │ - if (poweredBy && poweredBy.parentNode) { │ │ │ │ - poweredBy.parentNode.removeChild(poweredBy) │ │ │ │ + manageTileCache: function(evt) { │ │ │ │ + var tile = evt.object; │ │ │ │ + var img = this.tileCache[tile.url]; │ │ │ │ + if (img) { │ │ │ │ + if (img.parentNode && OpenLayers.Element.hasClass(img.parentNode, "olBackBuffer")) { │ │ │ │ + img.parentNode.removeChild(img); │ │ │ │ + img.id = null │ │ │ │ } │ │ │ │ - if (this.mapObject && window.google && google.maps && google.maps.event && google.maps.event.clearListeners) { │ │ │ │ - google.maps.event.clearListeners(this.mapObject, "tilesloaded") │ │ │ │ + if (!img.parentNode) { │ │ │ │ + img.style.visibility = "hidden"; │ │ │ │ + img.style.opacity = 0; │ │ │ │ + tile.setImage(img); │ │ │ │ + OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url); │ │ │ │ + this.tileCacheIndex.push(tile.url) │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ - removeMap: function(map) { │ │ │ │ - if (this.visibility && this.mapObject) { │ │ │ │ - this.setGMapVisibility(false) │ │ │ │ - } │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[map.id]; │ │ │ │ - if (cache) { │ │ │ │ - if (cache.count <= 1) { │ │ │ │ - this.removeGMapElements(); │ │ │ │ - delete OpenLayers.Layer.Google.cache[map.id] │ │ │ │ - } else { │ │ │ │ - --cache.count │ │ │ │ + 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) │ │ │ │ } │ │ │ │ } │ │ │ │ - delete this.termsOfUse; │ │ │ │ - delete this.poweredBy; │ │ │ │ - delete this.mapObject; │ │ │ │ - delete this.dragObject; │ │ │ │ - OpenLayers.Layer.EventPane.prototype.removeMap.apply(this, arguments) │ │ │ │ }, │ │ │ │ - getOLBoundsFromMapObjectBounds: function(moBounds) { │ │ │ │ - var olBounds = null; │ │ │ │ - if (moBounds != null) { │ │ │ │ - var sw = moBounds.getSouthWest(); │ │ │ │ - var ne = moBounds.getNorthEast(); │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - sw = this.forwardMercator(sw.lng(), sw.lat()); │ │ │ │ - ne = this.forwardMercator(ne.lng(), ne.lat()) │ │ │ │ - } else { │ │ │ │ - sw = new OpenLayers.LonLat(sw.lng(), sw.lat()); │ │ │ │ - ne = new OpenLayers.LonLat(ne.lng(), ne.lat()) │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - olBounds = new OpenLayers.Bounds(sw.lon, sw.lat, ne.lon, ne.lat) │ │ │ │ } │ │ │ │ - return olBounds │ │ │ │ - }, │ │ │ │ - getWarningHTML: function() { │ │ │ │ - return OpenLayers.i18n("googleWarning") │ │ │ │ - }, │ │ │ │ - getMapObjectCenter: function() { │ │ │ │ - return this.mapObject.getCenter() │ │ │ │ }, │ │ │ │ - getMapObjectZoom: function() { │ │ │ │ - return this.mapObject.getZoom() │ │ │ │ + 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.Strategy = OpenLayers.Class({ │ │ │ │ + layer: null, │ │ │ │ + options: null, │ │ │ │ + active: null, │ │ │ │ + autoActivate: true, │ │ │ │ + autoDestroy: true, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Util.extend(this, options); │ │ │ │ + this.options = options; │ │ │ │ + this.active = false │ │ │ │ }, │ │ │ │ - getLongitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - return this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lon : moLonLat.lng() │ │ │ │ + destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ + this.layer = null; │ │ │ │ + this.options = null │ │ │ │ }, │ │ │ │ - getLatitudeFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - var lat = this.sphericalMercator ? this.forwardMercator(moLonLat.lng(), moLonLat.lat()).lat : moLonLat.lat(); │ │ │ │ - return lat │ │ │ │ + setLayer: function(layer) { │ │ │ │ + this.layer = layer │ │ │ │ }, │ │ │ │ - getXFromMapObjectPixel: function(moPixel) { │ │ │ │ - return moPixel.x │ │ │ │ + activate: function() { │ │ │ │ + if (!this.active) { │ │ │ │ + this.active = true; │ │ │ │ + return true │ │ │ │ + } │ │ │ │ + return false │ │ │ │ }, │ │ │ │ - getYFromMapObjectPixel: function(moPixel) { │ │ │ │ - return moPixel.y │ │ │ │ + deactivate: function() { │ │ │ │ + if (this.active) { │ │ │ │ + this.active = false; │ │ │ │ + return true │ │ │ │ + } │ │ │ │ + return false │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Google" │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.Google.cache = {}; │ │ │ │ -OpenLayers.Layer.Google.v2 = { │ │ │ │ - termsOfUse: null, │ │ │ │ - poweredBy: null, │ │ │ │ - dragObject: null, │ │ │ │ - loadMapObject: function() { │ │ │ │ - if (!this.type) { │ │ │ │ - this.type = G_NORMAL_MAP │ │ │ │ - } │ │ │ │ - var mapObject, termsOfUse, poweredBy; │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - mapObject = cache.mapObject; │ │ │ │ - termsOfUse = cache.termsOfUse; │ │ │ │ - poweredBy = cache.poweredBy; │ │ │ │ - ++cache.count │ │ │ │ - } else { │ │ │ │ - var container = this.map.viewPortDiv; │ │ │ │ - var div = document.createElement("div"); │ │ │ │ - div.id = this.map.id + "_GMap2Container"; │ │ │ │ - div.style.position = "absolute"; │ │ │ │ - div.style.width = "100%"; │ │ │ │ - div.style.height = "100%"; │ │ │ │ - container.appendChild(div); │ │ │ │ - try { │ │ │ │ - mapObject = new GMap2(div); │ │ │ │ - termsOfUse = div.lastChild; │ │ │ │ - container.appendChild(termsOfUse); │ │ │ │ - termsOfUse.style.zIndex = "1100"; │ │ │ │ - termsOfUse.style.right = ""; │ │ │ │ - termsOfUse.style.bottom = ""; │ │ │ │ - termsOfUse.className = "olLayerGoogleCopyright"; │ │ │ │ - poweredBy = div.lastChild; │ │ │ │ - container.appendChild(poweredBy); │ │ │ │ - poweredBy.style.zIndex = "1100"; │ │ │ │ - poweredBy.style.right = ""; │ │ │ │ - poweredBy.style.bottom = ""; │ │ │ │ - poweredBy.className = "olLayerGooglePoweredBy gmnoprint" │ │ │ │ - } catch (e) { │ │ │ │ - throw e │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.Google.cache[this.map.id] = { │ │ │ │ - mapObject: mapObject, │ │ │ │ - termsOfUse: termsOfUse, │ │ │ │ - poweredBy: poweredBy, │ │ │ │ - count: 1 │ │ │ │ +OpenLayers.Events.featureclick = OpenLayers.Class({ │ │ │ │ + cache: null, │ │ │ │ + map: null, │ │ │ │ + provides: ["featureclick", "nofeatureclick", "featureover", "featureout"], │ │ │ │ + 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) │ │ │ │ + }) │ │ │ │ } │ │ │ │ - } │ │ │ │ - this.mapObject = mapObject; │ │ │ │ - this.termsOfUse = termsOfUse; │ │ │ │ - this.poweredBy = poweredBy; │ │ │ │ - if (OpenLayers.Util.indexOf(this.mapObject.getMapTypes(), this.type) === -1) { │ │ │ │ - this.mapObject.addMapType(this.type) │ │ │ │ - } │ │ │ │ - if (typeof mapObject.getDragObject == "function") { │ │ │ │ - this.dragObject = mapObject.getDragObject() │ │ │ │ } else { │ │ │ │ - this.dragPanMapObject = null │ │ │ │ + throw "Listeners for '" + this.provides.join("', '") + "' events can only be registered for OpenLayers.Layer.Vector " + "or OpenLayers.Map instances" │ │ │ │ } │ │ │ │ - if (this.isBaseLayer === false) { │ │ │ │ - this.setGMapVisibility(this.div.style.display !== "none") │ │ │ │ + for (var i = this.provides.length - 1; i >= 0; --i) { │ │ │ │ + target.extensions[this.provides[i]] = true │ │ │ │ } │ │ │ │ }, │ │ │ │ - onMapResize: function() { │ │ │ │ - if (this.visibility && this.mapObject.isLoaded()) { │ │ │ │ - this.mapObject.checkResize() │ │ │ │ - } else { │ │ │ │ - if (!this._resized) { │ │ │ │ - var layer = this; │ │ │ │ - var handle = GEvent.addListener(this.mapObject, "load", function() { │ │ │ │ - GEvent.removeListener(handle); │ │ │ │ - delete layer._resized; │ │ │ │ - layer.mapObject.checkResize(); │ │ │ │ - layer.moveTo(layer.map.getCenter(), layer.map.getZoom()) │ │ │ │ + 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 │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + start: function(evt) { │ │ │ │ + this.startEvt = evt │ │ │ │ + }, │ │ │ │ + cancel: function(evt) { │ │ │ │ + delete this.startEvt │ │ │ │ + }, │ │ │ │ + onClick: function(evt) { │ │ │ │ + if (!this.startEvt || evt.type !== "touchend" && !OpenLayers.Event.isLeftClick(evt)) { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + var features = this.getFeatures(this.startEvt); │ │ │ │ + delete this.startEvt; │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ + } │ │ │ │ + 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 │ │ │ │ }) │ │ │ │ } │ │ │ │ - this._resized = true │ │ │ │ } │ │ │ │ }, │ │ │ │ - setGMapVisibility: function(visible) { │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - var container = this.mapObject.getContainer(); │ │ │ │ - if (visible === true) { │ │ │ │ - this.mapObject.setMapType(this.type); │ │ │ │ - container.style.display = ""; │ │ │ │ - this.termsOfUse.style.left = ""; │ │ │ │ - this.termsOfUse.style.display = ""; │ │ │ │ - this.poweredBy.style.display = ""; │ │ │ │ - cache.displayed = this.id │ │ │ │ - } else { │ │ │ │ - if (cache.displayed === this.id) { │ │ │ │ - delete cache.displayed │ │ │ │ - } │ │ │ │ - if (!cache.displayed) { │ │ │ │ - container.style.display = "none"; │ │ │ │ - this.termsOfUse.style.display = "none"; │ │ │ │ - this.termsOfUse.style.left = "-9999px"; │ │ │ │ - this.poweredBy.style.display = "none" │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + 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 { │ │ │ │ + delete this.cache[id] │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - getMapContainer: function() { │ │ │ │ - return this.mapObject.getContainer() │ │ │ │ - }, │ │ │ │ - getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ - var moBounds = null; │ │ │ │ - if (olBounds != null) { │ │ │ │ - var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ - var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ - moBounds = new GLatLngBounds(new GLatLng(sw.lat, sw.lon), new GLatLng(ne.lat, ne.lon)) │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ + } │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ } │ │ │ │ - return moBounds │ │ │ │ - }, │ │ │ │ - setMapObjectCenter: function(center, zoom) { │ │ │ │ - this.mapObject.setCenter(center, zoom) │ │ │ │ - }, │ │ │ │ - dragPanMapObject: function(dX, dY) { │ │ │ │ - this.dragObject.moveBy(new GSize(-dX, dY)) │ │ │ │ - }, │ │ │ │ - getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ - return this.mapObject.fromContainerPixelToLatLng(moPixel) │ │ │ │ - }, │ │ │ │ - getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - return this.mapObject.fromLatLngToContainerPixel(moLonLat) │ │ │ │ }, │ │ │ │ - getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ - return this.mapObject.getBoundsZoomLevel(moBounds) │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ }, │ │ │ │ - getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ - var gLatLng; │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - var lonlat = this.inverseMercator(lon, lat); │ │ │ │ - gLatLng = new GLatLng(lonlat.lat, lonlat.lon) │ │ │ │ - } else { │ │ │ │ - gLatLng = new GLatLng(lat, lon) │ │ │ │ + getFeatures: function(evt) { │ │ │ │ + var x = evt.clientX, │ │ │ │ + y = evt.clientY, │ │ │ │ + features = [], │ │ │ │ + targets = [], │ │ │ │ + layers = [], │ │ │ │ + layer, target, feature, i, len; │ │ │ │ + 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 { │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - return gLatLng │ │ │ │ + for (i = 0, len = targets.length; i < len; ++i) { │ │ │ │ + targets[i].style.display = "" │ │ │ │ + } │ │ │ │ + for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ + layers[i].div.style.display = "block" │ │ │ │ + } │ │ │ │ + return features │ │ │ │ }, │ │ │ │ - getMapObjectPixelFromXY: function(x, y) { │ │ │ │ - return new GPoint(x, y) │ │ │ │ + destroy: function() { │ │ │ │ + for (var i = this.provides.length - 1; i >= 0; --i) { │ │ │ │ + delete this.target.extensions[this.provides[i]] │ │ │ │ + } │ │ │ │ + this.map.events.un({ │ │ │ │ + mousemove: this.onMousemove, │ │ │ │ + mousedown: this.start, │ │ │ │ + mouseup: this.onClick, │ │ │ │ + touchstart: this.start, │ │ │ │ + touchmove: this.cancel, │ │ │ │ + touchend: this.onClick, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + delete this.cache; │ │ │ │ + delete this.map; │ │ │ │ + delete this.target │ │ │ │ } │ │ │ │ -}; │ │ │ │ -OpenLayers.Layer.WorldWind = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - DEFAULT_PARAMS: {}, │ │ │ │ - isBaseLayer: true, │ │ │ │ - lzd: null, │ │ │ │ - zoomLevels: null, │ │ │ │ - initialize: function(name, url, lzd, zoomLevels, params, options) { │ │ │ │ - this.lzd = lzd; │ │ │ │ - this.zoomLevels = zoomLevels; │ │ │ │ - var newArguments = []; │ │ │ │ - newArguments.push(name, url, params, options); │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ - this.params = OpenLayers.Util.applyDefaults(this.params, this.DEFAULT_PARAMS) │ │ │ │ - }, │ │ │ │ - getZoom: function() { │ │ │ │ - var zoom = this.map.getZoom(); │ │ │ │ - var extent = this.map.getMaxExtent(); │ │ │ │ - zoom = zoom - Math.log(this.maxResolution / (this.lzd / 512)) / Math.log(2); │ │ │ │ - return zoom │ │ │ │ - }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var zoom = this.getZoom(); │ │ │ │ - var extent = this.map.getMaxExtent(); │ │ │ │ - var deg = this.lzd / Math.pow(2, this.getZoom()); │ │ │ │ - var x = Math.floor((bounds.left - extent.left) / deg); │ │ │ │ - var y = Math.floor((bounds.bottom - extent.bottom) / deg); │ │ │ │ - if (this.map.getResolution() <= this.lzd / 512 && this.getZoom() <= this.zoomLevels) { │ │ │ │ - return this.getFullRequestString({ │ │ │ │ - L: zoom, │ │ │ │ - X: x, │ │ │ │ - Y: y │ │ │ │ +}); │ │ │ │ +OpenLayers.Events.nofeatureclick = OpenLayers.Events.featureclick; │ │ │ │ +OpenLayers.Events.featureover = OpenLayers.Events.featureclick; │ │ │ │ +OpenLayers.Events.featureout = OpenLayers.Events.featureclick; │ │ │ │ +OpenLayers.Events.buttonclick = OpenLayers.Class({ │ │ │ │ + target: null, │ │ │ │ + events: ["mousedown", "mouseup", "click", "dblclick", "touchstart", "touchmove", "touchend", "keydown"], │ │ │ │ + startRegEx: /^mousedown|touchstart$/, │ │ │ │ + cancelRegEx: /^touchmove$/, │ │ │ │ + completeRegEx: /^mouseup|touchend$/, │ │ │ │ + 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 │ │ │ │ }) │ │ │ │ - } else { │ │ │ │ - return OpenLayers.Util.getImageLocation("blank.gif") │ │ │ │ } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.WorldWind" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - isBaseLayer: true, │ │ │ │ - format: "image/png", │ │ │ │ - serverResolutions: null, │ │ │ │ - initialize: function(name, url, layername, options) { │ │ │ │ - this.layername = layername; │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name, url, {}, options]); │ │ │ │ - this.extension = this.format.split("/")[1].toLowerCase(); │ │ │ │ - this.extension = this.extension == "jpg" ? "jpeg" : this.extension │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.TileCache(this.name, this.url, this.layername, this.getOptions()) │ │ │ │ + destroy: function() { │ │ │ │ + for (var i = this.events.length - 1; i >= 0; --i) { │ │ │ │ + this.target.unregister(this.events[i], this, this.buttonClick) │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ + delete this.target │ │ │ │ }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var bbox = this.maxExtent; │ │ │ │ - var size = this.tileSize; │ │ │ │ - var tileX = Math.round((bounds.left - bbox.left) / (res * size.w)); │ │ │ │ - var tileY = Math.round((bounds.bottom - bbox.bottom) / (res * size.h)); │ │ │ │ - var tileZ = this.serverResolutions != null ? OpenLayers.Util.indexOf(this.serverResolutions, res) : this.map.getZoom(); │ │ │ │ - var components = [this.layername, OpenLayers.Number.zeroPad(tileZ, 2), OpenLayers.Number.zeroPad(parseInt(tileX / 1e6), 3), OpenLayers.Number.zeroPad(parseInt(tileX / 1e3) % 1e3, 3), OpenLayers.Number.zeroPad(parseInt(tileX) % 1e3, 3), OpenLayers.Number.zeroPad(parseInt(tileY / 1e6), 3), OpenLayers.Number.zeroPad(parseInt(tileY / 1e3) % 1e3, 3), OpenLayers.Number.zeroPad(parseInt(tileY) % 1e3, 3) + "." + this.extension]; │ │ │ │ - var path = components.join("/"); │ │ │ │ - var url = this.url; │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(path, url) │ │ │ │ - } │ │ │ │ - url = url.charAt(url.length - 1) == "/" ? url : url + "/"; │ │ │ │ - return url + path │ │ │ │ + getPressedButton: function(element) { │ │ │ │ + var depth = 3, │ │ │ │ + button; │ │ │ │ + do { │ │ │ │ + if (OpenLayers.Element.hasClass(element, "olButton")) { │ │ │ │ + button = element; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + element = element.parentNode │ │ │ │ + } while (--depth > 0 && element); │ │ │ │ + return button │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.TileCache" │ │ │ │ -}); │ │ │ │ -OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, { │ │ │ │ - defaultStyle: null, │ │ │ │ - extractStyles: true, │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - if (options.extractStyles !== false) { │ │ │ │ - options.defaultStyle = { │ │ │ │ - externalGraphic: OpenLayers.Util.getImageLocation("marker.png"), │ │ │ │ - graphicWidth: 21, │ │ │ │ - graphicHeight: 25, │ │ │ │ - graphicXOffset: -10.5, │ │ │ │ - graphicYOffset: -12.5 │ │ │ │ + ignore: function(element) { │ │ │ │ + var depth = 3, │ │ │ │ + ignore = false; │ │ │ │ + do { │ │ │ │ + if (element.nodeName.toLowerCase() === "a") { │ │ │ │ + ignore = true; │ │ │ │ + break │ │ │ │ } │ │ │ │ - } │ │ │ │ - OpenLayers.Format.prototype.initialize.apply(this, [options]) │ │ │ │ + element = element.parentNode │ │ │ │ + } while (--depth > 0 && element); │ │ │ │ + return ignore │ │ │ │ }, │ │ │ │ - read: function(text) { │ │ │ │ - var lines = text.split("\n"); │ │ │ │ - var columns; │ │ │ │ - var features = []; │ │ │ │ - for (var lcv = 0; lcv < lines.length - 1; lcv++) { │ │ │ │ - var currLine = lines[lcv].replace(/^\s*/, "").replace(/\s*$/, ""); │ │ │ │ - if (currLine.charAt(0) != "#") { │ │ │ │ - if (!columns) { │ │ │ │ - columns = currLine.split("\t") │ │ │ │ - } else { │ │ │ │ - var vals = currLine.split("\t"); │ │ │ │ - var geometry = new OpenLayers.Geometry.Point(0, 0); │ │ │ │ - var attributes = {}; │ │ │ │ - var style = this.defaultStyle ? OpenLayers.Util.applyDefaults({}, this.defaultStyle) : null; │ │ │ │ - var icon, iconSize, iconOffset, overflow; │ │ │ │ - var set = false; │ │ │ │ - for (var valIndex = 0; valIndex < vals.length; valIndex++) { │ │ │ │ - if (vals[valIndex]) { │ │ │ │ - if (columns[valIndex] == "point") { │ │ │ │ - var coords = vals[valIndex].split(","); │ │ │ │ - geometry.y = parseFloat(coords[0]); │ │ │ │ - geometry.x = parseFloat(coords[1]); │ │ │ │ - set = true │ │ │ │ - } else if (columns[valIndex] == "lat") { │ │ │ │ - geometry.y = parseFloat(vals[valIndex]); │ │ │ │ - set = true │ │ │ │ - } else if (columns[valIndex] == "lon") { │ │ │ │ - geometry.x = parseFloat(vals[valIndex]); │ │ │ │ - set = true │ │ │ │ - } else if (columns[valIndex] == "title") attributes["title"] = vals[valIndex]; │ │ │ │ - else if (columns[valIndex] == "image" || columns[valIndex] == "icon" && style) { │ │ │ │ - style["externalGraphic"] = vals[valIndex] │ │ │ │ - } else if (columns[valIndex] == "iconSize" && style) { │ │ │ │ - var size = vals[valIndex].split(","); │ │ │ │ - style["graphicWidth"] = parseFloat(size[0]); │ │ │ │ - style["graphicHeight"] = parseFloat(size[1]) │ │ │ │ - } else if (columns[valIndex] == "iconOffset" && style) { │ │ │ │ - var offset = vals[valIndex].split(","); │ │ │ │ - style["graphicXOffset"] = parseFloat(offset[0]); │ │ │ │ - style["graphicYOffset"] = parseFloat(offset[1]) │ │ │ │ - } else if (columns[valIndex] == "description") { │ │ │ │ - attributes["description"] = vals[valIndex] │ │ │ │ - } else if (columns[valIndex] == "overflow") { │ │ │ │ - attributes["overflow"] = vals[valIndex] │ │ │ │ - } else { │ │ │ │ - attributes[columns[valIndex]] = vals[valIndex] │ │ │ │ + buttonClick: function(evt) { │ │ │ │ + var propagate = true, │ │ │ │ + element = OpenLayers.Event.element(evt); │ │ │ │ + if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) { │ │ │ │ + var button = this.getPressedButton(element); │ │ │ │ + if (button) { │ │ │ │ + if (evt.type === "keydown") { │ │ │ │ + switch (evt.keyCode) { │ │ │ │ + case OpenLayers.Event.KEY_RETURN: │ │ │ │ + case OpenLayers.Event.KEY_SPACE: │ │ │ │ + this.target.triggerEvent("buttonclick", { │ │ │ │ + buttonElement: button │ │ │ │ + }); │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + propagate = false; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } else if (this.startEvt) { │ │ │ │ + if (this.completeRegEx.test(evt.type)) { │ │ │ │ + var pos = OpenLayers.Util.pagePosition(button); │ │ │ │ + var viewportElement = OpenLayers.Util.getViewportElement(); │ │ │ │ + var scrollTop = window.pageYOffset || viewportElement.scrollTop; │ │ │ │ + var scrollLeft = window.pageXOffset || viewportElement.scrollLeft; │ │ │ │ + pos[0] = pos[0] - scrollLeft; │ │ │ │ + pos[1] = pos[1] - scrollTop; │ │ │ │ + this.target.triggerEvent("buttonclick", { │ │ │ │ + buttonElement: button, │ │ │ │ + buttonXY: { │ │ │ │ + x: this.startEvt.clientX - pos[0], │ │ │ │ + y: this.startEvt.clientY - pos[1] │ │ │ │ } │ │ │ │ - } │ │ │ │ + }) │ │ │ │ } │ │ │ │ - if (set) { │ │ │ │ - if (this.internalProjection && this.externalProjection) { │ │ │ │ - geometry.transform(this.externalProjection, this.internalProjection) │ │ │ │ - } │ │ │ │ - var feature = new OpenLayers.Feature.Vector(geometry, attributes, style); │ │ │ │ - features.push(feature) │ │ │ │ + if (this.cancelRegEx.test(evt.type)) { │ │ │ │ + delete this.startEvt │ │ │ │ } │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + propagate = false │ │ │ │ + } │ │ │ │ + if (this.startRegEx.test(evt.type)) { │ │ │ │ + this.startEvt = evt; │ │ │ │ + OpenLayers.Event.stop(evt); │ │ │ │ + propagate = false │ │ │ │ } │ │ │ │ + } else { │ │ │ │ + propagate = !this.ignore(OpenLayers.Event.element(evt)); │ │ │ │ + delete this.startEvt │ │ │ │ } │ │ │ │ } │ │ │ │ - return features │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Format.Text" │ │ │ │ + return propagate │ │ │ │ + } │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.Text = OpenLayers.Class(OpenLayers.Layer.Markers, { │ │ │ │ - location: null, │ │ │ │ - features: null, │ │ │ │ - formatOptions: null, │ │ │ │ - selectedFeature: null, │ │ │ │ - initialize: function(name, options) { │ │ │ │ - OpenLayers.Layer.Markers.prototype.initialize.apply(this, arguments); │ │ │ │ - this.features = [] │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Layer.Markers.prototype.destroy.apply(this, arguments); │ │ │ │ - this.clearFeatures(); │ │ │ │ - this.features = null │ │ │ │ - }, │ │ │ │ - loadText: function() { │ │ │ │ - if (!this.loaded) { │ │ │ │ - if (this.location != null) { │ │ │ │ - var onFail = function(e) { │ │ │ │ - this.events.triggerEvent("loadend") │ │ │ │ - }; │ │ │ │ - this.events.triggerEvent("loadstart"); │ │ │ │ - OpenLayers.Request.GET({ │ │ │ │ - url: this.location, │ │ │ │ - success: this.parseData, │ │ │ │ - failure: onFail, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.loaded = true │ │ │ │ - } │ │ │ │ +OpenLayers.Strategy.Filter = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ + filter: null, │ │ │ │ + cache: null, │ │ │ │ + caching: false, │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); │ │ │ │ + if (activated) { │ │ │ │ + this.cache = []; │ │ │ │ + this.layer.events.on({ │ │ │ │ + beforefeaturesadded: this.handleAdd, │ │ │ │ + beforefeaturesremoved: this.handleRemove, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ } │ │ │ │ + return activated │ │ │ │ }, │ │ │ │ - moveTo: function(bounds, zoomChanged, minor) { │ │ │ │ - OpenLayers.Layer.Markers.prototype.moveTo.apply(this, arguments); │ │ │ │ - if (this.visibility && !this.loaded) { │ │ │ │ - this.loadText() │ │ │ │ + deactivate: function() { │ │ │ │ + this.cache = null; │ │ │ │ + if (this.layer && this.layer.events) { │ │ │ │ + this.layer.events.un({ │ │ │ │ + beforefeaturesadded: this.handleAdd, │ │ │ │ + beforefeaturesremoved: this.handleRemove, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ } │ │ │ │ + return OpenLayers.Strategy.prototype.deactivate.apply(this, arguments) │ │ │ │ }, │ │ │ │ - parseData: function(ajaxRequest) { │ │ │ │ - var text = ajaxRequest.responseText; │ │ │ │ - var options = {}; │ │ │ │ - OpenLayers.Util.extend(options, this.formatOptions); │ │ │ │ - if (this.map && !this.projection.equals(this.map.getProjectionObject())) { │ │ │ │ - options.externalProjection = this.projection; │ │ │ │ - options.internalProjection = this.map.getProjectionObject() │ │ │ │ - } │ │ │ │ - var parser = new OpenLayers.Format.Text(options); │ │ │ │ - var features = parser.read(text); │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - var data = {}; │ │ │ │ - var feature = features[i]; │ │ │ │ - var location; │ │ │ │ - var iconSize, iconOffset; │ │ │ │ - location = new OpenLayers.LonLat(feature.geometry.x, feature.geometry.y); │ │ │ │ - if (feature.style.graphicWidth && feature.style.graphicHeight) { │ │ │ │ - iconSize = new OpenLayers.Size(feature.style.graphicWidth, feature.style.graphicHeight) │ │ │ │ - } │ │ │ │ - if (feature.style.graphicXOffset !== undefined && feature.style.graphicYOffset !== undefined) { │ │ │ │ - iconOffset = new OpenLayers.Pixel(feature.style.graphicXOffset, feature.style.graphicYOffset) │ │ │ │ - } │ │ │ │ - if (feature.style.externalGraphic != null) { │ │ │ │ - data.icon = new OpenLayers.Icon(feature.style.externalGraphic, iconSize, iconOffset) │ │ │ │ - } else { │ │ │ │ - data.icon = OpenLayers.Marker.defaultIcon(); │ │ │ │ - if (iconSize != null) { │ │ │ │ - data.icon.setSize(iconSize) │ │ │ │ + handleAdd: function(event) { │ │ │ │ + if (!this.caching && this.filter) { │ │ │ │ + var features = event.features; │ │ │ │ + event.features = []; │ │ │ │ + var feature; │ │ │ │ + for (var i = 0, ii = features.length; i < ii; ++i) { │ │ │ │ + feature = features[i]; │ │ │ │ + if (this.filter.evaluate(feature)) { │ │ │ │ + event.features.push(feature) │ │ │ │ + } else { │ │ │ │ + this.cache.push(feature) │ │ │ │ } │ │ │ │ } │ │ │ │ - if (feature.attributes.title != null && feature.attributes.description != null) { │ │ │ │ - data["popupContentHTML"] = "<h2>" + feature.attributes.title + "</h2>" + "<p>" + feature.attributes.description + "</p>" │ │ │ │ - } │ │ │ │ - data["overflow"] = feature.attributes.overflow || "auto"; │ │ │ │ - var markerFeature = new OpenLayers.Feature(this, location, data); │ │ │ │ - this.features.push(markerFeature); │ │ │ │ - var marker = markerFeature.createMarker(); │ │ │ │ - if (feature.attributes.title != null && feature.attributes.description != null) { │ │ │ │ - marker.events.register("click", markerFeature, this.markerClick) │ │ │ │ - } │ │ │ │ - this.addMarker(marker) │ │ │ │ } │ │ │ │ - this.events.triggerEvent("loadend") │ │ │ │ }, │ │ │ │ - markerClick: function(evt) { │ │ │ │ - var sameMarkerClicked = this == this.layer.selectedFeature; │ │ │ │ - this.layer.selectedFeature = !sameMarkerClicked ? this : null; │ │ │ │ - for (var i = 0, len = this.layer.map.popups.length; i < len; i++) { │ │ │ │ - this.layer.map.removePopup(this.layer.map.popups[i]) │ │ │ │ - } │ │ │ │ - if (!sameMarkerClicked) { │ │ │ │ - this.layer.map.addPopup(this.createPopup()) │ │ │ │ + handleRemove: function(event) { │ │ │ │ + if (!this.caching) { │ │ │ │ + this.cache = [] │ │ │ │ } │ │ │ │ - OpenLayers.Event.stop(evt) │ │ │ │ }, │ │ │ │ - clearFeatures: function() { │ │ │ │ - if (this.features != null) { │ │ │ │ - while (this.features.length > 0) { │ │ │ │ - var feature = this.features[0]; │ │ │ │ - OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ - feature.destroy() │ │ │ │ + setFilter: function(filter) { │ │ │ │ + this.filter = filter; │ │ │ │ + var previousCache = this.cache; │ │ │ │ + this.cache = []; │ │ │ │ + this.handleAdd({ │ │ │ │ + features: this.layer.features │ │ │ │ + }); │ │ │ │ + if (this.cache.length > 0) { │ │ │ │ + this.caching = true; │ │ │ │ + this.layer.removeFeatures(this.cache.slice()); │ │ │ │ + this.caching = false │ │ │ │ + } │ │ │ │ + if (previousCache.length > 0) { │ │ │ │ + var event = { │ │ │ │ + features: previousCache │ │ │ │ + }; │ │ │ │ + this.handleAdd(event); │ │ │ │ + if (event.features.length > 0) { │ │ │ │ + this.caching = true; │ │ │ │ + this.layer.addFeatures(event.features); │ │ │ │ + this.caching = false │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Text" │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Filter" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.Boxes = OpenLayers.Class(OpenLayers.Layer.Markers, { │ │ │ │ - drawMarker: function(marker) { │ │ │ │ - var topleft = this.map.getLayerPxFromLonLat({ │ │ │ │ - lon: marker.bounds.left, │ │ │ │ - lat: marker.bounds.top │ │ │ │ - }); │ │ │ │ - var botright = this.map.getLayerPxFromLonLat({ │ │ │ │ - lon: marker.bounds.right, │ │ │ │ - lat: marker.bounds.bottom │ │ │ │ - }); │ │ │ │ - if (botright == null || topleft == null) { │ │ │ │ - marker.display(false) │ │ │ │ - } else { │ │ │ │ - var markerDiv = marker.draw(topleft, { │ │ │ │ - w: Math.max(1, botright.x - topleft.x), │ │ │ │ - h: Math.max(1, botright.y - topleft.y) │ │ │ │ - }); │ │ │ │ - if (!marker.drawn) { │ │ │ │ - this.div.appendChild(markerDiv); │ │ │ │ - marker.drawn = true │ │ │ │ +OpenLayers.Strategy.Save = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ + events: null, │ │ │ │ + auto: false, │ │ │ │ + timer: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Strategy.prototype.initialize.apply(this, [options]); │ │ │ │ + this.events = new OpenLayers.Events(this) │ │ │ │ + }, │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + if (this.auto) { │ │ │ │ + if (typeof this.auto === "number") { │ │ │ │ + this.timer = window.setInterval(OpenLayers.Function.bind(this.save, this), this.auto * 1e3) │ │ │ │ + } else { │ │ │ │ + this.layer.events.on({ │ │ │ │ + featureadded: this.triggerSave, │ │ │ │ + afterfeaturemodified: this.triggerSave, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ + return activated │ │ │ │ }, │ │ │ │ - removeMarker: function(marker) { │ │ │ │ - OpenLayers.Util.removeItem(this.markers, marker); │ │ │ │ - if (marker.div != null && marker.div.parentNode == this.div) { │ │ │ │ - this.div.removeChild(marker.div) │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + if (this.auto) { │ │ │ │ + if (typeof this.auto === "number") { │ │ │ │ + window.clearInterval(this.timer) │ │ │ │ + } else { │ │ │ │ + this.layer.events.un({ │ │ │ │ + featureadded: this.triggerSave, │ │ │ │ + afterfeaturemodified: this.triggerSave, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + return deactivated │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Boxes" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.Vector = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ - isBaseLayer: false, │ │ │ │ - isFixed: false, │ │ │ │ - features: null, │ │ │ │ - filter: null, │ │ │ │ - selectedFeatures: null, │ │ │ │ - unrenderedFeatures: null, │ │ │ │ - reportError: true, │ │ │ │ - style: null, │ │ │ │ - styleMap: null, │ │ │ │ - strategies: null, │ │ │ │ - protocol: null, │ │ │ │ - renderers: ["SVG", "VML", "Canvas"], │ │ │ │ - renderer: null, │ │ │ │ - rendererOptions: null, │ │ │ │ - geometryType: null, │ │ │ │ - drawn: false, │ │ │ │ - ratio: 1, │ │ │ │ - initialize: function(name, options) { │ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, arguments); │ │ │ │ - if (!this.renderer || !this.renderer.supported()) { │ │ │ │ - this.assignRenderer() │ │ │ │ - } │ │ │ │ - if (!this.renderer || !this.renderer.supported()) { │ │ │ │ - this.renderer = null; │ │ │ │ - this.displayError() │ │ │ │ + triggerSave: function(event) { │ │ │ │ + var feature = event.feature; │ │ │ │ + if (feature.state === OpenLayers.State.INSERT || feature.state === OpenLayers.State.UPDATE || feature.state === OpenLayers.State.DELETE) { │ │ │ │ + this.save([event.feature]) │ │ │ │ } │ │ │ │ - if (!this.styleMap) { │ │ │ │ - this.styleMap = new OpenLayers.StyleMap │ │ │ │ + }, │ │ │ │ + save: function(features) { │ │ │ │ + if (!features) { │ │ │ │ + features = this.layer.features │ │ │ │ } │ │ │ │ - this.features = []; │ │ │ │ - this.selectedFeatures = []; │ │ │ │ - this.unrenderedFeatures = {}; │ │ │ │ - if (this.strategies) { │ │ │ │ - for (var i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - this.strategies[i].setLayer(this) │ │ │ │ + this.events.triggerEvent("start", { │ │ │ │ + features: features │ │ │ │ + }); │ │ │ │ + var remote = this.layer.projection; │ │ │ │ + var local = this.layer.map.getProjectionObject(); │ │ │ │ + if (!local.equals(remote)) { │ │ │ │ + var len = features.length; │ │ │ │ + var clones = new Array(len); │ │ │ │ + var orig, clone; │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ + orig = features[i]; │ │ │ │ + clone = orig.clone(); │ │ │ │ + clone.fid = orig.fid; │ │ │ │ + clone.state = orig.state; │ │ │ │ + if (orig.url) { │ │ │ │ + clone.url = orig.url │ │ │ │ + } │ │ │ │ + clone._original = orig; │ │ │ │ + clone.geometry.transform(local, remote); │ │ │ │ + clones[i] = clone │ │ │ │ } │ │ │ │ + features = clones │ │ │ │ } │ │ │ │ + this.layer.protocol.commit(features, { │ │ │ │ + callback: this.onCommit, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.strategies) { │ │ │ │ - var strategy, i, len; │ │ │ │ - for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - strategy = this.strategies[i]; │ │ │ │ - if (strategy.autoDestroy) { │ │ │ │ - strategy.destroy() │ │ │ │ + onCommit: function(response) { │ │ │ │ + var evt = { │ │ │ │ + response: response │ │ │ │ + }; │ │ │ │ + if (response.success()) { │ │ │ │ + var features = response.reqFeatures; │ │ │ │ + var state, feature; │ │ │ │ + var destroys = []; │ │ │ │ + var insertIds = response.insertIds || []; │ │ │ │ + var j = 0; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + feature = features[i]; │ │ │ │ + feature = feature._original || feature; │ │ │ │ + state = feature.state; │ │ │ │ + if (state) { │ │ │ │ + if (state == OpenLayers.State.DELETE) { │ │ │ │ + destroys.push(feature) │ │ │ │ + } else if (state == OpenLayers.State.INSERT) { │ │ │ │ + feature.fid = insertIds[j]; │ │ │ │ + ++j │ │ │ │ + } │ │ │ │ + feature.state = null │ │ │ │ } │ │ │ │ } │ │ │ │ - this.strategies = null │ │ │ │ - } │ │ │ │ - if (this.protocol) { │ │ │ │ - if (this.protocol.autoDestroy) { │ │ │ │ - this.protocol.destroy() │ │ │ │ + if (destroys.length > 0) { │ │ │ │ + this.layer.destroyFeatures(destroys) │ │ │ │ } │ │ │ │ - this.protocol = null │ │ │ │ + this.events.triggerEvent("success", evt) │ │ │ │ + } else { │ │ │ │ + this.events.triggerEvent("fail", evt) │ │ │ │ } │ │ │ │ - this.destroyFeatures(); │ │ │ │ - this.features = null; │ │ │ │ - this.selectedFeatures = null; │ │ │ │ - this.unrenderedFeatures = null; │ │ │ │ - if (this.renderer) { │ │ │ │ - this.renderer.destroy() │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Save" │ │ │ │ +}); │ │ │ │ +OpenLayers.Strategy.BBOX = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ + bounds: null, │ │ │ │ + resolution: null, │ │ │ │ + ratio: 2, │ │ │ │ + resFactor: null, │ │ │ │ + response: null, │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + moveend: this.update, │ │ │ │ + refresh: this.update, │ │ │ │ + visibilitychanged: this.update, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + this.update() │ │ │ │ } │ │ │ │ - this.renderer = null; │ │ │ │ - this.geometryType = null; │ │ │ │ - this.drawn = null; │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments) │ │ │ │ + return activated │ │ │ │ }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Vector(this.name, this.getOptions()) │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.layer.events.un({ │ │ │ │ + moveend: this.update, │ │ │ │ + refresh: this.update, │ │ │ │ + visibilitychanged: this.update, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ - var features = this.features; │ │ │ │ - var len = features.length; │ │ │ │ - var clonedFeatures = new Array(len); │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ - clonedFeatures[i] = features[i].clone() │ │ │ │ + return deactivated │ │ │ │ + }, │ │ │ │ + update: function(options) { │ │ │ │ + var mapBounds = this.getMapBounds(); │ │ │ │ + if (mapBounds !== null && (options && options.force || this.layer.visibility && this.layer.calculateInRange() && this.invalidBounds(mapBounds))) { │ │ │ │ + this.calculateBounds(mapBounds); │ │ │ │ + this.resolution = this.layer.map.getResolution(); │ │ │ │ + this.triggerRead(options) │ │ │ │ } │ │ │ │ - obj.features = clonedFeatures; │ │ │ │ - return obj │ │ │ │ }, │ │ │ │ - refresh: function(obj) { │ │ │ │ - if (this.calculateInRange() && this.visibility) { │ │ │ │ - this.events.triggerEvent("refresh", obj) │ │ │ │ + getMapBounds: function() { │ │ │ │ + if (this.layer.map === null) { │ │ │ │ + return null │ │ │ │ + } │ │ │ │ + var bounds = this.layer.map.getExtent(); │ │ │ │ + if (bounds && !this.layer.projection.equals(this.layer.map.getProjectionObject())) { │ │ │ │ + bounds = bounds.clone().transform(this.layer.map.getProjectionObject(), this.layer.projection) │ │ │ │ } │ │ │ │ + return bounds │ │ │ │ }, │ │ │ │ - assignRenderer: function() { │ │ │ │ - for (var i = 0, len = this.renderers.length; i < len; i++) { │ │ │ │ - var rendererClass = this.renderers[i]; │ │ │ │ - var renderer = typeof rendererClass == "function" ? rendererClass : OpenLayers.Renderer[rendererClass]; │ │ │ │ - if (renderer && renderer.prototype.supported()) { │ │ │ │ - this.renderer = new renderer(this.div, this.rendererOptions); │ │ │ │ - break │ │ │ │ - } │ │ │ │ + invalidBounds: function(mapBounds) { │ │ │ │ + if (!mapBounds) { │ │ │ │ + mapBounds = this.getMapBounds() │ │ │ │ + } │ │ │ │ + var invalid = !this.bounds || !this.bounds.containsBounds(mapBounds); │ │ │ │ + if (!invalid && this.resFactor) { │ │ │ │ + var ratio = this.resolution / this.layer.map.getResolution(); │ │ │ │ + invalid = ratio >= this.resFactor || ratio <= 1 / this.resFactor │ │ │ │ } │ │ │ │ + return invalid │ │ │ │ }, │ │ │ │ - displayError: function() { │ │ │ │ - if (this.reportError) { │ │ │ │ - OpenLayers.Console.userError(OpenLayers.i18n("browserNotSupported", { │ │ │ │ - renderers: this.renderers.join("\n") │ │ │ │ - })) │ │ │ │ + calculateBounds: function(mapBounds) { │ │ │ │ + if (!mapBounds) { │ │ │ │ + mapBounds = this.getMapBounds() │ │ │ │ } │ │ │ │ + var center = mapBounds.getCenterLonLat(); │ │ │ │ + var dataWidth = mapBounds.getWidth() * this.ratio; │ │ │ │ + var dataHeight = mapBounds.getHeight() * this.ratio; │ │ │ │ + this.bounds = new OpenLayers.Bounds(center.lon - dataWidth / 2, center.lat - dataHeight / 2, center.lon + dataWidth / 2, center.lat + dataHeight / 2) │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.prototype.setMap.apply(this, arguments); │ │ │ │ - if (!this.renderer) { │ │ │ │ - this.map.removeLayer(this) │ │ │ │ - } else { │ │ │ │ - this.renderer.map = this.map; │ │ │ │ - var newSize = this.map.getSize(); │ │ │ │ - newSize.w = newSize.w * this.ratio; │ │ │ │ - newSize.h = newSize.h * this.ratio; │ │ │ │ - this.renderer.setSize(newSize) │ │ │ │ + triggerRead: function(options) { │ │ │ │ + if (this.response && !(options && options.noAbort === true)) { │ │ │ │ + this.layer.protocol.abort(this.response); │ │ │ │ + this.layer.events.triggerEvent("loadend") │ │ │ │ } │ │ │ │ + var evt = { │ │ │ │ + filter: this.createFilter() │ │ │ │ + }; │ │ │ │ + this.layer.events.triggerEvent("loadstart", evt); │ │ │ │ + this.response = this.layer.protocol.read(OpenLayers.Util.applyDefaults({ │ │ │ │ + filter: evt.filter, │ │ │ │ + callback: this.merge, │ │ │ │ + scope: this │ │ │ │ + }, options)) │ │ │ │ }, │ │ │ │ - afterAdd: function() { │ │ │ │ - if (this.strategies) { │ │ │ │ - var strategy, i, len; │ │ │ │ - for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - strategy = this.strategies[i]; │ │ │ │ - if (strategy.autoActivate) { │ │ │ │ - strategy.activate() │ │ │ │ - } │ │ │ │ - } │ │ │ │ + createFilter: function() { │ │ │ │ + var filter = new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ + value: this.bounds, │ │ │ │ + projection: this.layer.projection │ │ │ │ + }); │ │ │ │ + if (this.layer.filter) { │ │ │ │ + filter = new OpenLayers.Filter.Logical({ │ │ │ │ + type: OpenLayers.Filter.Logical.AND, │ │ │ │ + filters: [this.layer.filter, filter] │ │ │ │ + }) │ │ │ │ } │ │ │ │ + return filter │ │ │ │ }, │ │ │ │ - removeMap: function(map) { │ │ │ │ - this.drawn = false; │ │ │ │ - if (this.strategies) { │ │ │ │ - var strategy, i, len; │ │ │ │ - for (i = 0, len = this.strategies.length; i < len; i++) { │ │ │ │ - strategy = this.strategies[i]; │ │ │ │ - if (strategy.autoActivate) { │ │ │ │ - strategy.deactivate() │ │ │ │ + merge: function(resp) { │ │ │ │ + this.layer.destroyFeatures(); │ │ │ │ + if (resp.success()) { │ │ │ │ + var features = resp.features; │ │ │ │ + if (features && features.length > 0) { │ │ │ │ + var remote = this.layer.projection; │ │ │ │ + var local = this.layer.map.getProjectionObject(); │ │ │ │ + if (!local.equals(remote)) { │ │ │ │ + var geom; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + geom = features[i].geometry; │ │ │ │ + if (geom) { │ │ │ │ + geom.transform(remote, local) │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + this.layer.addFeatures(features) │ │ │ │ } │ │ │ │ + } else { │ │ │ │ + this.bounds = null │ │ │ │ } │ │ │ │ + this.response = null; │ │ │ │ + this.layer.events.triggerEvent("loadend", { │ │ │ │ + response: resp │ │ │ │ + }) │ │ │ │ }, │ │ │ │ - onMapResize: function() { │ │ │ │ - OpenLayers.Layer.prototype.onMapResize.apply(this, arguments); │ │ │ │ - var newSize = this.map.getSize(); │ │ │ │ - newSize.w = newSize.w * this.ratio; │ │ │ │ - newSize.h = newSize.h * this.ratio; │ │ │ │ - this.renderer.setSize(newSize) │ │ │ │ - }, │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ - var coordSysUnchanged = true; │ │ │ │ - if (!dragging) { │ │ │ │ - this.renderer.root.style.visibility = "hidden"; │ │ │ │ - var viewSize = this.map.getSize(), │ │ │ │ - viewWidth = viewSize.w, │ │ │ │ - viewHeight = viewSize.h, │ │ │ │ - offsetLeft = viewWidth / 2 * this.ratio - viewWidth / 2, │ │ │ │ - offsetTop = viewHeight / 2 * this.ratio - viewHeight / 2; │ │ │ │ - offsetLeft += this.map.layerContainerOriginPx.x; │ │ │ │ - offsetLeft = -Math.round(offsetLeft); │ │ │ │ - offsetTop += this.map.layerContainerOriginPx.y; │ │ │ │ - offsetTop = -Math.round(offsetTop); │ │ │ │ - this.div.style.left = offsetLeft + "px"; │ │ │ │ - this.div.style.top = offsetTop + "px"; │ │ │ │ - var extent = this.map.getExtent().scale(this.ratio); │ │ │ │ - coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged); │ │ │ │ - this.renderer.root.style.visibility = "visible"; │ │ │ │ - if (OpenLayers.IS_GECKO === true) { │ │ │ │ - this.div.scrollLeft = this.div.scrollLeft │ │ │ │ - } │ │ │ │ - if (!zoomChanged && coordSysUnchanged) { │ │ │ │ - for (var i in this.unrenderedFeatures) { │ │ │ │ - var feature = this.unrenderedFeatures[i]; │ │ │ │ - this.drawFeature(feature) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.BBOX" │ │ │ │ +}); │ │ │ │ +OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ + distance: 20, │ │ │ │ + threshold: null, │ │ │ │ + features: null, │ │ │ │ + clusters: null, │ │ │ │ + clustering: false, │ │ │ │ + resolution: null, │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + beforefeaturesadded: this.cacheFeatures, │ │ │ │ + featuresremoved: this.clearCache, │ │ │ │ + moveend: this.cluster, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ } │ │ │ │ - if (!this.drawn || zoomChanged || !coordSysUnchanged) { │ │ │ │ - this.drawn = true; │ │ │ │ - var feature; │ │ │ │ - for (var i = 0, len = this.features.length; i < len; i++) { │ │ │ │ - this.renderer.locked = i !== len - 1; │ │ │ │ - feature = this.features[i]; │ │ │ │ - this.drawFeature(feature) │ │ │ │ - } │ │ │ │ + return activated │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.clearCache(); │ │ │ │ + this.layer.events.un({ │ │ │ │ + beforefeaturesadded: this.cacheFeatures, │ │ │ │ + featuresremoved: this.clearCache, │ │ │ │ + moveend: this.cluster, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ } │ │ │ │ + return deactivated │ │ │ │ }, │ │ │ │ - display: function(display) { │ │ │ │ - OpenLayers.Layer.prototype.display.apply(this, arguments); │ │ │ │ - var currentDisplay = this.div.style.display; │ │ │ │ - if (currentDisplay != this.renderer.root.style.display) { │ │ │ │ - this.renderer.root.style.display = currentDisplay │ │ │ │ + cacheFeatures: function(event) { │ │ │ │ + var propagate = true; │ │ │ │ + if (!this.clustering) { │ │ │ │ + this.clearCache(); │ │ │ │ + this.features = event.features; │ │ │ │ + this.cluster(); │ │ │ │ + propagate = false │ │ │ │ } │ │ │ │ + return propagate │ │ │ │ }, │ │ │ │ - addFeatures: function(features, options) { │ │ │ │ - if (!OpenLayers.Util.isArray(features)) { │ │ │ │ - features = [features] │ │ │ │ + clearCache: function() { │ │ │ │ + if (!this.clustering) { │ │ │ │ + this.features = null │ │ │ │ } │ │ │ │ - var notify = !options || !options.silent; │ │ │ │ - if (notify) { │ │ │ │ - var event = { │ │ │ │ - features: features │ │ │ │ - }; │ │ │ │ - var ret = this.events.triggerEvent("beforefeaturesadded", event); │ │ │ │ - if (ret === false) { │ │ │ │ - return │ │ │ │ + }, │ │ │ │ + cluster: function(event) { │ │ │ │ + if ((!event || event.zoomChanged) && this.features) { │ │ │ │ + var resolution = this.layer.map.getResolution(); │ │ │ │ + if (resolution != this.resolution || !this.clustersExist()) { │ │ │ │ + this.resolution = resolution; │ │ │ │ + var clusters = []; │ │ │ │ + var feature, clustered, cluster; │ │ │ │ + for (var i = 0; i < this.features.length; ++i) { │ │ │ │ + feature = this.features[i]; │ │ │ │ + if (feature.geometry) { │ │ │ │ + clustered = false; │ │ │ │ + for (var j = clusters.length - 1; j >= 0; --j) { │ │ │ │ + cluster = clusters[j]; │ │ │ │ + if (this.shouldCluster(cluster, feature)) { │ │ │ │ + this.addToCluster(cluster, feature); │ │ │ │ + clustered = true; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (!clustered) { │ │ │ │ + clusters.push(this.createCluster(this.features[i])) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.clustering = true; │ │ │ │ + this.layer.removeAllFeatures(); │ │ │ │ + this.clustering = false; │ │ │ │ + if (clusters.length > 0) { │ │ │ │ + if (this.threshold > 1) { │ │ │ │ + var clone = clusters.slice(); │ │ │ │ + clusters = []; │ │ │ │ + var candidate; │ │ │ │ + for (var i = 0, len = clone.length; i < len; ++i) { │ │ │ │ + candidate = clone[i]; │ │ │ │ + if (candidate.attributes.count < this.threshold) { │ │ │ │ + Array.prototype.push.apply(clusters, candidate.cluster) │ │ │ │ + } else { │ │ │ │ + clusters.push(candidate) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.clustering = true; │ │ │ │ + this.layer.addFeatures(clusters); │ │ │ │ + this.clustering = false │ │ │ │ + } │ │ │ │ + this.clusters = clusters │ │ │ │ } │ │ │ │ - features = event.features │ │ │ │ } │ │ │ │ - var featuresAdded = []; │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - if (i != features.length - 1) { │ │ │ │ - this.renderer.locked = true │ │ │ │ - } else { │ │ │ │ - this.renderer.locked = false │ │ │ │ - } │ │ │ │ - var feature = features[i]; │ │ │ │ - if (this.geometryType && !(feature.geometry instanceof this.geometryType)) { │ │ │ │ - throw new TypeError("addFeatures: component should be an " + this.geometryType.prototype.CLASS_NAME) │ │ │ │ - } │ │ │ │ - feature.layer = this; │ │ │ │ - if (!feature.style && this.style) { │ │ │ │ - feature.style = OpenLayers.Util.extend({}, this.style) │ │ │ │ - } │ │ │ │ - if (notify) { │ │ │ │ - if (this.events.triggerEvent("beforefeatureadded", { │ │ │ │ - feature: feature │ │ │ │ - }) === false) { │ │ │ │ - continue │ │ │ │ + }, │ │ │ │ + clustersExist: function() { │ │ │ │ + var exist = false; │ │ │ │ + if (this.clusters && this.clusters.length > 0 && this.clusters.length == this.layer.features.length) { │ │ │ │ + exist = true; │ │ │ │ + for (var i = 0; i < this.clusters.length; ++i) { │ │ │ │ + if (this.clusters[i] != this.layer.features[i]) { │ │ │ │ + exist = false; │ │ │ │ + break │ │ │ │ } │ │ │ │ - this.preFeatureInsert(feature) │ │ │ │ - } │ │ │ │ - featuresAdded.push(feature); │ │ │ │ - this.features.push(feature); │ │ │ │ - this.drawFeature(feature); │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featureadded", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.onFeatureInsert(feature) │ │ │ │ } │ │ │ │ } │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featuresadded", { │ │ │ │ - features: featuresAdded │ │ │ │ - }) │ │ │ │ - } │ │ │ │ + return exist │ │ │ │ }, │ │ │ │ - removeFeatures: function(features, options) { │ │ │ │ - if (!features || features.length === 0) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - if (features === this.features) { │ │ │ │ - return this.removeAllFeatures(options) │ │ │ │ - } │ │ │ │ - if (!OpenLayers.Util.isArray(features)) { │ │ │ │ - features = [features] │ │ │ │ - } │ │ │ │ - if (features === this.selectedFeatures) { │ │ │ │ - features = features.slice() │ │ │ │ - } │ │ │ │ - var notify = !options || !options.silent; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("beforefeaturesremoved", { │ │ │ │ - features: features │ │ │ │ + shouldCluster: function(cluster, feature) { │ │ │ │ + var cc = cluster.geometry.getBounds().getCenterLonLat(); │ │ │ │ + var fc = feature.geometry.getBounds().getCenterLonLat(); │ │ │ │ + var distance = Math.sqrt(Math.pow(cc.lon - fc.lon, 2) + Math.pow(cc.lat - fc.lat, 2)) / this.resolution; │ │ │ │ + return distance <= this.distance │ │ │ │ + }, │ │ │ │ + addToCluster: function(cluster, feature) { │ │ │ │ + cluster.cluster.push(feature); │ │ │ │ + cluster.attributes.count += 1 │ │ │ │ + }, │ │ │ │ + createCluster: function(feature) { │ │ │ │ + var center = feature.geometry.getBounds().getCenterLonLat(); │ │ │ │ + var cluster = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(center.lon, center.lat), { │ │ │ │ + count: 1 │ │ │ │ + }); │ │ │ │ + cluster.cluster = [feature]; │ │ │ │ + return cluster │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Cluster" │ │ │ │ +}); │ │ │ │ +OpenLayers.Strategy.Paging = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ + features: null, │ │ │ │ + length: 10, │ │ │ │ + num: null, │ │ │ │ + paging: false, │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + beforefeaturesadded: this.cacheFeatures, │ │ │ │ + scope: this │ │ │ │ }) │ │ │ │ } │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - if (i != 0 && features[i - 1].geometry) { │ │ │ │ - this.renderer.locked = true │ │ │ │ - } else { │ │ │ │ - this.renderer.locked = false │ │ │ │ - } │ │ │ │ - var feature = features[i]; │ │ │ │ - delete this.unrenderedFeatures[feature.id]; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - this.features = OpenLayers.Util.removeItem(this.features, feature); │ │ │ │ - feature.layer = null; │ │ │ │ - if (feature.geometry) { │ │ │ │ - this.renderer.eraseFeatures(feature) │ │ │ │ - } │ │ │ │ - if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1) { │ │ │ │ - OpenLayers.Util.removeItem(this.selectedFeatures, feature) │ │ │ │ - } │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featuresremoved", { │ │ │ │ - features: features │ │ │ │ + return activated │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.clearCache(); │ │ │ │ + this.layer.events.un({ │ │ │ │ + beforefeaturesadded: this.cacheFeatures, │ │ │ │ + scope: this │ │ │ │ }) │ │ │ │ } │ │ │ │ + return deactivated │ │ │ │ }, │ │ │ │ - removeAllFeatures: function(options) { │ │ │ │ - var notify = !options || !options.silent; │ │ │ │ - var features = this.features; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("beforefeaturesremoved", { │ │ │ │ - features: features │ │ │ │ - }) │ │ │ │ + cacheFeatures: function(event) { │ │ │ │ + if (!this.paging) { │ │ │ │ + this.clearCache(); │ │ │ │ + this.features = event.features; │ │ │ │ + this.pageNext(event) │ │ │ │ } │ │ │ │ - var feature; │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - feature = features[i]; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("beforefeatureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - feature.layer = null; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featureremoved", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ + }, │ │ │ │ + clearCache: function() { │ │ │ │ + if (this.features) { │ │ │ │ + for (var i = 0; i < this.features.length; ++i) { │ │ │ │ + this.features[i].destroy() │ │ │ │ } │ │ │ │ } │ │ │ │ - this.renderer.clear(); │ │ │ │ - this.features = []; │ │ │ │ - this.unrenderedFeatures = {}; │ │ │ │ - this.selectedFeatures = []; │ │ │ │ - if (notify) { │ │ │ │ - this.events.triggerEvent("featuresremoved", { │ │ │ │ - features: features │ │ │ │ - }) │ │ │ │ - } │ │ │ │ + this.features = null; │ │ │ │ + this.num = null │ │ │ │ }, │ │ │ │ - destroyFeatures: function(features, options) { │ │ │ │ - var all = features == undefined; │ │ │ │ - if (all) { │ │ │ │ - features = this.features │ │ │ │ + pageCount: function() { │ │ │ │ + var numFeatures = this.features ? this.features.length : 0; │ │ │ │ + return Math.ceil(numFeatures / this.length) │ │ │ │ + }, │ │ │ │ + pageNum: function() { │ │ │ │ + return this.num │ │ │ │ + }, │ │ │ │ + pageLength: function(newLength) { │ │ │ │ + if (newLength && newLength > 0) { │ │ │ │ + this.length = newLength │ │ │ │ } │ │ │ │ - if (features) { │ │ │ │ - this.removeFeatures(features, options); │ │ │ │ - for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ - features[i].destroy() │ │ │ │ + return this.length │ │ │ │ + }, │ │ │ │ + pageNext: function(event) { │ │ │ │ + var changed = false; │ │ │ │ + if (this.features) { │ │ │ │ + if (this.num === null) { │ │ │ │ + this.num = -1 │ │ │ │ } │ │ │ │ + var start = (this.num + 1) * this.length; │ │ │ │ + changed = this.page(start, event) │ │ │ │ } │ │ │ │ + return changed │ │ │ │ }, │ │ │ │ - drawFeature: function(feature, style) { │ │ │ │ - if (!this.drawn) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - if (typeof style != "object") { │ │ │ │ - if (!style && feature.state === OpenLayers.State.DELETE) { │ │ │ │ - style = "delete" │ │ │ │ - } │ │ │ │ - var renderIntent = style || feature.renderIntent; │ │ │ │ - style = feature.style || this.style; │ │ │ │ - if (!style) { │ │ │ │ - style = this.styleMap.createSymbolizer(feature, renderIntent) │ │ │ │ + pagePrevious: function() { │ │ │ │ + var changed = false; │ │ │ │ + if (this.features) { │ │ │ │ + if (this.num === null) { │ │ │ │ + this.num = this.pageCount() │ │ │ │ } │ │ │ │ + var start = (this.num - 1) * this.length; │ │ │ │ + changed = this.page(start) │ │ │ │ } │ │ │ │ - var drawn = this.renderer.drawFeature(feature, style); │ │ │ │ - if (drawn === false || drawn === null) { │ │ │ │ - this.unrenderedFeatures[feature.id] = feature │ │ │ │ - } else { │ │ │ │ - delete this.unrenderedFeatures[feature.id] │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - eraseFeatures: function(features) { │ │ │ │ - this.renderer.eraseFeatures(features) │ │ │ │ + return changed │ │ │ │ }, │ │ │ │ - getFeatureFromEvent: function(evt) { │ │ │ │ - if (!this.renderer) { │ │ │ │ - throw new Error("getFeatureFromEvent called on layer with no " + "renderer. This usually means you destroyed a " + "layer, but not some handler which is associated " + "with it.") │ │ │ │ - } │ │ │ │ - var feature = null; │ │ │ │ - var featureId = this.renderer.getFeatureIdFromEvent(evt); │ │ │ │ - if (featureId) { │ │ │ │ - if (typeof featureId === "string") { │ │ │ │ - feature = this.getFeatureById(featureId) │ │ │ │ - } else { │ │ │ │ - feature = featureId │ │ │ │ + page: function(start, event) { │ │ │ │ + var changed = false; │ │ │ │ + if (this.features) { │ │ │ │ + if (start >= 0 && start < this.features.length) { │ │ │ │ + var num = Math.floor(start / this.length); │ │ │ │ + if (num != this.num) { │ │ │ │ + this.paging = true; │ │ │ │ + var features = this.features.slice(start, start + this.length); │ │ │ │ + this.layer.removeFeatures(this.layer.features); │ │ │ │ + this.num = num; │ │ │ │ + if (event && event.features) { │ │ │ │ + event.features = features │ │ │ │ + } else { │ │ │ │ + this.layer.addFeatures(features) │ │ │ │ + } │ │ │ │ + this.paging = false; │ │ │ │ + changed = true │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ - return feature │ │ │ │ + return changed │ │ │ │ }, │ │ │ │ - getFeatureBy: function(property, value) { │ │ │ │ - var feature = null; │ │ │ │ - for (var i = 0, len = this.features.length; i < len; ++i) { │ │ │ │ - if (this.features[i][property] == value) { │ │ │ │ - feature = this.features[i]; │ │ │ │ - break │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Paging" │ │ │ │ +}); │ │ │ │ +OpenLayers.Strategy.Fixed = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ + preload: false, │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.apply(this, arguments); │ │ │ │ + if (activated) { │ │ │ │ + this.layer.events.on({ │ │ │ │ + refresh: this.load, │ │ │ │ + scope: this │ │ │ │ + }); │ │ │ │ + if (this.layer.visibility == true || this.preload) { │ │ │ │ + this.load() │ │ │ │ + } else { │ │ │ │ + this.layer.events.on({ │ │ │ │ + visibilitychanged: this.load, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ } │ │ │ │ } │ │ │ │ - return feature │ │ │ │ - }, │ │ │ │ - getFeatureById: function(featureId) { │ │ │ │ - return this.getFeatureBy("id", featureId) │ │ │ │ - }, │ │ │ │ - getFeatureByFid: function(featureFid) { │ │ │ │ - return this.getFeatureBy("fid", featureFid) │ │ │ │ + return activated │ │ │ │ }, │ │ │ │ - getFeaturesByAttribute: function(attrName, attrValue) { │ │ │ │ - var i, feature, len = this.features.length, │ │ │ │ - foundFeatures = []; │ │ │ │ - for (i = 0; i < len; i++) { │ │ │ │ - feature = this.features[i]; │ │ │ │ - if (feature && feature.attributes) { │ │ │ │ - if (feature.attributes[attrName] === attrValue) { │ │ │ │ - foundFeatures.push(feature) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.layer.events.un({ │ │ │ │ + refresh: this.load, │ │ │ │ + visibilitychanged: this.load, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ } │ │ │ │ - return foundFeatures │ │ │ │ + return deactivated │ │ │ │ }, │ │ │ │ - onFeatureInsert: function(feature) {}, │ │ │ │ - preFeatureInsert: function(feature) {}, │ │ │ │ - getDataExtent: function() { │ │ │ │ - var maxExtent = null; │ │ │ │ - var features = this.features; │ │ │ │ + load: function(options) { │ │ │ │ + var layer = this.layer; │ │ │ │ + layer.events.triggerEvent("loadstart", { │ │ │ │ + filter: layer.filter │ │ │ │ + }); │ │ │ │ + layer.protocol.read(OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: this.merge, │ │ │ │ + filter: layer.filter, │ │ │ │ + scope: this │ │ │ │ + }, options)); │ │ │ │ + layer.events.un({ │ │ │ │ + visibilitychanged: this.load, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + merge: function(resp) { │ │ │ │ + var layer = this.layer; │ │ │ │ + layer.destroyFeatures(); │ │ │ │ + var features = resp.features; │ │ │ │ if (features && features.length > 0) { │ │ │ │ - var geometry = null; │ │ │ │ - for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ - geometry = features[i].geometry; │ │ │ │ - if (geometry) { │ │ │ │ - if (maxExtent === null) { │ │ │ │ - maxExtent = new OpenLayers.Bounds │ │ │ │ + var remote = layer.projection; │ │ │ │ + var local = layer.map.getProjectionObject(); │ │ │ │ + if (!local.equals(remote)) { │ │ │ │ + var geom; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + geom = features[i].geometry; │ │ │ │ + if (geom) { │ │ │ │ + geom.transform(remote, local) │ │ │ │ } │ │ │ │ - maxExtent.extend(geometry.getBounds()) │ │ │ │ } │ │ │ │ } │ │ │ │ + layer.addFeatures(features) │ │ │ │ } │ │ │ │ - return maxExtent │ │ │ │ + layer.events.triggerEvent("loadend", { │ │ │ │ + response: resp │ │ │ │ + }) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Vector" │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Fixed" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.PointTrack = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ - dataFrom: null, │ │ │ │ - styleFrom: null, │ │ │ │ - addNodes: function(pointFeatures, options) { │ │ │ │ - if (pointFeatures.length < 2) { │ │ │ │ - throw new Error("At least two point features have to be added to " + "create a line from") │ │ │ │ - } │ │ │ │ - var lines = new Array(pointFeatures.length - 1); │ │ │ │ - var pointFeature, startPoint, endPoint; │ │ │ │ - for (var i = 0, len = pointFeatures.length; i < len; i++) { │ │ │ │ - pointFeature = pointFeatures[i]; │ │ │ │ - endPoint = pointFeature.geometry; │ │ │ │ - if (!endPoint) { │ │ │ │ - var lonlat = pointFeature.lonlat; │ │ │ │ - endPoint = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat) │ │ │ │ - } else if (endPoint.CLASS_NAME != "OpenLayers.Geometry.Point") { │ │ │ │ - throw new TypeError("Only features with point geometries are supported.") │ │ │ │ - } │ │ │ │ - if (i > 0) { │ │ │ │ - var attributes = this.dataFrom != null ? pointFeatures[i + this.dataFrom].data || pointFeatures[i + this.dataFrom].attributes : null; │ │ │ │ - var style = this.styleFrom != null ? pointFeatures[i + this.styleFrom].style : null; │ │ │ │ - var line = new OpenLayers.Geometry.LineString([startPoint, endPoint]); │ │ │ │ - lines[i - 1] = new OpenLayers.Feature.Vector(line, attributes, style) │ │ │ │ +OpenLayers.Strategy.Refresh = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ + force: false, │ │ │ │ + interval: 0, │ │ │ │ + timer: null, │ │ │ │ + activate: function() { │ │ │ │ + var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ + if (activated) { │ │ │ │ + if (this.layer.visibility === true) { │ │ │ │ + this.start() │ │ │ │ } │ │ │ │ - startPoint = endPoint │ │ │ │ + this.layer.events.on({ │ │ │ │ + visibilitychanged: this.reset, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ } │ │ │ │ - this.addFeatures(lines, options) │ │ │ │ + return activated │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.PointTrack" │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = OpenLayers.Strategy.prototype.deactivate.call(this); │ │ │ │ + if (deactivated) { │ │ │ │ + this.stop(); │ │ │ │ + this.layer.events.un({ │ │ │ │ + visibilitychanged: this.reset, │ │ │ │ + scope: this │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + return deactivated │ │ │ │ + }, │ │ │ │ + reset: function() { │ │ │ │ + if (this.layer.visibility === true) { │ │ │ │ + this.start() │ │ │ │ + } else { │ │ │ │ + this.stop() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + start: function() { │ │ │ │ + if (this.interval && typeof this.interval === "number" && this.interval > 0) { │ │ │ │ + this.timer = window.setInterval(OpenLayers.Function.bind(this.refresh, this), this.interval) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + refresh: function() { │ │ │ │ + if (this.layer && this.layer.refresh && typeof this.layer.refresh == "function") { │ │ │ │ + this.layer.refresh({ │ │ │ │ + force: this.force │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + stop: function() { │ │ │ │ + if (this.timer !== null) { │ │ │ │ + window.clearInterval(this.timer); │ │ │ │ + this.timer = null │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Strategy.Refresh" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.PointTrack.SOURCE_NODE = -1; │ │ │ │ -OpenLayers.Layer.PointTrack.TARGET_NODE = 0; │ │ │ │ -OpenLayers.Layer.PointTrack.dataFrom = { │ │ │ │ - SOURCE_NODE: -1, │ │ │ │ - TARGET_NODE: 0 │ │ │ │ -}; │ │ │ │ OpenLayers.Tile.UTFGrid = OpenLayers.Class(OpenLayers.Tile, { │ │ │ │ url: null, │ │ │ │ utfgridResolution: 2, │ │ │ │ json: null, │ │ │ │ format: null, │ │ │ │ destroy: function() { │ │ │ │ this.clear(); │ │ │ │ @@ -16294,2835 +15344,2089 @@ │ │ │ │ this.json = this.format.read(str) │ │ │ │ }, │ │ │ │ clear: function() { │ │ │ │ this.json = null │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.Tile.UTFGrid" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.UTFGrid = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ - isBaseLayer: false, │ │ │ │ - projection: new OpenLayers.Projection("EPSG:900913"), │ │ │ │ - useJSONP: false, │ │ │ │ - tileClass: OpenLayers.Tile.UTFGrid, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, [options.name, options.url, {}, options]); │ │ │ │ - this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ - utfgridResolution: this.utfgridResolution │ │ │ │ - }, this.tileOptions) │ │ │ │ +OpenLayers.Tile.Image.IFrame = { │ │ │ │ + useIFrame: null, │ │ │ │ + blankImageUrl: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAQAIBRAA7", │ │ │ │ + draw: function() { │ │ │ │ + var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this); │ │ │ │ + if (draw) { │ │ │ │ + var url = this.layer.getURL(this.bounds); │ │ │ │ + var usedIFrame = this.useIFrame; │ │ │ │ + this.useIFrame = this.maxGetUrlLength !== null && !this.layer.async && url.length > this.maxGetUrlLength; │ │ │ │ + var fromIFrame = usedIFrame && !this.useIFrame; │ │ │ │ + var toIFrame = !usedIFrame && this.useIFrame; │ │ │ │ + if (fromIFrame || toIFrame) { │ │ │ │ + if (this.imgDiv && this.imgDiv.parentNode === this.frame) { │ │ │ │ + this.frame.removeChild(this.imgDiv) │ │ │ │ + } │ │ │ │ + this.imgDiv = null; │ │ │ │ + if (fromIFrame) { │ │ │ │ + this.frame.removeChild(this.frame.firstChild) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments) │ │ │ │ }, │ │ │ │ - createBackBuffer: function() {}, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.UTFGrid(this.getOptions()) │ │ │ │ + getImage: function() { │ │ │ │ + if (this.useIFrame === true) { │ │ │ │ + if (!this.frame.childNodes.length) { │ │ │ │ + var eventPane = document.createElement("div"), │ │ │ │ + style = eventPane.style; │ │ │ │ + style.position = "absolute"; │ │ │ │ + style.width = "100%"; │ │ │ │ + style.height = "100%"; │ │ │ │ + style.zIndex = 1; │ │ │ │ + style.backgroundImage = "url(" + this.blankImageUrl + ")"; │ │ │ │ + this.frame.appendChild(eventPane) │ │ │ │ + } │ │ │ │ + var id = this.id + "_iFrame", │ │ │ │ + iframe; │ │ │ │ + if (parseFloat(navigator.appVersion.split("MSIE")[1]) < 9) { │ │ │ │ + iframe = document.createElement('<iframe name="' + id + '">'); │ │ │ │ + iframe.style.backgroundColor = "#FFFFFF"; │ │ │ │ + iframe.style.filter = "chroma(color=#FFFFFF)" │ │ │ │ + } else { │ │ │ │ + iframe = document.createElement("iframe"); │ │ │ │ + iframe.style.backgroundColor = "transparent"; │ │ │ │ + iframe.name = id │ │ │ │ + } │ │ │ │ + iframe.scrolling = "no"; │ │ │ │ + iframe.marginWidth = "0px"; │ │ │ │ + iframe.marginHeight = "0px"; │ │ │ │ + iframe.frameBorder = "0"; │ │ │ │ + iframe.style.position = "absolute"; │ │ │ │ + iframe.style.width = "100%"; │ │ │ │ + iframe.style.height = "100%"; │ │ │ │ + if (this.layer.opacity < 1) { │ │ │ │ + OpenLayers.Util.modifyDOMElement(iframe, null, null, null, null, null, null, this.layer.opacity) │ │ │ │ + } │ │ │ │ + this.frame.appendChild(iframe); │ │ │ │ + this.imgDiv = iframe; │ │ │ │ + return iframe │ │ │ │ + } else { │ │ │ │ + return OpenLayers.Tile.Image.prototype.getImage.apply(this, arguments) │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ }, │ │ │ │ - getFeatureInfo: function(location) { │ │ │ │ - var info = null; │ │ │ │ - var tileInfo = this.getTileData(location); │ │ │ │ - if (tileInfo && tileInfo.tile) { │ │ │ │ - info = tileInfo.tile.getFeatureInfo(tileInfo.i, tileInfo.j) │ │ │ │ + createRequestForm: function() { │ │ │ │ + var form = document.createElement("form"); │ │ │ │ + form.method = "POST"; │ │ │ │ + var cacheId = this.layer.params["_OLSALT"]; │ │ │ │ + cacheId = (cacheId ? cacheId + "_" : "") + this.bounds.toBBOX(); │ │ │ │ + form.action = OpenLayers.Util.urlAppend(this.layer.url, cacheId); │ │ │ │ + form.target = this.id + "_iFrame"; │ │ │ │ + var imageSize = this.layer.getImageSize(), │ │ │ │ + params = OpenLayers.Util.getParameters(this.url), │ │ │ │ + field; │ │ │ │ + for (var par in params) { │ │ │ │ + field = document.createElement("input"); │ │ │ │ + field.type = "hidden"; │ │ │ │ + field.name = par; │ │ │ │ + field.value = params[par]; │ │ │ │ + form.appendChild(field) │ │ │ │ } │ │ │ │ - return info │ │ │ │ + return form │ │ │ │ }, │ │ │ │ - getFeatureId: function(location) { │ │ │ │ - var id = null; │ │ │ │ - var info = this.getTileData(location); │ │ │ │ - if (info.tile) { │ │ │ │ - id = info.tile.getFeatureId(info.i, info.j) │ │ │ │ + setImgSrc: function(url) { │ │ │ │ + if (this.useIFrame === true) { │ │ │ │ + if (url) { │ │ │ │ + var form = this.createRequestForm(); │ │ │ │ + this.frame.appendChild(form); │ │ │ │ + form.submit(); │ │ │ │ + this.frame.removeChild(form) │ │ │ │ + } else if (this.imgDiv.parentNode === this.frame) { │ │ │ │ + this.frame.removeChild(this.imgDiv); │ │ │ │ + this.imgDiv = null │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + OpenLayers.Tile.Image.prototype.setImgSrc.apply(this, arguments) │ │ │ │ } │ │ │ │ - return id │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.UTFGrid" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.PointGrid = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ - dx: null, │ │ │ │ - dy: null, │ │ │ │ - ratio: 1.5, │ │ │ │ - maxFeatures: 250, │ │ │ │ - rotation: 0, │ │ │ │ - origin: null, │ │ │ │ - gridBounds: null, │ │ │ │ - initialize: function(config) { │ │ │ │ - config = config || {}; │ │ │ │ - OpenLayers.Layer.Vector.prototype.initialize.apply(this, [config.name, config]) │ │ │ │ + onImageLoad: function() { │ │ │ │ + OpenLayers.Tile.Image.prototype.onImageLoad.apply(this, arguments); │ │ │ │ + if (this.useIFrame === true) { │ │ │ │ + this.imgDiv.style.opacity = 1; │ │ │ │ + this.frame.style.opacity = this.layer.opacity │ │ │ │ + } │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); │ │ │ │ - map.events.register("moveend", this, this.onMoveEnd) │ │ │ │ + createBackBuffer: function() { │ │ │ │ + var backBuffer; │ │ │ │ + if (this.useIFrame === false) { │ │ │ │ + backBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer.call(this) │ │ │ │ + } │ │ │ │ + return backBuffer │ │ │ │ + } │ │ │ │ +}; │ │ │ │ +OpenLayers.Renderer.Canvas = OpenLayers.Class(OpenLayers.Renderer, { │ │ │ │ + hitDetection: true, │ │ │ │ + hitOverflow: 0, │ │ │ │ + canvas: null, │ │ │ │ + features: null, │ │ │ │ + pendingRedraw: false, │ │ │ │ + cachedSymbolBounds: {}, │ │ │ │ + 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") │ │ │ │ + } │ │ │ │ }, │ │ │ │ - removeMap: function(map) { │ │ │ │ - map.events.unregister("moveend", this, this.onMoveEnd); │ │ │ │ - OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments) │ │ │ │ + setExtent: function() { │ │ │ │ + OpenLayers.Renderer.prototype.setExtent.apply(this, arguments); │ │ │ │ + return false │ │ │ │ }, │ │ │ │ - setRatio: function(ratio) { │ │ │ │ - this.ratio = ratio; │ │ │ │ - this.updateGrid(true) │ │ │ │ + eraseGeometry: function(geometry, featureId) { │ │ │ │ + this.eraseFeatures(this.features[featureId][0]) │ │ │ │ }, │ │ │ │ - setMaxFeatures: function(maxFeatures) { │ │ │ │ - this.maxFeatures = maxFeatures; │ │ │ │ - this.updateGrid(true) │ │ │ │ + supported: function() { │ │ │ │ + return OpenLayers.CANVAS_SUPPORTED │ │ │ │ }, │ │ │ │ - setSpacing: function(dx, dy) { │ │ │ │ - this.dx = dx; │ │ │ │ - this.dy = dy || dx; │ │ │ │ - this.updateGrid(true) │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ }, │ │ │ │ - setOrigin: function(origin) { │ │ │ │ - this.origin = origin; │ │ │ │ - this.updateGrid(true) │ │ │ │ + drawFeature: function(feature, style) { │ │ │ │ + var rendered; │ │ │ │ + if (feature.geometry) { │ │ │ │ + style = this.applyDefaultSymbolizer(style || feature.style); │ │ │ │ + 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) { │ │ │ │ + this.features[feature.id] = [feature, style] │ │ │ │ + } else { │ │ │ │ + delete this.features[feature.id] │ │ │ │ + } │ │ │ │ + this.pendingRedraw = true │ │ │ │ + } │ │ │ │ + if (this.pendingRedraw && !this.locked) { │ │ │ │ + this.redraw(); │ │ │ │ + this.pendingRedraw = false │ │ │ │ + } │ │ │ │ + return rendered │ │ │ │ }, │ │ │ │ - getOrigin: function() { │ │ │ │ - if (!this.origin) { │ │ │ │ - this.origin = this.map.getExtent().getCenterLonLat() │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - return this.origin │ │ │ │ }, │ │ │ │ - setRotation: function(rotation) { │ │ │ │ - this.rotation = rotation; │ │ │ │ - this.updateGrid(true) │ │ │ │ + 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 : -(.5 * width); │ │ │ │ + var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.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 / 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 │ │ │ │ }, │ │ │ │ - onMoveEnd: function() { │ │ │ │ - this.updateGrid() │ │ │ │ + drawNamedSymbol: function(geometry, style, featureId) { │ │ │ │ + var x, y, cx, cy, i, symbolBounds, scaling, angle; │ │ │ │ + var unscaledStrokeWidth; │ │ │ │ + var deg2rad = Math.PI / 180; │ │ │ │ + var symbol = OpenLayers.Renderer.symbol[style.graphicName]; │ │ │ │ + if (!symbol) { │ │ │ │ + throw new Error(style.graphicName + " is not a valid symbol name") │ │ │ │ + } │ │ │ │ + if (!symbol.length || symbol.length < 2) return; │ │ │ │ + var pt = this.getLocalXY(geometry); │ │ │ │ + var p0 = pt[0]; │ │ │ │ + var p1 = pt[1]; │ │ │ │ + if (isNaN(p0) || isNaN(p1)) return; │ │ │ │ + this.canvas.lineCap = "round"; │ │ │ │ + this.canvas.lineJoin = "round"; │ │ │ │ + if (this.hitDetection) { │ │ │ │ + this.hitContext.lineCap = "round"; │ │ │ │ + this.hitContext.lineJoin = "round" │ │ │ │ + } │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ + this.canvas.save(); │ │ │ │ + if (this.hitDetection) { │ │ │ │ + this.hitContext.save() │ │ │ │ + } │ │ │ │ + this.canvas.translate(p0, p1); │ │ │ │ + if (this.hitDetection) { │ │ │ │ + this.hitContext.translate(p0, p1) │ │ │ │ + } │ │ │ │ + angle = deg2rad * style.rotation; │ │ │ │ + if (!isNaN(angle)) { │ │ │ │ + this.canvas.rotate(angle); │ │ │ │ + if (this.hitDetection) { │ │ │ │ + this.hitContext.rotate(angle) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + scaling = 2 * style.pointRadius / Math.max(symbolBounds.getWidth(), symbolBounds.getHeight()); │ │ │ │ + this.canvas.scale(scaling, scaling); │ │ │ │ + if (this.hitDetection) { │ │ │ │ + this.hitContext.scale(scaling, scaling) │ │ │ │ + } │ │ │ │ + cx = symbolBounds.getCenterLonLat().lon; │ │ │ │ + cy = symbolBounds.getCenterLonLat().lat; │ │ │ │ + this.canvas.translate(-cx, -cy); │ │ │ │ + if (this.hitDetection) { │ │ │ │ + this.hitContext.translate(-cx, -cy) │ │ │ │ + } │ │ │ │ + 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") │ │ │ │ }, │ │ │ │ - getViewBounds: function() { │ │ │ │ - var bounds = this.map.getExtent(); │ │ │ │ - if (this.rotation) { │ │ │ │ - var origin = this.getOrigin(); │ │ │ │ - var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat); │ │ │ │ - var rect = bounds.toGeometry(); │ │ │ │ - rect.rotate(-this.rotation, rotationOrigin); │ │ │ │ - bounds = rect.getBounds() │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - return bounds │ │ │ │ }, │ │ │ │ - updateGrid: function(force) { │ │ │ │ - if (force || this.invalidBounds()) { │ │ │ │ - var viewBounds = this.getViewBounds(); │ │ │ │ - var origin = this.getOrigin(); │ │ │ │ - var rotationOrigin = new OpenLayers.Geometry.Point(origin.lon, origin.lat); │ │ │ │ - var viewBoundsWidth = viewBounds.getWidth(); │ │ │ │ - var viewBoundsHeight = viewBounds.getHeight(); │ │ │ │ - var aspectRatio = viewBoundsWidth / viewBoundsHeight; │ │ │ │ - var maxHeight = Math.sqrt(this.dx * this.dy * this.maxFeatures / aspectRatio); │ │ │ │ - var maxWidth = maxHeight * aspectRatio; │ │ │ │ - var gridWidth = Math.min(viewBoundsWidth * this.ratio, maxWidth); │ │ │ │ - var gridHeight = Math.min(viewBoundsHeight * this.ratio, maxHeight); │ │ │ │ - var center = viewBounds.getCenterLonLat(); │ │ │ │ - this.gridBounds = new OpenLayers.Bounds(center.lon - gridWidth / 2, center.lat - gridHeight / 2, center.lon + gridWidth / 2, center.lat + gridHeight / 2); │ │ │ │ - var rows = Math.floor(gridHeight / this.dy); │ │ │ │ - var cols = Math.floor(gridWidth / this.dx); │ │ │ │ - var gridLeft = origin.lon + this.dx * Math.ceil((this.gridBounds.left - origin.lon) / this.dx); │ │ │ │ - var gridBottom = origin.lat + this.dy * Math.ceil((this.gridBounds.bottom - origin.lat) / this.dy); │ │ │ │ - var features = new Array(rows * cols); │ │ │ │ - var x, y, point; │ │ │ │ - for (var i = 0; i < cols; ++i) { │ │ │ │ - x = gridLeft + i * this.dx; │ │ │ │ - for (var j = 0; j < rows; ++j) { │ │ │ │ - y = gridBottom + j * this.dy; │ │ │ │ - point = new OpenLayers.Geometry.Point(x, y); │ │ │ │ - if (this.rotation) { │ │ │ │ - point.rotate(this.rotation, rotationOrigin) │ │ │ │ - } │ │ │ │ - features[i * rows + j] = new OpenLayers.Feature.Vector(point) │ │ │ │ + featureIdToHex: function(featureId) { │ │ │ │ + var id = Number(featureId.split("_").pop()) + 1; │ │ │ │ + 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 │ │ │ │ + }, │ │ │ │ + setHitContextStyle: function(type, featureId, symbolizer, strokeScaling) { │ │ │ │ + var hex = this.featureIdToHex(featureId); │ │ │ │ + if (type == "fill") { │ │ │ │ + this.hitContext.globalAlpha = 1; │ │ │ │ + this.hitContext.fillStyle = hex │ │ │ │ + } else if (type == "stroke") { │ │ │ │ + this.hitContext.globalAlpha = 1; │ │ │ │ + this.hitContext.strokeStyle = hex; │ │ │ │ + if (typeof strokeScaling === "undefined") { │ │ │ │ + this.hitContext.lineWidth = symbolizer.strokeWidth + 2 │ │ │ │ + } else { │ │ │ │ + if (!isNaN(strokeScaling)) { │ │ │ │ + this.hitContext.lineWidth = symbolizer.strokeWidth + 2 / strokeScaling │ │ │ │ } │ │ │ │ } │ │ │ │ - this.destroyFeatures(this.features, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.addFeatures(features, { │ │ │ │ - silent: true │ │ │ │ - }) │ │ │ │ + } else { │ │ │ │ + this.hitContext.globalAlpha = 0; │ │ │ │ + this.hitContext.lineWidth = 1 │ │ │ │ } │ │ │ │ }, │ │ │ │ - invalidBounds: function() { │ │ │ │ - return !this.gridBounds || !this.gridBounds.containsBounds(this.getViewBounds()) │ │ │ │ + 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") │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.PointGrid" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.Zoomify = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - size: null, │ │ │ │ - isBaseLayer: true, │ │ │ │ - standardTileSize: 256, │ │ │ │ - tileOriginCorner: "tl", │ │ │ │ - numberOfTiers: 0, │ │ │ │ - tileCountUpToTier: null, │ │ │ │ - tierSizeInTiles: null, │ │ │ │ - tierImageSize: null, │ │ │ │ - initialize: function(name, url, size, options) { │ │ │ │ - this.initializeZoomify(size); │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name, url, size, {}, options]) │ │ │ │ + drawLineString: function(geometry, style, featureId) { │ │ │ │ + style = OpenLayers.Util.applyDefaults({ │ │ │ │ + fill: false │ │ │ │ + }, style); │ │ │ │ + this.drawLinearRing(geometry, style, featureId) │ │ │ │ }, │ │ │ │ - initializeZoomify: function(size) { │ │ │ │ - var imageSize = size.clone(); │ │ │ │ - this.size = size.clone(); │ │ │ │ - var tiles = new OpenLayers.Size(Math.ceil(imageSize.w / this.standardTileSize), Math.ceil(imageSize.h / this.standardTileSize)); │ │ │ │ - this.tierSizeInTiles = [tiles]; │ │ │ │ - this.tierImageSize = [imageSize]; │ │ │ │ - while (imageSize.w > this.standardTileSize || imageSize.h > this.standardTileSize) { │ │ │ │ - imageSize = new OpenLayers.Size(Math.floor(imageSize.w / 2), Math.floor(imageSize.h / 2)); │ │ │ │ - tiles = new OpenLayers.Size(Math.ceil(imageSize.w / this.standardTileSize), Math.ceil(imageSize.h / this.standardTileSize)); │ │ │ │ - this.tierSizeInTiles.push(tiles); │ │ │ │ - this.tierImageSize.push(imageSize) │ │ │ │ - } │ │ │ │ - this.tierSizeInTiles.reverse(); │ │ │ │ - this.tierImageSize.reverse(); │ │ │ │ - this.numberOfTiers = this.tierSizeInTiles.length; │ │ │ │ - var resolutions = [1]; │ │ │ │ - this.tileCountUpToTier = [0]; │ │ │ │ - for (var i = 1; i < this.numberOfTiers; i++) { │ │ │ │ - resolutions.unshift(Math.pow(2, i)); │ │ │ │ - this.tileCountUpToTier.push(this.tierSizeInTiles[i - 1].w * this.tierSizeInTiles[i - 1].h + this.tileCountUpToTier[i - 1]) │ │ │ │ + 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 (!this.serverResolutions) { │ │ │ │ - this.serverResolutions = resolutions │ │ │ │ + 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") │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Layer.Grid.prototype.destroy.apply(this, arguments); │ │ │ │ - this.tileCountUpToTier.length = 0; │ │ │ │ - this.tierSizeInTiles.length = 0; │ │ │ │ - this.tierImageSize.length = 0 │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Zoomify(this.name, this.url, this.size, this.options) │ │ │ │ + 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() │ │ │ │ + } │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); │ │ │ │ - var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h)); │ │ │ │ - var z = this.getZoomForResolution(res); │ │ │ │ - var tileIndex = x + y * this.tierSizeInTiles[z].w + this.tileCountUpToTier[z]; │ │ │ │ - var path = "TileGroup" + Math.floor(tileIndex / 256) + "/" + z + "-" + x + "-" + y + ".jpg"; │ │ │ │ - var url = this.url; │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(path, url) │ │ │ │ + drawPolygon: function(geometry, style, featureId) { │ │ │ │ + var components = geometry.components; │ │ │ │ + var len = components.length; │ │ │ │ + this.drawLinearRing(components[0], style, featureId); │ │ │ │ + for (var i = 1; i < len; ++i) { │ │ │ │ + this.canvas.globalCompositeOperation = "destination-out"; │ │ │ │ + if (this.hitDetection) { │ │ │ │ + this.hitContext.globalCompositeOperation = "destination-out" │ │ │ │ + } │ │ │ │ + this.drawLinearRing(components[i], OpenLayers.Util.applyDefaults({ │ │ │ │ + stroke: false, │ │ │ │ + fillOpacity: 1 │ │ │ │ + }, 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) │ │ │ │ } │ │ │ │ - return url + path │ │ │ │ }, │ │ │ │ - getImageSize: function() { │ │ │ │ - if (arguments.length > 0) { │ │ │ │ - var bounds = this.adjustBounds(arguments[0]); │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); │ │ │ │ - var y = Math.round((this.tileOrigin.lat - bounds.top) / (res * this.tileSize.h)); │ │ │ │ - var z = this.getZoomForResolution(res); │ │ │ │ - var w = this.standardTileSize; │ │ │ │ - var h = this.standardTileSize; │ │ │ │ - if (x == this.tierSizeInTiles[z].w - 1) { │ │ │ │ - var w = this.tierImageSize[z].w % this.standardTileSize │ │ │ │ + drawText: function(location, style) { │ │ │ │ + var pt = this.getLocalXY(location); │ │ │ │ + this.setCanvasStyle("reset"); │ │ │ │ + this.canvas.fillStyle = style.fontColor; │ │ │ │ + this.canvas.globalAlpha = style.fontOpacity || 1; │ │ │ │ + var fontStyle = [style.fontStyle ? style.fontStyle : "normal", "normal", 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) { │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - if (y == this.tierSizeInTiles[z].h - 1) { │ │ │ │ - var h = this.tierImageSize[z].h % this.standardTileSize │ │ │ │ + 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; │ │ │ │ + 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) { │ │ │ │ + this.canvas.mozTextStyle = fontStyle; │ │ │ │ + var hfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]]; │ │ │ │ + if (hfactor == null) { │ │ │ │ + hfactor = -.5 │ │ │ │ + } │ │ │ │ + var vfactor = OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]]; │ │ │ │ + if (vfactor == null) { │ │ │ │ + vfactor = -.5 │ │ │ │ + } │ │ │ │ + var lineHeight = this.canvas.mozMeasureText("xx"); │ │ │ │ + pt[1] += lineHeight * (1 + vfactor * numRows); │ │ │ │ + for (var i = 0; i < numRows; i++) { │ │ │ │ + var x = pt[0] + hfactor * this.canvas.mozMeasureText(labelRows[i]); │ │ │ │ + var y = pt[1] + i * lineHeight; │ │ │ │ + this.canvas.translate(x, y); │ │ │ │ + this.canvas.mozDrawText(labelRows[i]); │ │ │ │ + this.canvas.translate(-x, -y) │ │ │ │ } │ │ │ │ - return new OpenLayers.Size(w, h) │ │ │ │ - } else { │ │ │ │ - return this.tileSize │ │ │ │ } │ │ │ │ + this.setCanvasStyle("reset") │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ - this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, this.map.maxExtent.top) │ │ │ │ + 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] │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Zoomify" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - serviceVersion: "1.0.0", │ │ │ │ - layername: null, │ │ │ │ - type: null, │ │ │ │ - isBaseLayer: true, │ │ │ │ - tileOrigin: null, │ │ │ │ - serverResolutions: null, │ │ │ │ - zoomOffset: 0, │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - var newArguments = []; │ │ │ │ - newArguments.push(name, url, {}, options); │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments) │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.TMS(this.name, this.url, this.getOptions()) │ │ │ │ + getFeatureIdFromEvent: function(evt) { │ │ │ │ + var featureId, feature; │ │ │ │ + if (this.hitDetection && this.root.style.display !== "none") { │ │ │ │ + if (!this.map.dragging) { │ │ │ │ + var xy = evt.xy; │ │ │ │ + var x = xy.x | 0; │ │ │ │ + var y = xy.y | 0; │ │ │ │ + var data = this.hitContext.getImageData(x, y, 1, 1).data; │ │ │ │ + if (data[3] === 255) { │ │ │ │ + var id = data[2] + 256 * (data[1] + 256 * data[0]); │ │ │ │ + if (id) { │ │ │ │ + featureId = "OpenLayers_Feature_Vector_" + (id - 1 + this.hitOverflow); │ │ │ │ + try { │ │ │ │ + feature = this.features[featureId][0] │ │ │ │ + } catch (err) {} │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ + return feature │ │ │ │ }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); │ │ │ │ - var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h)); │ │ │ │ - var z = this.getServerZoom(); │ │ │ │ - var path = this.serviceVersion + "/" + this.layername + "/" + z + "/" + x + "/" + y + "." + this.type; │ │ │ │ - var url = this.url; │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(path, url) │ │ │ │ + eraseFeatures: function(features) { │ │ │ │ + if (!OpenLayers.Util.isArray(features)) { │ │ │ │ + features = [features] │ │ │ │ } │ │ │ │ - return url + path │ │ │ │ + for (var i = 0; i < features.length; ++i) { │ │ │ │ + delete this.features[features[i].id] │ │ │ │ + } │ │ │ │ + this.redraw() │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments); │ │ │ │ - if (!this.tileOrigin) { │ │ │ │ - this.tileOrigin = new OpenLayers.LonLat(this.map.maxExtent.left, this.map.maxExtent.bottom) │ │ │ │ + redraw: function() { │ │ │ │ + if (!this.locked) { │ │ │ │ + var height = this.root.height; │ │ │ │ + var width = this.root.width; │ │ │ │ + this.canvas.clearRect(0, 0, width, height); │ │ │ │ + if (this.hitDetection) { │ │ │ │ + this.hitContext.clearRect(0, 0, width, height) │ │ │ │ + } │ │ │ │ + var labelMap = []; │ │ │ │ + var feature, geometry, style; │ │ │ │ + var worldBounds = this.map.baseLayer && this.map.baseLayer.wrapDateLine && this.map.getMaxExtent(); │ │ │ │ + for (var id in this.features) { │ │ │ │ + if (!this.features.hasOwnProperty(id)) { │ │ │ │ + continue │ │ │ │ + } │ │ │ │ + feature = this.features[id][0]; │ │ │ │ + geometry = feature.geometry; │ │ │ │ + this.calculateFeatureDx(geometry.getBounds(), worldBounds); │ │ │ │ + style = this.features[id][1]; │ │ │ │ + this.drawGeometry(geometry, style, feature.id); │ │ │ │ + if (style.label) { │ │ │ │ + labelMap.push([feature, style]) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var item; │ │ │ │ + for (var i = 0, len = labelMap.length; i < len; ++i) { │ │ │ │ + item = labelMap[i]; │ │ │ │ + this.drawText(item[0].geometry.getCentroid(), item[1]) │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.TMS" │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.Canvas" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.WMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - DEFAULT_PARAMS: { │ │ │ │ - service: "WMS", │ │ │ │ - version: "1.1.1", │ │ │ │ - request: "GetMap", │ │ │ │ - styles: "", │ │ │ │ - format: "image/jpeg" │ │ │ │ +OpenLayers.Renderer.Canvas.LABEL_ALIGN = { │ │ │ │ + l: "left", │ │ │ │ + r: "right", │ │ │ │ + t: "top", │ │ │ │ + b: "bottom" │ │ │ │ +}; │ │ │ │ +OpenLayers.Renderer.Canvas.LABEL_FACTOR = { │ │ │ │ + l: 0, │ │ │ │ + r: -1, │ │ │ │ + t: 0, │ │ │ │ + b: -1 │ │ │ │ +}; │ │ │ │ +OpenLayers.Renderer.Canvas.drawImageScaleFactor = null; │ │ │ │ +OpenLayers.ElementsIndexer = OpenLayers.Class({ │ │ │ │ + maxZIndex: null, │ │ │ │ + order: null, │ │ │ │ + indices: null, │ │ │ │ + compare: null, │ │ │ │ + initialize: function(yOrdering) { │ │ │ │ + this.compare = yOrdering ? OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER; │ │ │ │ + this.clear() │ │ │ │ }, │ │ │ │ - isBaseLayer: true, │ │ │ │ - encodeBBOX: false, │ │ │ │ - noMagic: false, │ │ │ │ - yx: {}, │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - var newArguments = []; │ │ │ │ - params = OpenLayers.Util.upperCaseObject(params); │ │ │ │ - if (parseFloat(params.VERSION) >= 1.3 && !params.EXCEPTIONS) { │ │ │ │ - params.EXCEPTIONS = "INIMAGE" │ │ │ │ + insert: function(newNode) { │ │ │ │ + if (this.exists(newNode)) { │ │ │ │ + this.remove(newNode) │ │ │ │ } │ │ │ │ - newArguments.push(name, url, params, options); │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ - OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)); │ │ │ │ - if (!this.noMagic && this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == "true") { │ │ │ │ - if (options == null || !options.isBaseLayer) { │ │ │ │ - this.isBaseLayer = false │ │ │ │ - } │ │ │ │ - if (this.params.FORMAT == "image/jpeg") { │ │ │ │ - this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif" : "image/png" │ │ │ │ + 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); │ │ │ │ + return this.getNextElement(rightIndex) │ │ │ │ }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.WMS(this.name, this.url, this.params, this.getOptions()) │ │ │ │ + remove: function(node) { │ │ │ │ + var nodeId = node.id; │ │ │ │ + var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId); │ │ │ │ + if (arrayIndex >= 0) { │ │ │ │ + this.order.splice(arrayIndex, 1); │ │ │ │ + delete this.indices[nodeId]; │ │ │ │ + if (this.order.length > 0) { │ │ │ │ + var lastId = this.order[this.order.length - 1]; │ │ │ │ + this.maxZIndex = this.indices[lastId] │ │ │ │ + } else { │ │ │ │ + this.maxZIndex = 0 │ │ │ │ + } │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ }, │ │ │ │ - reverseAxisOrder: function() { │ │ │ │ - var projCode = this.projection.getCode(); │ │ │ │ - return parseFloat(this.params.VERSION) >= 1.3 && !!(this.yx[projCode] || OpenLayers.Projection.defaults[projCode] && OpenLayers.Projection.defaults[projCode].yx) │ │ │ │ + clear: function() { │ │ │ │ + this.order = []; │ │ │ │ + this.indices = {}; │ │ │ │ + this.maxZIndex = 0 │ │ │ │ }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var imageSize = this.getImageSize(); │ │ │ │ - var newParams = {}; │ │ │ │ - var reverseAxisOrder = this.reverseAxisOrder(); │ │ │ │ - newParams.BBOX = this.encodeBBOX ? bounds.toBBOX(null, reverseAxisOrder) : bounds.toArray(reverseAxisOrder); │ │ │ │ - newParams.WIDTH = imageSize.w; │ │ │ │ - newParams.HEIGHT = imageSize.h; │ │ │ │ - var requestString = this.getFullRequestString(newParams); │ │ │ │ - return requestString │ │ │ │ + exists: function(node) { │ │ │ │ + return this.indices[node.id] != null │ │ │ │ }, │ │ │ │ - mergeNewParams: function(newParams) { │ │ │ │ - var upperParams = OpenLayers.Util.upperCaseObject(newParams); │ │ │ │ - var newArguments = [upperParams]; │ │ │ │ - return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments) │ │ │ │ + getZIndex: function(node) { │ │ │ │ + return node._style.graphicZIndex │ │ │ │ }, │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ - var mapProjection = this.map.getProjectionObject(); │ │ │ │ - var projectionCode = this.projection && this.projection.equals(mapProjection) ? this.projection.getCode() : mapProjection.getCode(); │ │ │ │ - var value = projectionCode == "none" ? null : projectionCode; │ │ │ │ - if (parseFloat(this.params.VERSION) >= 1.3) { │ │ │ │ - this.params.CRS = value │ │ │ │ - } else { │ │ │ │ - this.params.SRS = value │ │ │ │ - } │ │ │ │ - if (typeof this.params.TRANSPARENT == "boolean") { │ │ │ │ - newParams.TRANSPARENT = this.params.TRANSPARENT ? "TRUE" : "FALSE" │ │ │ │ + determineZIndex: function(node) { │ │ │ │ + var zIndex = node._style.graphicZIndex; │ │ │ │ + if (zIndex == null) { │ │ │ │ + zIndex = this.maxZIndex; │ │ │ │ + node._style.graphicZIndex = zIndex │ │ │ │ + } else if (zIndex > this.maxZIndex) { │ │ │ │ + this.maxZIndex = zIndex │ │ │ │ } │ │ │ │ - return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, arguments) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.WMS" │ │ │ │ -}); │ │ │ │ -OpenLayers.Format.ArcXML = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ - fontStyleKeys: ["antialiasing", "blockout", "font", "fontcolor", "fontsize", "fontstyle", "glowing", "interval", "outline", "printmode", "shadow", "transparency"], │ │ │ │ - request: null, │ │ │ │ - response: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - this.request = new OpenLayers.Format.ArcXML.Request; │ │ │ │ - this.response = new OpenLayers.Format.ArcXML.Response; │ │ │ │ - if (options) { │ │ │ │ - if (options.requesttype == "feature") { │ │ │ │ - this.request.get_image = null; │ │ │ │ - var qry = this.request.get_feature.query; │ │ │ │ - this.addCoordSys(qry.featurecoordsys, options.featureCoordSys); │ │ │ │ - this.addCoordSys(qry.filtercoordsys, options.filterCoordSys); │ │ │ │ - if (options.polygon) { │ │ │ │ - qry.isspatial = true; │ │ │ │ - qry.spatialfilter.polygon = options.polygon │ │ │ │ - } else if (options.envelope) { │ │ │ │ - qry.isspatial = true; │ │ │ │ - qry.spatialfilter.envelope = { │ │ │ │ - minx: 0, │ │ │ │ - miny: 0, │ │ │ │ - maxx: 0, │ │ │ │ - maxy: 0 │ │ │ │ - }; │ │ │ │ - this.parseEnvelope(qry.spatialfilter.envelope, options.envelope) │ │ │ │ - } │ │ │ │ - } else if (options.requesttype == "image") { │ │ │ │ - this.request.get_feature = null; │ │ │ │ - var props = this.request.get_image.properties; │ │ │ │ - this.parseEnvelope(props.envelope, options.envelope); │ │ │ │ - this.addLayers(props.layerlist, options.layers); │ │ │ │ - this.addImageSize(props.imagesize, options.tileSize); │ │ │ │ - this.addCoordSys(props.featurecoordsys, options.featureCoordSys); │ │ │ │ - this.addCoordSys(props.filtercoordsys, options.filterCoordSys) │ │ │ │ - } else { │ │ │ │ - this.request = 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 │ │ │ │ } │ │ │ │ - OpenLayers.Format.XML.prototype.initialize.apply(this, [options]) │ │ │ │ }, │ │ │ │ - parseEnvelope: function(env, arr) { │ │ │ │ - if (arr && arr.length == 4) { │ │ │ │ - env.minx = arr[0]; │ │ │ │ - env.miny = arr[1]; │ │ │ │ - env.maxx = arr[2]; │ │ │ │ - env.maxy = arr[3] │ │ │ │ + CLASS_NAME: "OpenLayers.ElementsIndexer" │ │ │ │ +}); │ │ │ │ +OpenLayers.ElementsIndexer.IndexingMethods = { │ │ │ │ + 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 │ │ │ │ }, │ │ │ │ - addLayers: function(ll, lyrs) { │ │ │ │ - for (var lind = 0, len = lyrs.length; lind < len; lind++) { │ │ │ │ - ll.push(lyrs[lind]) │ │ │ │ + Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) { │ │ │ │ + var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, nextNode); │ │ │ │ + if (nextNode && returnVal == 0) { │ │ │ │ + returnVal = 1 │ │ │ │ } │ │ │ │ + return returnVal │ │ │ │ }, │ │ │ │ - addImageSize: function(imsize, olsize) { │ │ │ │ - if (olsize !== null) { │ │ │ │ - imsize.width = olsize.w; │ │ │ │ - imsize.height = olsize.h; │ │ │ │ - imsize.printwidth = olsize.w; │ │ │ │ - imsize.printheight = olsize.h │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - }, │ │ │ │ - addCoordSys: function(featOrFilt, fsys) { │ │ │ │ - if (typeof fsys == "string") { │ │ │ │ - featOrFilt.id = parseInt(fsys); │ │ │ │ - featOrFilt.string = fsys │ │ │ │ - } else if (typeof fsys == "object" && fsys.proj !== null) { │ │ │ │ - featOrFilt.id = fsys.proj.srsProjNumber; │ │ │ │ - featOrFilt.string = fsys.proj.srsCode │ │ │ │ - } else { │ │ │ │ - featOrFilt = fsys │ │ │ │ + return returnVal │ │ │ │ + } │ │ │ │ +}; │ │ │ │ +OpenLayers.Renderer.Elements = OpenLayers.Class(OpenLayers.Renderer, { │ │ │ │ + rendererRoot: null, │ │ │ │ + root: null, │ │ │ │ + vectorRoot: null, │ │ │ │ + textRoot: null, │ │ │ │ + xmlns: null, │ │ │ │ + xOffset: 0, │ │ │ │ + indexer: null, │ │ │ │ + BACKGROUND_ID_SUFFIX: "_background", │ │ │ │ + LABEL_ID_SUFFIX: "_label", │ │ │ │ + LABEL_OUTLINE_SUFFIX: "_outline", │ │ │ │ + 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) │ │ │ │ } │ │ │ │ }, │ │ │ │ - iserror: function(data) { │ │ │ │ - var ret = null; │ │ │ │ - if (!data) { │ │ │ │ - ret = this.response.error !== "" │ │ │ │ - } else { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ - var errorNodes = data.documentElement.getElementsByTagName("ERROR"); │ │ │ │ - ret = errorNodes !== null && errorNodes.length > 0 │ │ │ │ - } │ │ │ │ - return ret │ │ │ │ + destroy: function() { │ │ │ │ + this.clear(); │ │ │ │ + this.rendererRoot = null; │ │ │ │ + this.root = null; │ │ │ │ + this.xmlns = null; │ │ │ │ + OpenLayers.Renderer.prototype.destroy.apply(this, arguments) │ │ │ │ }, │ │ │ │ - read: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]) │ │ │ │ - } │ │ │ │ - var arcNode = null; │ │ │ │ - if (data && data.documentElement) { │ │ │ │ - if (data.documentElement.nodeName == "ARCXML") { │ │ │ │ - arcNode = data.documentElement │ │ │ │ - } else { │ │ │ │ - arcNode = data.documentElement.getElementsByTagName("ARCXML")[0] │ │ │ │ + clear: function() { │ │ │ │ + var child; │ │ │ │ + var root = this.vectorRoot; │ │ │ │ + if (root) { │ │ │ │ + while (child = root.firstChild) { │ │ │ │ + root.removeChild(child) │ │ │ │ } │ │ │ │ } │ │ │ │ - if (!arcNode || arcNode.firstChild.nodeName === "parsererror") { │ │ │ │ - var error, source; │ │ │ │ - try { │ │ │ │ - error = data.firstChild.nodeValue; │ │ │ │ - source = data.firstChild.childNodes[1].firstChild.nodeValue │ │ │ │ - } catch (err) {} │ │ │ │ - throw { │ │ │ │ - message: "Error parsing the ArcXML request", │ │ │ │ - error: error, │ │ │ │ - source: source │ │ │ │ + root = this.textRoot; │ │ │ │ + if (root) { │ │ │ │ + while (child = root.firstChild) { │ │ │ │ + root.removeChild(child) │ │ │ │ } │ │ │ │ } │ │ │ │ - var response = this.parseResponse(arcNode); │ │ │ │ - return response │ │ │ │ - }, │ │ │ │ - write: function(request) { │ │ │ │ - if (!request) { │ │ │ │ - request = this.request │ │ │ │ + if (this.indexer) { │ │ │ │ + this.indexer.clear() │ │ │ │ } │ │ │ │ - var root = this.createElementNS("", "ARCXML"); │ │ │ │ - root.setAttribute("version", "1.1"); │ │ │ │ - var reqElem = this.createElementNS("", "REQUEST"); │ │ │ │ - if (request.get_image != null) { │ │ │ │ - var getElem = this.createElementNS("", "GET_IMAGE"); │ │ │ │ - reqElem.appendChild(getElem); │ │ │ │ - var propElem = this.createElementNS("", "PROPERTIES"); │ │ │ │ - getElem.appendChild(propElem); │ │ │ │ - var props = request.get_image.properties; │ │ │ │ - if (props.featurecoordsys != null) { │ │ │ │ - var feat = this.createElementNS("", "FEATURECOORDSYS"); │ │ │ │ - propElem.appendChild(feat); │ │ │ │ - if (props.featurecoordsys.id === 0) { │ │ │ │ - feat.setAttribute("string", props.featurecoordsys["string"]) │ │ │ │ - } else { │ │ │ │ - feat.setAttribute("id", props.featurecoordsys.id) │ │ │ │ - } │ │ │ │ + }, │ │ │ │ + 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 (props.filtercoordsys != null) { │ │ │ │ - var filt = this.createElementNS("", "FILTERCOORDSYS"); │ │ │ │ - propElem.appendChild(filt); │ │ │ │ - if (props.filtercoordsys.id === 0) { │ │ │ │ - filt.setAttribute("string", props.filtercoordsys.string) │ │ │ │ - } else { │ │ │ │ - filt.setAttribute("id", props.filtercoordsys.id) │ │ │ │ - } │ │ │ │ + if (rightOfDateLine !== this.rightOfDateLine || resolutionChanged) { │ │ │ │ + coordSysUnchanged = false; │ │ │ │ + this.xOffset = rightOfDateLine === true ? world.getWidth() / resolution : 0 │ │ │ │ } │ │ │ │ - if (props.envelope != null) { │ │ │ │ - var env = this.createElementNS("", "ENVELOPE"); │ │ │ │ - propElem.appendChild(env); │ │ │ │ - env.setAttribute("minx", props.envelope.minx); │ │ │ │ - env.setAttribute("miny", props.envelope.miny); │ │ │ │ - env.setAttribute("maxx", props.envelope.maxx); │ │ │ │ - env.setAttribute("maxy", props.envelope.maxy) │ │ │ │ + this.rightOfDateLine = rightOfDateLine │ │ │ │ + } │ │ │ │ + return coordSysUnchanged │ │ │ │ + }, │ │ │ │ + getNodeType: function(geometry, style) {}, │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - var imagesz = this.createElementNS("", "IMAGESIZE"); │ │ │ │ - propElem.appendChild(imagesz); │ │ │ │ - imagesz.setAttribute("height", props.imagesize.height); │ │ │ │ - imagesz.setAttribute("width", props.imagesize.width); │ │ │ │ - if (props.imagesize.height != props.imagesize.printheight || props.imagesize.width != props.imagesize.printwidth) { │ │ │ │ - imagesz.setAttribute("printheight", props.imagesize.printheight); │ │ │ │ - imagesz.setArrtibute("printwidth", props.imagesize.printwidth) │ │ │ │ + return rendered │ │ │ │ + } │ │ │ │ + rendered = false; │ │ │ │ + var removeBackground = false; │ │ │ │ + if (style.display != "none") { │ │ │ │ + if (style.backgroundGraphic) { │ │ │ │ + this.redrawBackgroundNode(geometry.id, geometry, style, featureId) │ │ │ │ + } else { │ │ │ │ + removeBackground = true │ │ │ │ } │ │ │ │ - if (props.background != null) { │ │ │ │ - var backgrnd = this.createElementNS("", "BACKGROUND"); │ │ │ │ - propElem.appendChild(backgrnd); │ │ │ │ - backgrnd.setAttribute("color", props.background.color.r + "," + props.background.color.g + "," + props.background.color.b); │ │ │ │ - if (props.background.transcolor !== null) { │ │ │ │ - backgrnd.setAttribute("transcolor", props.background.transcolor.r + "," + props.background.transcolor.g + "," + props.background.transcolor.b) │ │ │ │ + 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 (props.layerlist != null && props.layerlist.length > 0) { │ │ │ │ - var layerlst = this.createElementNS("", "LAYERLIST"); │ │ │ │ - propElem.appendChild(layerlst); │ │ │ │ - for (var ld = 0; ld < props.layerlist.length; ld++) { │ │ │ │ - var ldef = this.createElementNS("", "LAYERDEF"); │ │ │ │ - layerlst.appendChild(ldef); │ │ │ │ - ldef.setAttribute("id", props.layerlist[ld].id); │ │ │ │ - ldef.setAttribute("visible", props.layerlist[ld].visible); │ │ │ │ - if (typeof props.layerlist[ld].query == "object") { │ │ │ │ - var query = props.layerlist[ld].query; │ │ │ │ - if (query.where.length < 0) { │ │ │ │ - continue │ │ │ │ - } │ │ │ │ - var queryElem = null; │ │ │ │ - if (typeof query.spatialfilter == "boolean" && query.spatialfilter) { │ │ │ │ - queryElem = this.createElementNS("", "SPATIALQUERY") │ │ │ │ - } else { │ │ │ │ - queryElem = this.createElementNS("", "QUERY") │ │ │ │ - } │ │ │ │ - queryElem.setAttribute("where", query.where); │ │ │ │ - if (typeof query.accuracy == "number" && query.accuracy > 0) { │ │ │ │ - queryElem.setAttribute("accuracy", query.accuracy) │ │ │ │ - } │ │ │ │ - if (typeof query.featurelimit == "number" && query.featurelimit < 2e3) { │ │ │ │ - queryElem.setAttribute("featurelimit", query.featurelimit) │ │ │ │ - } │ │ │ │ - if (typeof query.subfields == "string" && query.subfields != "#ALL#") { │ │ │ │ - queryElem.setAttribute("subfields", query.subfields) │ │ │ │ - } │ │ │ │ - if (typeof query.joinexpression == "string" && query.joinexpression.length > 0) { │ │ │ │ - queryElem.setAttribute("joinexpression", query.joinexpression) │ │ │ │ - } │ │ │ │ - if (typeof query.jointables == "string" && query.jointables.length > 0) { │ │ │ │ - queryElem.setAttribute("jointables", query.jointables) │ │ │ │ - } │ │ │ │ - ldef.appendChild(queryElem) │ │ │ │ - } │ │ │ │ - if (typeof props.layerlist[ld].renderer == "object") { │ │ │ │ - this.addRenderer(ldef, props.layerlist[ld].renderer) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + } │ │ │ │ + if (removeBackground) { │ │ │ │ + var node = document.getElementById(geometry.id + this.BACKGROUND_ID_SUFFIX); │ │ │ │ + if (node) { │ │ │ │ + node.parentNode.removeChild(node) │ │ │ │ } │ │ │ │ - } else if (request.get_feature != null) { │ │ │ │ - var getElem = this.createElementNS("", "GET_FEATURES"); │ │ │ │ - getElem.setAttribute("outputmode", "newxml"); │ │ │ │ - getElem.setAttribute("checkesc", "true"); │ │ │ │ - if (request.get_feature.geometry) { │ │ │ │ - getElem.setAttribute("geometry", request.get_feature.geometry) │ │ │ │ + } │ │ │ │ + return rendered │ │ │ │ + }, │ │ │ │ + redrawNode: function(id, geometry, style, featureId) { │ │ │ │ + style = this.applyDefaultSymbolizer(style); │ │ │ │ + var node = this.nodeFactory(id, this.getNodeType(geometry, style)); │ │ │ │ + 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; │ │ │ │ + if (this.indexer) { │ │ │ │ + var insert = this.indexer.insert(node); │ │ │ │ + if (insert) { │ │ │ │ + this.vectorRoot.insertBefore(node, insert) │ │ │ │ } else { │ │ │ │ - getElem.setAttribute("geometry", "false") │ │ │ │ - } │ │ │ │ - if (request.get_feature.compact) { │ │ │ │ - getElem.setAttribute("compact", request.get_feature.compact) │ │ │ │ - } │ │ │ │ - if (request.get_feature.featurelimit == "number") { │ │ │ │ - getElem.setAttribute("featurelimit", request.get_feature.featurelimit) │ │ │ │ - } │ │ │ │ - getElem.setAttribute("globalenvelope", "true"); │ │ │ │ - reqElem.appendChild(getElem); │ │ │ │ - if (request.get_feature.layer != null && request.get_feature.layer.length > 0) { │ │ │ │ - var lyrElem = this.createElementNS("", "LAYER"); │ │ │ │ - lyrElem.setAttribute("id", request.get_feature.layer); │ │ │ │ - getElem.appendChild(lyrElem) │ │ │ │ + this.vectorRoot.appendChild(node) │ │ │ │ } │ │ │ │ - var fquery = request.get_feature.query; │ │ │ │ - if (fquery != null) { │ │ │ │ - var qElem = null; │ │ │ │ - if (fquery.isspatial) { │ │ │ │ - qElem = this.createElementNS("", "SPATIALQUERY") │ │ │ │ - } else { │ │ │ │ - qElem = this.createElementNS("", "QUERY") │ │ │ │ - } │ │ │ │ - getElem.appendChild(qElem); │ │ │ │ - if (typeof fquery.accuracy == "number") { │ │ │ │ - qElem.setAttribute("accuracy", fquery.accuracy) │ │ │ │ - } │ │ │ │ - if (fquery.featurecoordsys != null) { │ │ │ │ - var fcsElem1 = this.createElementNS("", "FEATURECOORDSYS"); │ │ │ │ - if (fquery.featurecoordsys.id == 0) { │ │ │ │ - fcsElem1.setAttribute("string", fquery.featurecoordsys.string) │ │ │ │ - } else { │ │ │ │ - fcsElem1.setAttribute("id", fquery.featurecoordsys.id) │ │ │ │ - } │ │ │ │ - qElem.appendChild(fcsElem1) │ │ │ │ - } │ │ │ │ - if (fquery.filtercoordsys != null) { │ │ │ │ - var fcsElem2 = this.createElementNS("", "FILTERCOORDSYS"); │ │ │ │ - if (fquery.filtercoordsys.id === 0) { │ │ │ │ - fcsElem2.setAttribute("string", fquery.filtercoordsys.string) │ │ │ │ - } else { │ │ │ │ - fcsElem2.setAttribute("id", fquery.filtercoordsys.id) │ │ │ │ - } │ │ │ │ - qElem.appendChild(fcsElem2) │ │ │ │ - } │ │ │ │ - if (fquery.buffer > 0) { │ │ │ │ - var bufElem = this.createElementNS("", "BUFFER"); │ │ │ │ - bufElem.setAttribute("distance", fquery.buffer); │ │ │ │ - qElem.appendChild(bufElem) │ │ │ │ - } │ │ │ │ - if (fquery.isspatial) { │ │ │ │ - var spfElem = this.createElementNS("", "SPATIALFILTER"); │ │ │ │ - spfElem.setAttribute("relation", fquery.spatialfilter.relation); │ │ │ │ - qElem.appendChild(spfElem); │ │ │ │ - if (fquery.spatialfilter.envelope) { │ │ │ │ - var envElem = this.createElementNS("", "ENVELOPE"); │ │ │ │ - envElem.setAttribute("minx", fquery.spatialfilter.envelope.minx); │ │ │ │ - envElem.setAttribute("miny", fquery.spatialfilter.envelope.miny); │ │ │ │ - envElem.setAttribute("maxx", fquery.spatialfilter.envelope.maxx); │ │ │ │ - envElem.setAttribute("maxy", fquery.spatialfilter.envelope.maxy); │ │ │ │ - spfElem.appendChild(envElem) │ │ │ │ - } else if (typeof fquery.spatialfilter.polygon == "object") { │ │ │ │ - spfElem.appendChild(this.writePolygonGeometry(fquery.spatialfilter.polygon)) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (fquery.where != null && fquery.where.length > 0) { │ │ │ │ - qElem.setAttribute("where", fquery.where) │ │ │ │ - } │ │ │ │ + } else { │ │ │ │ + if (node.parentNode !== this.vectorRoot) { │ │ │ │ + this.vectorRoot.appendChild(node) │ │ │ │ } │ │ │ │ } │ │ │ │ - root.appendChild(reqElem); │ │ │ │ - return OpenLayers.Format.XML.prototype.write.apply(this, [root]) │ │ │ │ + this.postDraw(node); │ │ │ │ + return drawResult.complete │ │ │ │ }, │ │ │ │ - addGroupRenderer: function(ldef, toprenderer) { │ │ │ │ - var topRelem = this.createElementNS("", "GROUPRENDERER"); │ │ │ │ - ldef.appendChild(topRelem); │ │ │ │ - for (var rind = 0; rind < toprenderer.length; rind++) { │ │ │ │ - var renderer = toprenderer[rind]; │ │ │ │ - this.addRenderer(topRelem, renderer) │ │ │ │ - } │ │ │ │ + redrawBackgroundNode: function(id, geometry, style, featureId) { │ │ │ │ + var backgroundStyle = OpenLayers.Util.extend({}, style); │ │ │ │ + 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; │ │ │ │ + backgroundStyle.backgroundGraphic = null; │ │ │ │ + backgroundStyle.backgroundXOffset = null; │ │ │ │ + backgroundStyle.backgroundYOffset = null; │ │ │ │ + backgroundStyle.backgroundGraphicZIndex = null; │ │ │ │ + return this.redrawNode(id + this.BACKGROUND_ID_SUFFIX, geometry, backgroundStyle, null) │ │ │ │ }, │ │ │ │ - addRenderer: function(topRelem, renderer) { │ │ │ │ - if (OpenLayers.Util.isArray(renderer)) { │ │ │ │ - this.addGroupRenderer(topRelem, renderer) │ │ │ │ - } else { │ │ │ │ - var renderElem = this.createElementNS("", renderer.type.toUpperCase() + "RENDERER"); │ │ │ │ - topRelem.appendChild(renderElem); │ │ │ │ - if (renderElem.tagName == "VALUEMAPRENDERER") { │ │ │ │ - this.addValueMapRenderer(renderElem, renderer) │ │ │ │ - } else if (renderElem.tagName == "VALUEMAPLABELRENDERER") { │ │ │ │ - this.addValueMapLabelRenderer(renderElem, renderer) │ │ │ │ - } else if (renderElem.tagName == "SIMPLELABELRENDERER") { │ │ │ │ - this.addSimpleLabelRenderer(renderElem, renderer) │ │ │ │ - } else if (renderElem.tagName == "SCALEDEPENDENTRENDERER") { │ │ │ │ - this.addScaleDependentRenderer(renderElem, renderer) │ │ │ │ + drawGeometryNode: function(node, geometry, style) { │ │ │ │ + style = style || node._style; │ │ │ │ + var options = { │ │ │ │ + isFilled: style.fill === undefined ? true : style.fill, │ │ │ │ + isStroked: style.stroke === undefined ? !!style.strokeWidth : style.stroke │ │ │ │ + }; │ │ │ │ + var drawn; │ │ │ │ + switch (geometry.CLASS_NAME) { │ │ │ │ + case "OpenLayers.Geometry.Point": │ │ │ │ + if (style.graphic === false) { │ │ │ │ + options.isFilled = false; │ │ │ │ + options.isStroked = false │ │ │ │ + } │ │ │ │ + drawn = this.drawPoint(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LineString": │ │ │ │ + options.isFilled = false; │ │ │ │ + drawn = this.drawLineString(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LinearRing": │ │ │ │ + drawn = this.drawLinearRing(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Polygon": │ │ │ │ + drawn = this.drawPolygon(node, geometry); │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Rectangle": │ │ │ │ + drawn = this.drawRectangle(node, geometry); │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + break │ │ │ │ + } │ │ │ │ + node._options = options; │ │ │ │ + if (drawn != false) { │ │ │ │ + return { │ │ │ │ + node: this.setStyle(node, style, options, geometry), │ │ │ │ + complete: drawn │ │ │ │ } │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ } │ │ │ │ }, │ │ │ │ - addScaleDependentRenderer: function(renderElem, renderer) { │ │ │ │ - if (typeof renderer.lower == "string" || typeof renderer.lower == "number") { │ │ │ │ - renderElem.setAttribute("lower", renderer.lower) │ │ │ │ + postDraw: function(node) {}, │ │ │ │ + drawPoint: function(node, geometry) {}, │ │ │ │ + drawLineString: function(node, geometry) {}, │ │ │ │ + drawLinearRing: function(node, geometry) {}, │ │ │ │ + drawPolygon: function(node, geometry) {}, │ │ │ │ + drawRectangle: function(node, geometry) {}, │ │ │ │ + drawCircle: function(node, geometry) {}, │ │ │ │ + removeText: function(featureId) { │ │ │ │ + var label = document.getElementById(featureId + this.LABEL_ID_SUFFIX); │ │ │ │ + if (label) { │ │ │ │ + this.textRoot.removeChild(label) │ │ │ │ } │ │ │ │ - if (typeof renderer.upper == "string" || typeof renderer.upper == "number") { │ │ │ │ - renderElem.setAttribute("upper", renderer.upper) │ │ │ │ + var outline = document.getElementById(featureId + this.LABEL_OUTLINE_SUFFIX); │ │ │ │ + if (outline) { │ │ │ │ + this.textRoot.removeChild(outline) │ │ │ │ } │ │ │ │ - this.addRenderer(renderElem, renderer.renderer) │ │ │ │ }, │ │ │ │ - addValueMapLabelRenderer: function(renderElem, renderer) { │ │ │ │ - renderElem.setAttribute("lookupfield", renderer.lookupfield); │ │ │ │ - renderElem.setAttribute("labelfield", renderer.labelfield); │ │ │ │ - if (typeof renderer.exacts == "object") { │ │ │ │ - for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) { │ │ │ │ - var exact = renderer.exacts[ext]; │ │ │ │ - var eelem = this.createElementNS("", "EXACT"); │ │ │ │ - if (typeof exact.value == "string") { │ │ │ │ - eelem.setAttribute("value", exact.value) │ │ │ │ - } │ │ │ │ - if (typeof exact.label == "string") { │ │ │ │ - eelem.setAttribute("label", exact.label) │ │ │ │ - } │ │ │ │ - if (typeof exact.method == "string") { │ │ │ │ - eelem.setAttribute("method", exact.method) │ │ │ │ - } │ │ │ │ - renderElem.appendChild(eelem); │ │ │ │ - if (typeof exact.symbol == "object") { │ │ │ │ - var selem = null; │ │ │ │ - if (exact.symbol.type == "text") { │ │ │ │ - selem = this.createElementNS("", "TEXTSYMBOL") │ │ │ │ - } │ │ │ │ - if (selem != null) { │ │ │ │ - var keys = this.fontStyleKeys; │ │ │ │ - for (var i = 0, len = keys.length; i < len; i++) { │ │ │ │ - var key = keys[i]; │ │ │ │ - if (exact.symbol[key]) { │ │ │ │ - selem.setAttribute(key, exact.symbol[key]) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - eelem.appendChild(selem) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + getFeatureIdFromEvent: function(evt) { │ │ │ │ + var target = evt.target; │ │ │ │ + var useElement = target && target.correspondingUseElement; │ │ │ │ + var node = useElement ? useElement : target || evt.srcElement; │ │ │ │ + return node._featureId │ │ │ │ }, │ │ │ │ - addValueMapRenderer: function(renderElem, renderer) { │ │ │ │ - renderElem.setAttribute("lookupfield", renderer.lookupfield); │ │ │ │ - if (typeof renderer.ranges == "object") { │ │ │ │ - for (var rng = 0, rnglen = renderer.ranges.length; rng < rnglen; rng++) { │ │ │ │ - var range = renderer.ranges[rng]; │ │ │ │ - var relem = this.createElementNS("", "RANGE"); │ │ │ │ - relem.setAttribute("lower", range.lower); │ │ │ │ - relem.setAttribute("upper", range.upper); │ │ │ │ - renderElem.appendChild(relem); │ │ │ │ - if (typeof range.symbol == "object") { │ │ │ │ - var selem = null; │ │ │ │ - if (range.symbol.type == "simplepolygon") { │ │ │ │ - selem = this.createElementNS("", "SIMPLEPOLYGONSYMBOL") │ │ │ │ - } │ │ │ │ - if (selem != null) { │ │ │ │ - if (typeof range.symbol.boundarycolor == "string") { │ │ │ │ - selem.setAttribute("boundarycolor", range.symbol.boundarycolor) │ │ │ │ - } │ │ │ │ - if (typeof range.symbol.fillcolor == "string") { │ │ │ │ - selem.setAttribute("fillcolor", range.symbol.fillcolor) │ │ │ │ - } │ │ │ │ - if (typeof range.symbol.filltransparency == "number") { │ │ │ │ - selem.setAttribute("filltransparency", range.symbol.filltransparency) │ │ │ │ - } │ │ │ │ - relem.appendChild(selem) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + 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 if (typeof renderer.exacts == "object") { │ │ │ │ - for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) { │ │ │ │ - var exact = renderer.exacts[ext]; │ │ │ │ - var eelem = this.createElementNS("", "EXACT"); │ │ │ │ - if (typeof exact.value == "string") { │ │ │ │ - eelem.setAttribute("value", exact.value) │ │ │ │ - } │ │ │ │ - if (typeof exact.label == "string") { │ │ │ │ - eelem.setAttribute("label", exact.label) │ │ │ │ + } else { │ │ │ │ + var element = OpenLayers.Util.getElement(geometry.id); │ │ │ │ + if (element && element.parentNode) { │ │ │ │ + if (element.geometry) { │ │ │ │ + element.geometry.destroy(); │ │ │ │ + element.geometry = null │ │ │ │ } │ │ │ │ - if (typeof exact.method == "string") { │ │ │ │ - eelem.setAttribute("method", exact.method) │ │ │ │ + element.parentNode.removeChild(element); │ │ │ │ + if (this.indexer) { │ │ │ │ + this.indexer.remove(element) │ │ │ │ } │ │ │ │ - renderElem.appendChild(eelem); │ │ │ │ - if (typeof exact.symbol == "object") { │ │ │ │ - var selem = null; │ │ │ │ - if (exact.symbol.type == "simplemarker") { │ │ │ │ - selem = this.createElementNS("", "SIMPLEMARKERSYMBOL") │ │ │ │ - } │ │ │ │ - if (selem != null) { │ │ │ │ - if (typeof exact.symbol.antialiasing == "string") { │ │ │ │ - selem.setAttribute("antialiasing", exact.symbol.antialiasing) │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.color == "string") { │ │ │ │ - selem.setAttribute("color", exact.symbol.color) │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.outline == "string") { │ │ │ │ - selem.setAttribute("outline", exact.symbol.outline) │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.overlap == "string") { │ │ │ │ - selem.setAttribute("overlap", exact.symbol.overlap) │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.shadow == "string") { │ │ │ │ - selem.setAttribute("shadow", exact.symbol.shadow) │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.transparency == "number") { │ │ │ │ - selem.setAttribute("transparency", exact.symbol.transparency) │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.usecentroid == "string") { │ │ │ │ - selem.setAttribute("usecentroid", exact.symbol.usecentroid) │ │ │ │ - } │ │ │ │ - if (typeof exact.symbol.width == "number") { │ │ │ │ - selem.setAttribute("width", exact.symbol.width) │ │ │ │ - } │ │ │ │ - eelem.appendChild(selem) │ │ │ │ + if (element._style.backgroundGraphic) { │ │ │ │ + var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX; │ │ │ │ + var bElem = OpenLayers.Util.getElement(backgroundId); │ │ │ │ + if (bElem && bElem.parentNode) { │ │ │ │ + bElem.parentNode.removeChild(bElem) │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ - addSimpleLabelRenderer: function(renderElem, renderer) { │ │ │ │ - renderElem.setAttribute("field", renderer.field); │ │ │ │ - var keys = ["featureweight", "howmanylabels", "labelbufferratio", "labelpriorities", "labelweight", "linelabelposition", "rotationalangles"]; │ │ │ │ - for (var i = 0, len = keys.length; i < len; i++) { │ │ │ │ - var key = keys[i]; │ │ │ │ - if (renderer[key]) { │ │ │ │ - renderElem.setAttribute(key, renderer[key]) │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - if (renderer.symbol.type == "text") { │ │ │ │ - var symbol = renderer.symbol; │ │ │ │ - var selem = this.createElementNS("", "TEXTSYMBOL"); │ │ │ │ - renderElem.appendChild(selem); │ │ │ │ - var keys = this.fontStyleKeys; │ │ │ │ - for (var i = 0, len = keys.length; i < len; i++) { │ │ │ │ - var key = keys[i]; │ │ │ │ - if (symbol[key]) { │ │ │ │ - selem.setAttribute(key, renderer[key]) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + return node │ │ │ │ + }, │ │ │ │ + nodeTypeCompare: function(node, type) {}, │ │ │ │ + createNode: function(type, id) {}, │ │ │ │ + moveRoot: function(renderer) { │ │ │ │ + var root = this.root; │ │ │ │ + if (renderer.root.parentNode == this.rendererRoot) { │ │ │ │ + root = renderer.root │ │ │ │ } │ │ │ │ + root.parentNode.removeChild(root); │ │ │ │ + renderer.rendererRoot.appendChild(root) │ │ │ │ }, │ │ │ │ - writePolygonGeometry: function(polygon) { │ │ │ │ - if (!(polygon instanceof OpenLayers.Geometry.Polygon)) { │ │ │ │ - throw { │ │ │ │ - message: "Cannot write polygon geometry to ArcXML with an " + polygon.CLASS_NAME + " object.", │ │ │ │ - geometry: polygon │ │ │ │ - } │ │ │ │ + getRenderLayerId: function() { │ │ │ │ + return this.root.parentNode.parentNode.id │ │ │ │ + }, │ │ │ │ + isComplexSymbol: function(graphicName) { │ │ │ │ + return graphicName != "circle" && !!graphicName │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.Elements" │ │ │ │ +}); │ │ │ │ +OpenLayers.Renderer.VML = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ + xmlns: "urn:schemas-microsoft-com:vml", │ │ │ │ + symbolCache: {}, │ │ │ │ + offset: null, │ │ │ │ + initialize: function(containerID) { │ │ │ │ + if (!this.supported()) { │ │ │ │ + return │ │ │ │ } │ │ │ │ - var polyElem = this.createElementNS("", "POLYGON"); │ │ │ │ - for (var ln = 0, lnlen = polygon.components.length; ln < lnlen; ln++) { │ │ │ │ - var ring = polygon.components[ln]; │ │ │ │ - var ringElem = this.createElementNS("", "RING"); │ │ │ │ - for (var rn = 0, rnlen = ring.components.length; rn < rnlen; rn++) { │ │ │ │ - var point = ring.components[rn]; │ │ │ │ - var pointElem = this.createElementNS("", "POINT"); │ │ │ │ - pointElem.setAttribute("x", point.x); │ │ │ │ - pointElem.setAttribute("y", point.y); │ │ │ │ - ringElem.appendChild(pointElem) │ │ │ │ + if (!document.namespaces.olv) { │ │ │ │ + document.namespaces.add("olv", this.xmlns); │ │ │ │ + var style = document.createStyleSheet(); │ │ │ │ + var shapes = ["shape", "rect", "oval", "fill", "stroke", "imagedata", "group", "textbox"]; │ │ │ │ + for (var i = 0, len = shapes.length; i < len; i++) { │ │ │ │ + style.addRule("olv\\:" + shapes[i], "behavior: url(#default#VML); " + "position: absolute; display: inline-block;") │ │ │ │ } │ │ │ │ - polyElem.appendChild(ringElem) │ │ │ │ } │ │ │ │ - return polyElem │ │ │ │ + OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments) │ │ │ │ }, │ │ │ │ - parseResponse: function(data) { │ │ │ │ - if (typeof data == "string") { │ │ │ │ - var newData = new OpenLayers.Format.XML; │ │ │ │ - data = newData.read(data) │ │ │ │ - } │ │ │ │ - var response = new OpenLayers.Format.ArcXML.Response; │ │ │ │ - var errorNode = data.getElementsByTagName("ERROR"); │ │ │ │ - if (errorNode != null && errorNode.length > 0) { │ │ │ │ - response.error = this.getChildValue(errorNode, "Unknown error.") │ │ │ │ + supported: function() { │ │ │ │ + return !!document.namespaces │ │ │ │ + }, │ │ │ │ + setExtent: function(extent, resolutionChanged) { │ │ │ │ + var coordSysUnchanged = OpenLayers.Renderer.Elements.prototype.setExtent.apply(this, arguments); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var left = extent.left / resolution | 0; │ │ │ │ + var top = extent.top / resolution - this.size.h | 0; │ │ │ │ + if (resolutionChanged || !this.offset) { │ │ │ │ + this.offset = { │ │ │ │ + x: left, │ │ │ │ + y: top │ │ │ │ + }; │ │ │ │ + left = 0; │ │ │ │ + top = 0 │ │ │ │ } else { │ │ │ │ - var responseNode = data.getElementsByTagName("RESPONSE"); │ │ │ │ - if (responseNode == null || responseNode.length == 0) { │ │ │ │ - response.error = "No RESPONSE tag found in ArcXML response."; │ │ │ │ - return response │ │ │ │ - } │ │ │ │ - var rtype = responseNode[0].firstChild.nodeName; │ │ │ │ - if (rtype == "#text") { │ │ │ │ - rtype = responseNode[0].firstChild.nextSibling.nodeName │ │ │ │ - } │ │ │ │ - if (rtype == "IMAGE") { │ │ │ │ - var envelopeNode = data.getElementsByTagName("ENVELOPE"); │ │ │ │ - var outputNode = data.getElementsByTagName("OUTPUT"); │ │ │ │ - if (envelopeNode == null || envelopeNode.length == 0) { │ │ │ │ - response.error = "No ENVELOPE tag found in ArcXML response." │ │ │ │ - } else if (outputNode == null || outputNode.length == 0) { │ │ │ │ - response.error = "No OUTPUT tag found in ArcXML response." │ │ │ │ - } else { │ │ │ │ - var envAttr = this.parseAttributes(envelopeNode[0]); │ │ │ │ - var outputAttr = this.parseAttributes(outputNode[0]); │ │ │ │ - if (typeof outputAttr.type == "string") { │ │ │ │ - response.image = { │ │ │ │ - envelope: envAttr, │ │ │ │ - output: { │ │ │ │ - type: outputAttr.type, │ │ │ │ - data: this.getChildValue(outputNode[0]) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - response.image = { │ │ │ │ - envelope: envAttr, │ │ │ │ - output: outputAttr │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else if (rtype == "FEATURES") { │ │ │ │ - var features = responseNode[0].getElementsByTagName("FEATURES"); │ │ │ │ - var featureCount = features[0].getElementsByTagName("FEATURECOUNT"); │ │ │ │ - response.features.featurecount = featureCount[0].getAttribute("count"); │ │ │ │ - if (response.features.featurecount > 0) { │ │ │ │ - var envelope = features[0].getElementsByTagName("ENVELOPE"); │ │ │ │ - response.features.envelope = this.parseAttributes(envelope[0], typeof 0); │ │ │ │ - var featureList = features[0].getElementsByTagName("FEATURE"); │ │ │ │ - for (var fn = 0; fn < featureList.length; fn++) { │ │ │ │ - var feature = new OpenLayers.Feature.Vector; │ │ │ │ - var fields = featureList[fn].getElementsByTagName("FIELD"); │ │ │ │ - for (var fdn = 0; fdn < fields.length; fdn++) { │ │ │ │ - var fieldName = fields[fdn].getAttribute("name"); │ │ │ │ - var fieldValue = fields[fdn].getAttribute("value"); │ │ │ │ - feature.attributes[fieldName] = fieldValue │ │ │ │ - } │ │ │ │ - var geom = featureList[fn].getElementsByTagName("POLYGON"); │ │ │ │ - if (geom.length > 0) { │ │ │ │ - var ring = geom[0].getElementsByTagName("RING"); │ │ │ │ - var polys = []; │ │ │ │ - for (var rn = 0; rn < ring.length; rn++) { │ │ │ │ - var linearRings = []; │ │ │ │ - linearRings.push(this.parsePointGeometry(ring[rn])); │ │ │ │ - var holes = ring[rn].getElementsByTagName("HOLE"); │ │ │ │ - for (var hn = 0; hn < holes.length; hn++) { │ │ │ │ - linearRings.push(this.parsePointGeometry(holes[hn])) │ │ │ │ - } │ │ │ │ - holes = null; │ │ │ │ - polys.push(new OpenLayers.Geometry.Polygon(linearRings)); │ │ │ │ - linearRings = null │ │ │ │ - } │ │ │ │ - ring = null; │ │ │ │ - if (polys.length == 1) { │ │ │ │ - feature.geometry = polys[0] │ │ │ │ - } else { │ │ │ │ - feature.geometry = new OpenLayers.Geometry.MultiPolygon(polys) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - response.features.feature.push(feature) │ │ │ │ - } │ │ │ │ + left = left - this.offset.x; │ │ │ │ + top = top - this.offset.y │ │ │ │ + } │ │ │ │ + var org = left - this.xOffset + " " + top; │ │ │ │ + this.root.coordorigin = org; │ │ │ │ + var roots = [this.root, this.vectorRoot, this.textRoot]; │ │ │ │ + var root; │ │ │ │ + for (var i = 0, len = roots.length; i < len; ++i) { │ │ │ │ + root = roots[i]; │ │ │ │ + var size = this.size.w + " " + this.size.h; │ │ │ │ + root.coordsize = size │ │ │ │ + } │ │ │ │ + this.root.style.flip = "y"; │ │ │ │ + return coordSysUnchanged │ │ │ │ + }, │ │ │ │ + setSize: function(size) { │ │ │ │ + OpenLayers.Renderer.prototype.setSize.apply(this, arguments); │ │ │ │ + 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 │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + 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" │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - response.error = "Unidentified response type." │ │ │ │ - } │ │ │ │ + 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 response │ │ │ │ + return nodeType │ │ │ │ }, │ │ │ │ - parseAttributes: function(node, type) { │ │ │ │ - var attributes = {}; │ │ │ │ - for (var attr = 0; attr < node.attributes.length; attr++) { │ │ │ │ - if (type == "number") { │ │ │ │ - attributes[node.attributes[attr].nodeName] = parseFloat(node.attributes[attr].nodeValue) │ │ │ │ + setStyle: function(node, style, options, geometry) { │ │ │ │ + style = style || node._style; │ │ │ │ + options = options || node._options; │ │ │ │ + var fillColor = style.fillColor; │ │ │ │ + var title = style.title || style.graphicTitle; │ │ │ │ + if (title) { │ │ │ │ + node.title = title │ │ │ │ + } │ │ │ │ + if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ + if (style.externalGraphic) { │ │ │ │ + options.isFilled = true; │ │ │ │ + var width = style.graphicWidth || style.graphicHeight; │ │ │ │ + var height = style.graphicHeight || style.graphicWidth; │ │ │ │ + width = width ? width : style.pointRadius * 2; │ │ │ │ + height = height ? height : style.pointRadius * 2; │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var xOffset = style.graphicXOffset != undefined ? style.graphicXOffset : -(.5 * width); │ │ │ │ + var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.5 * height); │ │ │ │ + node.style.left = ((geometry.x - this.featureDx) / resolution - this.offset.x + xOffset | 0) + "px"; │ │ │ │ + node.style.top = (geometry.y / resolution - this.offset.y - (yOffset + height) | 0) + "px"; │ │ │ │ + node.style.width = width + "px"; │ │ │ │ + node.style.height = height + "px"; │ │ │ │ + node.style.flip = "y"; │ │ │ │ + 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 { │ │ │ │ - attributes[node.attributes[attr].nodeName] = node.attributes[attr].nodeValue │ │ │ │ + this.drawCircle(node, geometry, style.pointRadius) │ │ │ │ } │ │ │ │ } │ │ │ │ - return attributes │ │ │ │ - }, │ │ │ │ - parsePointGeometry: function(node) { │ │ │ │ - var ringPoints = []; │ │ │ │ - var coords = node.getElementsByTagName("COORDS"); │ │ │ │ - if (coords.length > 0) { │ │ │ │ - var coordArr = this.getChildValue(coords[0]); │ │ │ │ - coordArr = coordArr.split(/;/); │ │ │ │ - for (var cn = 0; cn < coordArr.length; cn++) { │ │ │ │ - var coordItems = coordArr[cn].split(/ /); │ │ │ │ - ringPoints.push(new OpenLayers.Geometry.Point(coordItems[0], coordItems[1])) │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - coords = null │ │ │ │ } else { │ │ │ │ - var point = node.getElementsByTagName("POINT"); │ │ │ │ - if (point.length > 0) { │ │ │ │ - for (var pn = 0; pn < point.length; pn++) { │ │ │ │ - ringPoints.push(new OpenLayers.Geometry.Point(parseFloat(point[pn].getAttribute("x")), parseFloat(point[pn].getAttribute("y")))) │ │ │ │ - } │ │ │ │ + if (!fill) { │ │ │ │ + fill = this.createNode("olv:fill", node.id + "_fill") │ │ │ │ } │ │ │ │ - point = null │ │ │ │ - } │ │ │ │ - return new OpenLayers.Geometry.LinearRing(ringPoints) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Format.ArcXML" │ │ │ │ -}); │ │ │ │ -OpenLayers.Format.ArcXML.Request = OpenLayers.Class({ │ │ │ │ - initialize: function(params) { │ │ │ │ - var defaults = { │ │ │ │ - get_image: { │ │ │ │ - properties: { │ │ │ │ - background: null, │ │ │ │ - draw: true, │ │ │ │ - envelope: { │ │ │ │ - minx: 0, │ │ │ │ - miny: 0, │ │ │ │ - maxx: 0, │ │ │ │ - maxy: 0 │ │ │ │ - }, │ │ │ │ - featurecoordsys: { │ │ │ │ - id: 0, │ │ │ │ - string: "", │ │ │ │ - datumtransformid: 0, │ │ │ │ - datumtransformstring: "" │ │ │ │ - }, │ │ │ │ - filtercoordsys: { │ │ │ │ - id: 0, │ │ │ │ - string: "", │ │ │ │ - datumtransformid: 0, │ │ │ │ - datumtransformstring: "" │ │ │ │ - }, │ │ │ │ - imagesize: { │ │ │ │ - height: 0, │ │ │ │ - width: 0, │ │ │ │ - dpi: 96, │ │ │ │ - printheight: 0, │ │ │ │ - printwidth: 0, │ │ │ │ - scalesymbols: false │ │ │ │ - }, │ │ │ │ - layerlist: [], │ │ │ │ - output: { │ │ │ │ - baseurl: "", │ │ │ │ - legendbaseurl: "", │ │ │ │ - legendname: "", │ │ │ │ - legendpath: "", │ │ │ │ - legendurl: "", │ │ │ │ - name: "", │ │ │ │ - path: "", │ │ │ │ - type: "jpg", │ │ │ │ - url: "" │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - get_feature: { │ │ │ │ - layer: "", │ │ │ │ - query: { │ │ │ │ - isspatial: false, │ │ │ │ - featurecoordsys: { │ │ │ │ - id: 0, │ │ │ │ - string: "", │ │ │ │ - datumtransformid: 0, │ │ │ │ - datumtransformstring: "" │ │ │ │ - }, │ │ │ │ - filtercoordsys: { │ │ │ │ - id: 0, │ │ │ │ - string: "", │ │ │ │ - datumtransformid: 0, │ │ │ │ - datumtransformstring: "" │ │ │ │ - }, │ │ │ │ - buffer: 0, │ │ │ │ - where: "", │ │ │ │ - spatialfilter: { │ │ │ │ - relation: "envelope_intersection", │ │ │ │ - envelope: null │ │ │ │ - } │ │ │ │ + fill.opacity = style.fillOpacity; │ │ │ │ + if (node._geometryClass === "OpenLayers.Geometry.Point" && style.externalGraphic) { │ │ │ │ + if (style.graphicOpacity) { │ │ │ │ + fill.opacity = style.graphicOpacity │ │ │ │ } │ │ │ │ - }, │ │ │ │ - environment: { │ │ │ │ - separators: { │ │ │ │ - cs: " ", │ │ │ │ - ts: ";" │ │ │ │ + fill.src = style.externalGraphic; │ │ │ │ + fill.type = "frame"; │ │ │ │ + if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ + fill.aspect = "atmost" │ │ │ │ } │ │ │ │ - }, │ │ │ │ - layer: [], │ │ │ │ - workspaces: [] │ │ │ │ - }; │ │ │ │ - return OpenLayers.Util.extend(this, defaults) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Format.ArcXML.Request" │ │ │ │ -}); │ │ │ │ -OpenLayers.Format.ArcXML.Response = OpenLayers.Class({ │ │ │ │ - initialize: function(params) { │ │ │ │ - var defaults = { │ │ │ │ - image: { │ │ │ │ - envelope: null, │ │ │ │ - output: "" │ │ │ │ - }, │ │ │ │ - features: { │ │ │ │ - featurecount: 0, │ │ │ │ - envelope: null, │ │ │ │ - feature: [] │ │ │ │ - }, │ │ │ │ - error: "" │ │ │ │ - }; │ │ │ │ - return OpenLayers.Util.extend(this, defaults) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Format.ArcXML.Response" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.ArcIMS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - DEFAULT_PARAMS: { │ │ │ │ - ClientVersion: "9.2", │ │ │ │ - ServiceName: "" │ │ │ │ - }, │ │ │ │ - featureCoordSys: "4326", │ │ │ │ - filterCoordSys: "4326", │ │ │ │ - layers: null, │ │ │ │ - async: true, │ │ │ │ - name: "ArcIMS", │ │ │ │ - isBaseLayer: true, │ │ │ │ - DEFAULT_OPTIONS: { │ │ │ │ - tileSize: new OpenLayers.Size(512, 512), │ │ │ │ - featureCoordSys: "4326", │ │ │ │ - filterCoordSys: "4326", │ │ │ │ - layers: null, │ │ │ │ - isBaseLayer: true, │ │ │ │ - async: true, │ │ │ │ - name: "ArcIMS" │ │ │ │ - }, │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - this.tileSize = new OpenLayers.Size(512, 512); │ │ │ │ - this.params = OpenLayers.Util.applyDefaults({ │ │ │ │ - ServiceName: options.serviceName │ │ │ │ - }, this.DEFAULT_PARAMS); │ │ │ │ - this.options = OpenLayers.Util.applyDefaults(options, this.DEFAULT_OPTIONS); │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, [name, url, this.params, options]); │ │ │ │ - if (this.transparent) { │ │ │ │ - if (!this.isBaseLayer) { │ │ │ │ - this.isBaseLayer = false │ │ │ │ } │ │ │ │ - if (this.format == "image/jpeg") { │ │ │ │ - this.format = OpenLayers.Util.alphaHack() ? "image/gif" : "image/png" │ │ │ │ + if (fill.parentNode != node) { │ │ │ │ + node.appendChild(fill) │ │ │ │ } │ │ │ │ } │ │ │ │ - if (this.options.layers === null) { │ │ │ │ - this.options.layers = [] │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - var url = ""; │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var axlReq = new OpenLayers.Format.ArcXML(OpenLayers.Util.extend(this.options, { │ │ │ │ - requesttype: "image", │ │ │ │ - envelope: bounds.toArray(), │ │ │ │ - tileSize: this.tileSize │ │ │ │ - })); │ │ │ │ - var req = new OpenLayers.Request.POST({ │ │ │ │ - url: this.getFullRequestString(), │ │ │ │ - data: axlReq.write(), │ │ │ │ - async: false │ │ │ │ - }); │ │ │ │ - if (req != null) { │ │ │ │ - var doc = req.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = req.responseText │ │ │ │ + var rotation = style.rotation; │ │ │ │ + if (rotation !== undefined || node._rotation !== undefined) { │ │ │ │ + node._rotation = rotation; │ │ │ │ + if (style.externalGraphic) { │ │ │ │ + this.graphicRotate(node, xOffset, yOffset, style); │ │ │ │ + fill.opacity = 0 │ │ │ │ + } else if (node._geometryClass === "OpenLayers.Geometry.Point") { │ │ │ │ + node.style.rotation = rotation || 0 │ │ │ │ } │ │ │ │ - var axlResp = new OpenLayers.Format.ArcXML; │ │ │ │ - var arcxml = axlResp.read(doc); │ │ │ │ - url = this.getUrlOrImage(arcxml.image.output) │ │ │ │ - } │ │ │ │ - return url │ │ │ │ - }, │ │ │ │ - getURLasync: function(bounds, callback, scope) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var axlReq = new OpenLayers.Format.ArcXML(OpenLayers.Util.extend(this.options, { │ │ │ │ - requesttype: "image", │ │ │ │ - envelope: bounds.toArray(), │ │ │ │ - tileSize: this.tileSize │ │ │ │ - })); │ │ │ │ - OpenLayers.Request.POST({ │ │ │ │ - url: this.getFullRequestString(), │ │ │ │ - async: true, │ │ │ │ - data: axlReq.write(), │ │ │ │ - callback: function(req) { │ │ │ │ - var doc = req.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = req.responseText │ │ │ │ - } │ │ │ │ - var axlResp = new OpenLayers.Format.ArcXML; │ │ │ │ - var arcxml = axlResp.read(doc); │ │ │ │ - callback.call(scope, this.getUrlOrImage(arcxml.image.output)) │ │ │ │ - }, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - getUrlOrImage: function(output) { │ │ │ │ - var ret = ""; │ │ │ │ - if (output.url) { │ │ │ │ - ret = output.url │ │ │ │ - } else if (output.data) { │ │ │ │ - ret = "data:image/" + output.type + ";base64," + output.data │ │ │ │ } │ │ │ │ - return ret │ │ │ │ - }, │ │ │ │ - setLayerQuery: function(id, querydef) { │ │ │ │ - for (var lyr = 0; lyr < this.options.layers.length; lyr++) { │ │ │ │ - if (id == this.options.layers[lyr].id) { │ │ │ │ - this.options.layers[lyr].query = querydef; │ │ │ │ - return │ │ │ │ + var strokes = node.getElementsByTagName("stroke"); │ │ │ │ + var stroke = strokes.length == 0 ? null : strokes[0]; │ │ │ │ + if (!options.isStroked) { │ │ │ │ + node.stroked = false; │ │ │ │ + if (stroke) { │ │ │ │ + stroke.on = false │ │ │ │ } │ │ │ │ - } │ │ │ │ - this.options.layers.push({ │ │ │ │ - id: id, │ │ │ │ - visible: true, │ │ │ │ - query: querydef │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - getFeatureInfo: function(geometry, layer, options) { │ │ │ │ - var buffer = options.buffer || 1; │ │ │ │ - var callback = options.callback || function() {}; │ │ │ │ - var scope = options.scope || window; │ │ │ │ - var requestOptions = {}; │ │ │ │ - OpenLayers.Util.extend(requestOptions, this.options); │ │ │ │ - requestOptions.requesttype = "feature"; │ │ │ │ - if (geometry instanceof OpenLayers.LonLat) { │ │ │ │ - requestOptions.polygon = null; │ │ │ │ - requestOptions.envelope = [geometry.lon - buffer, geometry.lat - buffer, geometry.lon + buffer, geometry.lat + buffer] │ │ │ │ - } else if (geometry instanceof OpenLayers.Geometry.Polygon) { │ │ │ │ - requestOptions.envelope = null; │ │ │ │ - requestOptions.polygon = geometry │ │ │ │ - } │ │ │ │ - var arcxml = new OpenLayers.Format.ArcXML(requestOptions); │ │ │ │ - OpenLayers.Util.extend(arcxml.request.get_feature, options); │ │ │ │ - arcxml.request.get_feature.layer = layer.id; │ │ │ │ - if (typeof layer.query.accuracy == "number") { │ │ │ │ - arcxml.request.get_feature.query.accuracy = layer.query.accuracy │ │ │ │ } else { │ │ │ │ - var mapCenter = this.map.getCenter(); │ │ │ │ - var viewPx = this.map.getViewPortPxFromLonLat(mapCenter); │ │ │ │ - viewPx.x++; │ │ │ │ - var mapOffCenter = this.map.getLonLatFromPixel(viewPx); │ │ │ │ - arcxml.request.get_feature.query.accuracy = mapOffCenter.lon - mapCenter.lon │ │ │ │ - } │ │ │ │ - arcxml.request.get_feature.query.where = layer.query.where; │ │ │ │ - arcxml.request.get_feature.query.spatialfilter.relation = "area_intersection"; │ │ │ │ - OpenLayers.Request.POST({ │ │ │ │ - url: this.getFullRequestString({ │ │ │ │ - CustomService: "Query" │ │ │ │ - }), │ │ │ │ - data: arcxml.write(), │ │ │ │ - callback: function(request) { │ │ │ │ - var response = arcxml.parseResponse(request.responseText); │ │ │ │ - if (!arcxml.iserror()) { │ │ │ │ - callback.call(scope, response.features) │ │ │ │ - } else { │ │ │ │ - callback.call(scope, null) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.ArcIMS(this.name, this.url, this.getOptions()) │ │ │ │ - } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.ArcIMS" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.ArcGIS93Rest = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - DEFAULT_PARAMS: { │ │ │ │ - format: "png" │ │ │ │ - }, │ │ │ │ - isBaseLayer: true, │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - var newArguments = []; │ │ │ │ - params = OpenLayers.Util.upperCaseObject(params); │ │ │ │ - newArguments.push(name, url, params, options); │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, newArguments); │ │ │ │ - OpenLayers.Util.applyDefaults(this.params, OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS)); │ │ │ │ - if (this.params.TRANSPARENT && this.params.TRANSPARENT.toString().toLowerCase() == "true") { │ │ │ │ - if (options == null || !options.isBaseLayer) { │ │ │ │ - this.isBaseLayer = false │ │ │ │ + if (!stroke) { │ │ │ │ + stroke = this.createNode("olv:stroke", node.id + "_stroke"); │ │ │ │ + node.appendChild(stroke) │ │ │ │ } │ │ │ │ - if (this.params.FORMAT == "jpg") { │ │ │ │ - this.params.FORMAT = OpenLayers.Util.alphaHack() ? "gif" : "png" │ │ │ │ + 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) │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.ArcGIS93Rest(this.name, this.url, this.params, this.getOptions()) │ │ │ │ + if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ + node.style.cursor = style.cursor │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var projWords = this.projection.getCode().split(":"); │ │ │ │ - var srid = projWords[projWords.length - 1]; │ │ │ │ - var imageSize = this.getImageSize(); │ │ │ │ - var newParams = { │ │ │ │ - BBOX: bounds.toBBOX(), │ │ │ │ - SIZE: imageSize.w + "," + imageSize.h, │ │ │ │ - F: "image", │ │ │ │ - BBOXSR: srid, │ │ │ │ - IMAGESR: srid │ │ │ │ - }; │ │ │ │ - if (this.layerDefs) { │ │ │ │ - var layerDefStrList = []; │ │ │ │ - var layerID; │ │ │ │ - for (layerID in this.layerDefs) { │ │ │ │ - if (this.layerDefs.hasOwnProperty(layerID)) { │ │ │ │ - if (this.layerDefs[layerID]) { │ │ │ │ - layerDefStrList.push(layerID); │ │ │ │ - layerDefStrList.push(":"); │ │ │ │ - layerDefStrList.push(this.layerDefs[layerID]); │ │ │ │ - layerDefStrList.push(";") │ │ │ │ - } │ │ │ │ + graphicRotate: function(node, xOffset, yOffset, style) { │ │ │ │ + var style = style || node._style; │ │ │ │ + var rotation = style.rotation || 0; │ │ │ │ + var aspectRatio, size; │ │ │ │ + if (!(style.graphicWidth && style.graphicHeight)) { │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - } │ │ │ │ - if (layerDefStrList.length > 0) { │ │ │ │ - newParams["LAYERDEFS"] = layerDefStrList.join("") │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var requestString = this.getFullRequestString(newParams); │ │ │ │ - return requestString │ │ │ │ - }, │ │ │ │ - setLayerFilter: function(id, queryDef) { │ │ │ │ - if (!this.layerDefs) { │ │ │ │ - this.layerDefs = {} │ │ │ │ - } │ │ │ │ - if (queryDef) { │ │ │ │ - this.layerDefs[id] = queryDef │ │ │ │ + }, this); │ │ │ │ + img.src = style.externalGraphic; │ │ │ │ + return │ │ │ │ } else { │ │ │ │ - delete this.layerDefs[id] │ │ │ │ + size = Math.max(style.graphicWidth, style.graphicHeight); │ │ │ │ + aspectRatio = style.graphicWidth / style.graphicHeight │ │ │ │ } │ │ │ │ - }, │ │ │ │ - clearLayerFilter: function(id) { │ │ │ │ - if (id) { │ │ │ │ - delete this.layerDefs[id] │ │ │ │ - } else { │ │ │ │ - delete this.layerDefs │ │ │ │ + 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"; │ │ │ │ + var image = document.getElementById(node.id + "_image"); │ │ │ │ + if (!image) { │ │ │ │ + image = this.createNode("olv:imagedata", node.id + "_image"); │ │ │ │ + node.appendChild(image) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - mergeNewParams: function(newParams) { │ │ │ │ - var upperParams = OpenLayers.Util.upperCaseObject(newParams); │ │ │ │ - var newArguments = [upperParams]; │ │ │ │ - return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, newArguments) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.ArcGIS93Rest" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.OSM = OpenLayers.Class(OpenLayers.Layer.XYZ, { │ │ │ │ - name: "OpenStreetMap", │ │ │ │ - url: ["http://a.tile.openstreetmap.org/${z}/${x}/${y}.png", "http://b.tile.openstreetmap.org/${z}/${x}/${y}.png", "http://c.tile.openstreetmap.org/${z}/${x}/${y}.png"], │ │ │ │ - attribution: "© <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors", │ │ │ │ - sphericalMercator: true, │ │ │ │ - wrapDateLine: true, │ │ │ │ - tileOptions: null, │ │ │ │ - initialize: function(name, url, options) { │ │ │ │ - OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments); │ │ │ │ - this.tileOptions = OpenLayers.Util.extend({ │ │ │ │ - crossOriginKeyword: "anonymous" │ │ │ │ - }, this.options && this.options.tileOptions) │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.OSM(this.name, this.url, this.getOptions()) │ │ │ │ + image.style.width = width + "px"; │ │ │ │ + image.style.height = height + "px"; │ │ │ │ + image.src = style.externalGraphic; │ │ │ │ + image.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(" + "src='', sizingMethod='scale')"; │ │ │ │ + var rot = rotation * Math.PI / 180; │ │ │ │ + var sintheta = Math.sin(rot); │ │ │ │ + var costheta = Math.cos(rot); │ │ │ │ + var filter = "progid:DXImageTransform.Microsoft.Matrix(M11=" + costheta + ",M12=" + -sintheta + ",M21=" + sintheta + ",M22=" + costheta + ",SizingMethod='auto expand')\n"; │ │ │ │ + var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ + if (opacity && opacity != 1) { │ │ │ │ + filter += "progid:DXImageTransform.Microsoft.BasicImage(opacity=" + opacity + ")\n" │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.OSM" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.MapServer = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - DEFAULT_PARAMS: { │ │ │ │ - mode: "map", │ │ │ │ - map_imagetype: "png" │ │ │ │ + node.style.filter = filter; │ │ │ │ + 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" │ │ │ │ }, │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments); │ │ │ │ - this.params = OpenLayers.Util.applyDefaults(this.params, this.DEFAULT_PARAMS); │ │ │ │ - if (options == null || options.isBaseLayer == null) { │ │ │ │ - this.isBaseLayer = this.params.transparent != "true" && this.params.transparent != true │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.MapServer(this.name, this.url, this.params, this.getOptions()) │ │ │ │ + if (strokeColor == "none" && node.strokecolor != strokeColor) { │ │ │ │ + node.strokecolor = strokeColor │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ - }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var extent = [bounds.left, bounds.bottom, bounds.right, bounds.top]; │ │ │ │ - var imageSize = this.getImageSize(); │ │ │ │ - var url = this.getFullRequestString({ │ │ │ │ - mapext: extent, │ │ │ │ - imgext: extent, │ │ │ │ - map_size: [imageSize.w, imageSize.h], │ │ │ │ - imgx: imageSize.w / 2, │ │ │ │ - imgy: imageSize.h / 2, │ │ │ │ - imgxy: [imageSize.w, imageSize.h] │ │ │ │ - }); │ │ │ │ - return url │ │ │ │ }, │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ - var url = altUrl == null ? this.url : altUrl; │ │ │ │ - var allParams = OpenLayers.Util.extend({}, this.params); │ │ │ │ - allParams = OpenLayers.Util.extend(allParams, newParams); │ │ │ │ - var paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(paramsString, url) │ │ │ │ - } │ │ │ │ - var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url)); │ │ │ │ - for (var key in allParams) { │ │ │ │ - if (key.toUpperCase() in urlParams) { │ │ │ │ - delete allParams[key] │ │ │ │ - } │ │ │ │ + setNodeDimension: function(node, geometry) { │ │ │ │ + var bbox = geometry.getBounds(); │ │ │ │ + if (bbox) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var scaledBox = new OpenLayers.Bounds((bbox.left - this.featureDx) / resolution - this.offset.x | 0, bbox.bottom / resolution - this.offset.y | 0, (bbox.right - this.featureDx) / resolution - this.offset.x | 0, bbox.top / resolution - this.offset.y | 0); │ │ │ │ + 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() │ │ │ │ } │ │ │ │ - paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ - var requestString = url; │ │ │ │ - paramsString = paramsString.replace(/,/g, "+"); │ │ │ │ - if (paramsString != "") { │ │ │ │ - var lastServerChar = url.charAt(url.length - 1); │ │ │ │ - if (lastServerChar == "&" || lastServerChar == "?") { │ │ │ │ - requestString += paramsString │ │ │ │ - } else { │ │ │ │ - if (url.indexOf("?") == -1) { │ │ │ │ - requestString += "?" + paramsString │ │ │ │ - } else { │ │ │ │ - requestString += "&" + paramsString │ │ │ │ + }, │ │ │ │ + dashStyle: function(style) { │ │ │ │ + var dash = style.strokeDashstyle; │ │ │ │ + switch (dash) { │ │ │ │ + case "solid": │ │ │ │ + case "dot": │ │ │ │ + case "dash": │ │ │ │ + case "dashdot": │ │ │ │ + case "longdash": │ │ │ │ + case "longdashdot": │ │ │ │ + return dash; │ │ │ │ + default: │ │ │ │ + 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" │ │ │ │ } │ │ │ │ - return requestString │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.MapServer" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.KaMap = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - isBaseLayer: true, │ │ │ │ - DEFAULT_PARAMS: { │ │ │ │ - i: "jpeg", │ │ │ │ - map: "" │ │ │ │ - }, │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments); │ │ │ │ - this.params = OpenLayers.Util.applyDefaults(this.params, this.DEFAULT_PARAMS) │ │ │ │ - }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var mapRes = this.map.getResolution(); │ │ │ │ - var scale = Math.round(this.map.getScale() * 1e4) / 1e4; │ │ │ │ - var pX = Math.round(bounds.left / mapRes); │ │ │ │ - var pY = -Math.round(bounds.top / mapRes); │ │ │ │ - return this.getFullRequestString({ │ │ │ │ - t: pY, │ │ │ │ - l: pX, │ │ │ │ - s: scale │ │ │ │ - }) │ │ │ │ }, │ │ │ │ - calculateGridLayout: function(bounds, origin, resolution) { │ │ │ │ - var tilelon = resolution * this.tileSize.w; │ │ │ │ - var tilelat = resolution * this.tileSize.h; │ │ │ │ - var offsetlon = bounds.left; │ │ │ │ - var tilecol = Math.floor(offsetlon / tilelon) - this.buffer; │ │ │ │ - var offsetlat = bounds.top; │ │ │ │ - var tilerow = Math.floor(offsetlat / tilelat) + this.buffer; │ │ │ │ - return { │ │ │ │ - tilelon: tilelon, │ │ │ │ - tilelat: tilelat, │ │ │ │ - startcol: tilecol, │ │ │ │ - startrow: tilerow │ │ │ │ + createNode: function(type, id) { │ │ │ │ + var node = document.createElement(type); │ │ │ │ + if (id) { │ │ │ │ + node.id = id │ │ │ │ } │ │ │ │ + node.unselectable = "on"; │ │ │ │ + node.onselectstart = OpenLayers.Function.False; │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - getTileBoundsForGridIndex: function(row, col) { │ │ │ │ - var origin = this.getTileOrigin(); │ │ │ │ - var tileLayout = this.gridLayout; │ │ │ │ - var tilelon = tileLayout.tilelon; │ │ │ │ - var tilelat = tileLayout.tilelat; │ │ │ │ - var minX = (tileLayout.startcol + col) * tilelon; │ │ │ │ - var minY = (tileLayout.startrow - row) * tilelat; │ │ │ │ - return new OpenLayers.Bounds(minX, minY, minX + tilelon, minY + tilelat) │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.KaMap(this.name, this.url, this.params, this.getOptions()) │ │ │ │ + nodeTypeCompare: function(node, type) { │ │ │ │ + var subType = type; │ │ │ │ + var splitIndex = subType.indexOf(":"); │ │ │ │ + if (splitIndex != -1) { │ │ │ │ + subType = subType.substr(splitIndex + 1) │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - if (this.tileSize != null) { │ │ │ │ - obj.tileSize = this.tileSize.clone() │ │ │ │ + var nodeName = node.nodeName; │ │ │ │ + splitIndex = nodeName.indexOf(":"); │ │ │ │ + if (splitIndex != -1) { │ │ │ │ + nodeName = nodeName.substr(splitIndex + 1) │ │ │ │ } │ │ │ │ - obj.grid = []; │ │ │ │ - return obj │ │ │ │ - }, │ │ │ │ - getTileBounds: function(viewPortPx) { │ │ │ │ - var resolution = this.getResolution(); │ │ │ │ - var tileMapWidth = resolution * this.tileSize.w; │ │ │ │ - var tileMapHeight = resolution * this.tileSize.h; │ │ │ │ - var mapPoint = this.getLonLatFromViewPortPx(viewPortPx); │ │ │ │ - var tileLeft = tileMapWidth * Math.floor(mapPoint.lon / tileMapWidth); │ │ │ │ - var tileBottom = tileMapHeight * Math.floor(mapPoint.lat / tileMapHeight); │ │ │ │ - return new OpenLayers.Bounds(tileLeft, tileBottom, tileLeft + tileMapWidth, tileBottom + tileMapHeight) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.KaMap" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.KaMapCache = OpenLayers.Class(OpenLayers.Layer.KaMap, { │ │ │ │ - IMAGE_EXTENSIONS: { │ │ │ │ - jpeg: "jpg", │ │ │ │ - gif: "gif", │ │ │ │ - png: "png", │ │ │ │ - png8: "png", │ │ │ │ - png24: "png", │ │ │ │ - dithered: "png" │ │ │ │ + return subType == nodeName │ │ │ │ }, │ │ │ │ - DEFAULT_FORMAT: "jpeg", │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - OpenLayers.Layer.KaMap.prototype.initialize.apply(this, arguments); │ │ │ │ - this.extension = this.IMAGE_EXTENSIONS[this.params.i.toLowerCase() || this.DEFAULT_FORMAT] │ │ │ │ + createRenderRoot: function() { │ │ │ │ + return this.nodeFactory(this.container.id + "_vmlRoot", "div") │ │ │ │ }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var mapRes = this.map.getResolution(); │ │ │ │ - var scale = Math.round(this.map.getScale() * 1e4) / 1e4; │ │ │ │ - var pX = Math.round(bounds.left / mapRes); │ │ │ │ - var pY = -Math.round(bounds.top / mapRes); │ │ │ │ - var metaX = Math.floor(pX / this.tileSize.w / this.params.metaTileSize.w) * this.tileSize.w * this.params.metaTileSize.w; │ │ │ │ - var metaY = Math.floor(pY / this.tileSize.h / this.params.metaTileSize.h) * this.tileSize.h * this.params.metaTileSize.h; │ │ │ │ - var components = ["/", this.params.map, "/", scale, "/", this.params.g.replace(/\s/g, "_"), "/def/t", metaY, "/l", metaX, "/t", pY, "l", pX, ".", this.extension]; │ │ │ │ - var url = this.url; │ │ │ │ - if (OpenLayers.Util.isArray(url)) { │ │ │ │ - url = this.selectUrl(components.join(""), url) │ │ │ │ - } │ │ │ │ - return url + components.join("") │ │ │ │ + createRoot: function(suffix) { │ │ │ │ + return this.nodeFactory(this.container.id + suffix, "olv:group") │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.KaMapCache" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.Image = OpenLayers.Class(OpenLayers.Layer, { │ │ │ │ - isBaseLayer: true, │ │ │ │ - url: null, │ │ │ │ - extent: null, │ │ │ │ - size: null, │ │ │ │ - tile: null, │ │ │ │ - aspectRatio: null, │ │ │ │ - initialize: function(name, url, extent, size, options) { │ │ │ │ - this.url = url; │ │ │ │ - this.extent = extent; │ │ │ │ - this.maxExtent = extent; │ │ │ │ - this.size = size; │ │ │ │ - OpenLayers.Layer.prototype.initialize.apply(this, [name, options]); │ │ │ │ - this.aspectRatio = this.extent.getHeight() / this.size.h / (this.extent.getWidth() / this.size.w) │ │ │ │ + drawPoint: function(node, geometry) { │ │ │ │ + return this.drawCircle(node, geometry, 1) │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.tile) { │ │ │ │ - this.removeTileMonitoringHooks(this.tile); │ │ │ │ - this.tile.destroy(); │ │ │ │ - this.tile = null │ │ │ │ + drawCircle: function(node, geometry, radius) { │ │ │ │ + if (!isNaN(geometry.x) && !isNaN(geometry.y)) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + node.style.left = ((geometry.x - this.featureDx) / resolution - this.offset.x | 0) - radius + "px"; │ │ │ │ + node.style.top = (geometry.y / resolution - this.offset.y | 0) - radius + "px"; │ │ │ │ + var diameter = radius * 2; │ │ │ │ + node.style.width = diameter + "px"; │ │ │ │ + node.style.height = diameter + "px"; │ │ │ │ + return node │ │ │ │ } │ │ │ │ - OpenLayers.Layer.prototype.destroy.apply(this, arguments) │ │ │ │ + return false │ │ │ │ }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.Image(this.name, this.url, this.extent, this.size, this.getOptions()) │ │ │ │ - } │ │ │ │ - obj = OpenLayers.Layer.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ + drawLineString: function(node, geometry) { │ │ │ │ + return this.drawLine(node, geometry, false) │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - if (this.options.maxResolution == null) { │ │ │ │ - this.options.maxResolution = this.aspectRatio * this.extent.getWidth() / this.size.w │ │ │ │ - } │ │ │ │ - OpenLayers.Layer.prototype.setMap.apply(this, arguments) │ │ │ │ + drawLinearRing: function(node, geometry) { │ │ │ │ + return this.drawLine(node, geometry, true) │ │ │ │ }, │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - OpenLayers.Layer.prototype.moveTo.apply(this, arguments); │ │ │ │ - var firstRendering = this.tile == null; │ │ │ │ - if (zoomChanged || firstRendering) { │ │ │ │ - this.setTileSize(); │ │ │ │ - var ulPx = this.map.getLayerPxFromLonLat({ │ │ │ │ - lon: this.extent.left, │ │ │ │ - lat: this.extent.top │ │ │ │ - }); │ │ │ │ - if (firstRendering) { │ │ │ │ - this.tile = new OpenLayers.Tile.Image(this, ulPx, this.extent, null, this.tileSize); │ │ │ │ - this.addTileMonitoringHooks(this.tile) │ │ │ │ - } else { │ │ │ │ - this.tile.size = this.tileSize.clone(); │ │ │ │ - this.tile.position = ulPx.clone() │ │ │ │ - } │ │ │ │ - this.tile.draw() │ │ │ │ + drawLine: function(node, geometry, closeLine) { │ │ │ │ + this.setNodeDimension(node, geometry); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var numComponents = geometry.components.length; │ │ │ │ + var parts = new Array(numComponents); │ │ │ │ + var comp, x, y; │ │ │ │ + for (var i = 0; i < numComponents; i++) { │ │ │ │ + comp = geometry.components[i]; │ │ │ │ + x = (comp.x - this.featureDx) / resolution - this.offset.x | 0; │ │ │ │ + y = comp.y / resolution - this.offset.y | 0; │ │ │ │ + parts[i] = " " + x + "," + y + " l " │ │ │ │ } │ │ │ │ + var end = closeLine ? " x e" : " e"; │ │ │ │ + node.path = "m" + parts.join("") + end; │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - setTileSize: function() { │ │ │ │ - var tileWidth = this.extent.getWidth() / this.map.getResolution(); │ │ │ │ - var tileHeight = this.extent.getHeight() / this.map.getResolution(); │ │ │ │ - this.tileSize = new OpenLayers.Size(tileWidth, tileHeight) │ │ │ │ - }, │ │ │ │ - addTileMonitoringHooks: function(tile) { │ │ │ │ - tile.onLoadStart = function() { │ │ │ │ - this.events.triggerEvent("loadstart") │ │ │ │ - }; │ │ │ │ - tile.events.register("loadstart", this, tile.onLoadStart); │ │ │ │ - tile.onLoadEnd = function() { │ │ │ │ - this.events.triggerEvent("loadend") │ │ │ │ - }; │ │ │ │ - tile.events.register("loadend", this, tile.onLoadEnd); │ │ │ │ - tile.events.register("unload", this, tile.onLoadEnd) │ │ │ │ - }, │ │ │ │ - removeTileMonitoringHooks: function(tile) { │ │ │ │ - tile.unload(); │ │ │ │ - tile.events.un({ │ │ │ │ - loadstart: tile.onLoadStart, │ │ │ │ - loadend: tile.onLoadEnd, │ │ │ │ - unload: tile.onLoadEnd, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - setUrl: function(newUrl) { │ │ │ │ - this.url = newUrl; │ │ │ │ - this.tile.draw() │ │ │ │ - }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - return this.url │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Image" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - isBaseLayer: true, │ │ │ │ - version: "1.0.0", │ │ │ │ - requestEncoding: "KVP", │ │ │ │ - url: null, │ │ │ │ - layer: null, │ │ │ │ - matrixSet: null, │ │ │ │ - style: null, │ │ │ │ - format: "image/jpeg", │ │ │ │ - tileOrigin: null, │ │ │ │ - tileFullExtent: null, │ │ │ │ - formatSuffix: null, │ │ │ │ - matrixIds: null, │ │ │ │ - dimensions: null, │ │ │ │ - params: null, │ │ │ │ - zoomOffset: 0, │ │ │ │ - serverResolutions: null, │ │ │ │ - formatSuffixMap: { │ │ │ │ - "image/png": "png", │ │ │ │ - "image/png8": "png", │ │ │ │ - "image/png24": "png", │ │ │ │ - "image/png32": "png", │ │ │ │ - png: "png", │ │ │ │ - "image/jpeg": "jpg", │ │ │ │ - "image/jpg": "jpg", │ │ │ │ - jpeg: "jpg", │ │ │ │ - jpg: "jpg" │ │ │ │ - }, │ │ │ │ - matrix: null, │ │ │ │ - initialize: function(config) { │ │ │ │ - var required = { │ │ │ │ - url: true, │ │ │ │ - layer: true, │ │ │ │ - style: true, │ │ │ │ - matrixSet: true │ │ │ │ - }; │ │ │ │ - for (var prop in required) { │ │ │ │ - if (!(prop in config)) { │ │ │ │ - throw new Error("Missing property '" + prop + "' in layer configuration.") │ │ │ │ - } │ │ │ │ - } │ │ │ │ - config.params = OpenLayers.Util.upperCaseObject(config.params); │ │ │ │ - var args = [config.name, config.url, config.params, config]; │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, args); │ │ │ │ - if (!this.formatSuffix) { │ │ │ │ - this.formatSuffix = this.formatSuffixMap[this.format] || this.format.split("/").pop() │ │ │ │ - } │ │ │ │ - if (this.matrixIds) { │ │ │ │ - var len = this.matrixIds.length; │ │ │ │ - if (len && typeof this.matrixIds[0] === "string") { │ │ │ │ - var ids = this.matrixIds; │ │ │ │ - this.matrixIds = new Array(len); │ │ │ │ - for (var i = 0; i < len; ++i) { │ │ │ │ - this.matrixIds[i] = { │ │ │ │ - identifier: ids[i] │ │ │ │ + drawPolygon: function(node, geometry) { │ │ │ │ + this.setNodeDimension(node, geometry); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var path = []; │ │ │ │ + var j, jj, points, area, first, second, i, ii, comp, pathComp, x, y; │ │ │ │ + for (j = 0, jj = geometry.components.length; j < jj; j++) { │ │ │ │ + path.push("m"); │ │ │ │ + points = geometry.components[j].components; │ │ │ │ + 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) { │ │ │ │ + if (!first) { │ │ │ │ + first = pathComp │ │ │ │ + } else if (first != pathComp) { │ │ │ │ + if (!second) { │ │ │ │ + second = pathComp │ │ │ │ + } else if (second != pathComp) { │ │ │ │ + area = true │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ + path.push(area ? " x " : " ") │ │ │ │ } │ │ │ │ + path.push("e"); │ │ │ │ + node.path = path.join(""); │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - setMap: function() { │ │ │ │ - OpenLayers.Layer.Grid.prototype.setMap.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - updateMatrixProperties: function() { │ │ │ │ - this.matrix = this.getMatrix(); │ │ │ │ - if (this.matrix) { │ │ │ │ - if (this.matrix.topLeftCorner) { │ │ │ │ - this.tileOrigin = this.matrix.topLeftCorner │ │ │ │ - } │ │ │ │ - if (this.matrix.tileWidth && this.matrix.tileHeight) { │ │ │ │ - this.tileSize = new OpenLayers.Size(this.matrix.tileWidth, this.matrix.tileHeight) │ │ │ │ - } │ │ │ │ - if (!this.tileOrigin) { │ │ │ │ - this.tileOrigin = new OpenLayers.LonLat(this.maxExtent.left, this.maxExtent.top) │ │ │ │ - } │ │ │ │ - if (!this.tileFullExtent) { │ │ │ │ - this.tileFullExtent = this.maxExtent │ │ │ │ - } │ │ │ │ - } │ │ │ │ + drawRectangle: function(node, geometry) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + node.style.left = ((geometry.x - this.featureDx) / resolution - this.offset.x | 0) + "px"; │ │ │ │ + node.style.top = (geometry.y / resolution - this.offset.y | 0) + "px"; │ │ │ │ + node.style.width = (geometry.width / resolution | 0) + "px"; │ │ │ │ + node.style.height = (geometry.height / resolution | 0) + "px"; │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - moveTo: function(bounds, zoomChanged, dragging) { │ │ │ │ - if (zoomChanged || !this.matrix) { │ │ │ │ - this.updateMatrixProperties() │ │ │ │ + drawText: function(featureId, style, location) { │ │ │ │ + var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "olv:rect"); │ │ │ │ + var textbox = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_textbox", "olv:textbox"); │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + label.style.left = ((location.x - this.featureDx) / resolution - this.offset.x | 0) + "px"; │ │ │ │ + label.style.top = (location.y / resolution - this.offset.y | 0) + "px"; │ │ │ │ + label.style.flip = "y"; │ │ │ │ + textbox.innerText = style.label; │ │ │ │ + if (style.cursor != "inherit" && style.cursor != null) { │ │ │ │ + textbox.style.cursor = style.cursor │ │ │ │ } │ │ │ │ - return OpenLayers.Layer.Grid.prototype.moveTo.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.WMTS(this.options) │ │ │ │ + if (style.fontColor) { │ │ │ │ + textbox.style.color = style.fontColor │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ - }, │ │ │ │ - getIdentifier: function() { │ │ │ │ - return this.getServerZoom() │ │ │ │ - }, │ │ │ │ - getMatrix: function() { │ │ │ │ - var matrix; │ │ │ │ - if (!this.matrixIds || this.matrixIds.length === 0) { │ │ │ │ - matrix = { │ │ │ │ - identifier: this.getIdentifier() │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - if ("scaleDenominator" in this.matrixIds[0]) { │ │ │ │ - var denom = OpenLayers.METERS_PER_INCH * OpenLayers.INCHES_PER_UNIT[this.units] * this.getServerResolution() / 28e-5; │ │ │ │ - var diff = Number.POSITIVE_INFINITY; │ │ │ │ - var delta; │ │ │ │ - for (var i = 0, ii = this.matrixIds.length; i < ii; ++i) { │ │ │ │ - delta = Math.abs(1 - this.matrixIds[i].scaleDenominator / denom); │ │ │ │ - if (delta < diff) { │ │ │ │ - diff = delta; │ │ │ │ - matrix = this.matrixIds[i] │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - matrix = this.matrixIds[this.getIdentifier()] │ │ │ │ - } │ │ │ │ + if (style.fontOpacity) { │ │ │ │ + textbox.style.filter = "alpha(opacity=" + style.fontOpacity * 100 + ")" │ │ │ │ } │ │ │ │ - return matrix │ │ │ │ - }, │ │ │ │ - getTileInfo: function(loc) { │ │ │ │ - var res = this.getServerResolution(); │ │ │ │ - var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w); │ │ │ │ - var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h); │ │ │ │ - var col = Math.floor(fx); │ │ │ │ - var row = Math.floor(fy); │ │ │ │ - return { │ │ │ │ - col: col, │ │ │ │ - row: row, │ │ │ │ - i: Math.floor((fx - col) * this.tileSize.w), │ │ │ │ - j: Math.floor((fy - row) * this.tileSize.h) │ │ │ │ + if (style.fontFamily) { │ │ │ │ + textbox.style.fontFamily = style.fontFamily │ │ │ │ } │ │ │ │ - }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - bounds = this.adjustBounds(bounds); │ │ │ │ - var url = ""; │ │ │ │ - if (!this.tileFullExtent || this.tileFullExtent.intersectsBounds(bounds)) { │ │ │ │ - var center = bounds.getCenterLonLat(); │ │ │ │ - var info = this.getTileInfo(center); │ │ │ │ - var matrixId = this.matrix.identifier; │ │ │ │ - var dimensions = this.dimensions, │ │ │ │ - params; │ │ │ │ - if (OpenLayers.Util.isArray(this.url)) { │ │ │ │ - url = this.selectUrl([this.version, this.style, this.matrixSet, this.matrix.identifier, info.row, info.col].join(","), this.url) │ │ │ │ - } else { │ │ │ │ - url = this.url │ │ │ │ - } │ │ │ │ - if (this.requestEncoding.toUpperCase() === "REST") { │ │ │ │ - params = this.params; │ │ │ │ - if (url.indexOf("{") !== -1) { │ │ │ │ - var template = url.replace(/\{/g, "${"); │ │ │ │ - var context = { │ │ │ │ - style: this.style, │ │ │ │ - Style: this.style, │ │ │ │ - TileMatrixSet: this.matrixSet, │ │ │ │ - TileMatrix: this.matrix.identifier, │ │ │ │ - TileRow: info.row, │ │ │ │ - TileCol: info.col │ │ │ │ - }; │ │ │ │ - if (dimensions) { │ │ │ │ - var dimension, i; │ │ │ │ - for (i = dimensions.length - 1; i >= 0; --i) { │ │ │ │ - dimension = dimensions[i]; │ │ │ │ - context[dimension] = params[dimension.toUpperCase()] │ │ │ │ - } │ │ │ │ - } │ │ │ │ - url = OpenLayers.String.format(template, context) │ │ │ │ - } else { │ │ │ │ - var path = this.version + "/" + this.layer + "/" + this.style + "/"; │ │ │ │ - if (dimensions) { │ │ │ │ - for (var i = 0; i < dimensions.length; i++) { │ │ │ │ - if (params[dimensions[i]]) { │ │ │ │ - path = path + params[dimensions[i]] + "/" │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - path = path + this.matrixSet + "/" + this.matrix.identifier + "/" + info.row + "/" + info.col + "." + this.formatSuffix; │ │ │ │ - if (!url.match(/\/$/)) { │ │ │ │ - url = url + "/" │ │ │ │ - } │ │ │ │ - url = url + path │ │ │ │ - } │ │ │ │ - } else if (this.requestEncoding.toUpperCase() === "KVP") { │ │ │ │ - params = { │ │ │ │ - SERVICE: "WMTS", │ │ │ │ - REQUEST: "GetTile", │ │ │ │ - VERSION: this.version, │ │ │ │ - LAYER: this.layer, │ │ │ │ - STYLE: this.style, │ │ │ │ - TILEMATRIXSET: this.matrixSet, │ │ │ │ - TILEMATRIX: this.matrix.identifier, │ │ │ │ - TILEROW: info.row, │ │ │ │ - TILECOL: info.col, │ │ │ │ - FORMAT: this.format │ │ │ │ - }; │ │ │ │ - url = OpenLayers.Layer.Grid.prototype.getFullRequestString.apply(this, [params]) │ │ │ │ - } │ │ │ │ + if (style.fontSize) { │ │ │ │ + textbox.style.fontSize = style.fontSize │ │ │ │ } │ │ │ │ - return url │ │ │ │ - }, │ │ │ │ - mergeNewParams: function(newParams) { │ │ │ │ - if (this.requestEncoding.toUpperCase() === "KVP") { │ │ │ │ - return OpenLayers.Layer.Grid.prototype.mergeNewParams.apply(this, [OpenLayers.Util.upperCaseObject(newParams)]) │ │ │ │ + if (style.fontWeight) { │ │ │ │ + textbox.style.fontWeight = style.fontWeight │ │ │ │ } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.WMTS" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.MapGuide = OpenLayers.Class(OpenLayers.Layer.Grid, { │ │ │ │ - isBaseLayer: true, │ │ │ │ - useHttpTile: false, │ │ │ │ - singleTile: false, │ │ │ │ - useOverlay: false, │ │ │ │ - useAsyncOverlay: true, │ │ │ │ - TILE_PARAMS: { │ │ │ │ - operation: "GETTILEIMAGE", │ │ │ │ - version: "1.2.0" │ │ │ │ - }, │ │ │ │ - SINGLE_TILE_PARAMS: { │ │ │ │ - operation: "GETMAPIMAGE", │ │ │ │ - format: "PNG", │ │ │ │ - locale: "en", │ │ │ │ - clip: "1", │ │ │ │ - version: "1.0.0" │ │ │ │ - }, │ │ │ │ - OVERLAY_PARAMS: { │ │ │ │ - operation: "GETDYNAMICMAPOVERLAYIMAGE", │ │ │ │ - format: "PNG", │ │ │ │ - locale: "en", │ │ │ │ - clip: "1", │ │ │ │ - version: "2.0.0" │ │ │ │ - }, │ │ │ │ - FOLDER_PARAMS: { │ │ │ │ - tileColumnsPerFolder: 30, │ │ │ │ - tileRowsPerFolder: 30, │ │ │ │ - format: "png", │ │ │ │ - querystring: null │ │ │ │ - }, │ │ │ │ - defaultSize: new OpenLayers.Size(300, 300), │ │ │ │ - tileOriginCorner: "tl", │ │ │ │ - initialize: function(name, url, params, options) { │ │ │ │ - OpenLayers.Layer.Grid.prototype.initialize.apply(this, arguments); │ │ │ │ - if (options == null || options.isBaseLayer == null) { │ │ │ │ - this.isBaseLayer = this.transparent != "true" && this.transparent != true │ │ │ │ + if (style.fontStyle) { │ │ │ │ + textbox.style.fontStyle = style.fontStyle │ │ │ │ } │ │ │ │ - if (options && options.useOverlay != null) { │ │ │ │ - this.useOverlay = options.useOverlay │ │ │ │ + if (style.labelSelect === true) { │ │ │ │ + label._featureId = featureId; │ │ │ │ + textbox._featureId = featureId; │ │ │ │ + textbox._geometry = location; │ │ │ │ + textbox._geometryClass = location.CLASS_NAME │ │ │ │ } │ │ │ │ - if (this.singleTile) { │ │ │ │ - if (this.useOverlay) { │ │ │ │ - OpenLayers.Util.applyDefaults(this.params, this.OVERLAY_PARAMS); │ │ │ │ - if (!this.useAsyncOverlay) { │ │ │ │ - this.params.version = "1.0.0" │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - OpenLayers.Util.applyDefaults(this.params, this.SINGLE_TILE_PARAMS) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - if (this.useHttpTile) { │ │ │ │ - OpenLayers.Util.applyDefaults(this.params, this.FOLDER_PARAMS) │ │ │ │ - } else { │ │ │ │ - OpenLayers.Util.applyDefaults(this.params, this.TILE_PARAMS) │ │ │ │ - } │ │ │ │ - this.setTileSize(this.defaultSize) │ │ │ │ + textbox.style.whiteSpace = "nowrap"; │ │ │ │ + textbox.inset = "1px,0px,0px,0px"; │ │ │ │ + if (!label.parentNode) { │ │ │ │ + label.appendChild(textbox); │ │ │ │ + this.textRoot.appendChild(label) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - clone: function(obj) { │ │ │ │ - if (obj == null) { │ │ │ │ - obj = new OpenLayers.Layer.MapGuide(this.name, this.url, this.params, this.getOptions()) │ │ │ │ + var align = style.labelAlign || "cm"; │ │ │ │ + if (align.length == 1) { │ │ │ │ + align += "m" │ │ │ │ } │ │ │ │ - obj = OpenLayers.Layer.Grid.prototype.clone.apply(this, [obj]); │ │ │ │ - return obj │ │ │ │ + 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" │ │ │ │ }, │ │ │ │ - getURL: function(bounds) { │ │ │ │ - var url; │ │ │ │ - var center = bounds.getCenterLonLat(); │ │ │ │ - var mapSize = this.map.getSize(); │ │ │ │ - if (this.singleTile) { │ │ │ │ - var params = { │ │ │ │ - setdisplaydpi: OpenLayers.DOTS_PER_INCH, │ │ │ │ - setdisplayheight: mapSize.h * this.ratio, │ │ │ │ - setdisplaywidth: mapSize.w * this.ratio, │ │ │ │ - setviewcenterx: center.lon, │ │ │ │ - setviewcentery: center.lat, │ │ │ │ - setviewscale: this.map.getScale() │ │ │ │ - }; │ │ │ │ - if (this.useOverlay && !this.useAsyncOverlay) { │ │ │ │ - var getVisParams = {}; │ │ │ │ - getVisParams = OpenLayers.Util.extend(getVisParams, params); │ │ │ │ - getVisParams.operation = "GETVISIBLEMAPEXTENT"; │ │ │ │ - getVisParams.version = "1.0.0"; │ │ │ │ - getVisParams.session = this.params.session; │ │ │ │ - getVisParams.mapName = this.params.mapName; │ │ │ │ - getVisParams.format = "text/xml"; │ │ │ │ - url = this.getFullRequestString(getVisParams); │ │ │ │ - OpenLayers.Request.GET({ │ │ │ │ - url: url, │ │ │ │ - async: false │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - url = this.getFullRequestString(params) │ │ │ │ - } else { │ │ │ │ - var currentRes = this.map.getResolution(); │ │ │ │ - var colidx = Math.floor((bounds.left - this.maxExtent.left) / currentRes); │ │ │ │ - colidx = Math.round(colidx / this.tileSize.w); │ │ │ │ - var rowidx = Math.floor((this.maxExtent.top - bounds.top) / currentRes); │ │ │ │ - rowidx = Math.round(rowidx / this.tileSize.h); │ │ │ │ - if (this.useHttpTile) { │ │ │ │ - url = this.getImageFilePath({ │ │ │ │ - tilecol: colidx, │ │ │ │ - tilerow: rowidx, │ │ │ │ - scaleindex: this.resolutions.length - this.map.zoom - 1 │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - url = this.getFullRequestString({ │ │ │ │ - tilecol: colidx, │ │ │ │ - tilerow: rowidx, │ │ │ │ - scaleindex: this.resolutions.length - this.map.zoom - 1 │ │ │ │ - }) │ │ │ │ - } │ │ │ │ + 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) │ │ │ │ } │ │ │ │ - return url │ │ │ │ + layer && layer.renderer.clear(); │ │ │ │ + OpenLayers.Renderer.Elements.prototype.moveRoot.apply(this, arguments); │ │ │ │ + layer && layer.redraw() │ │ │ │ }, │ │ │ │ - getFullRequestString: function(newParams, altUrl) { │ │ │ │ - var url = altUrl == null ? this.url : altUrl; │ │ │ │ - if (typeof url == "object") { │ │ │ │ - url = url[Math.floor(Math.random() * url.length)] │ │ │ │ + importSymbol: function(graphicName) { │ │ │ │ + var id = this.container.id + "-" + graphicName; │ │ │ │ + var cache = this.symbolCache[id]; │ │ │ │ + if (cache) { │ │ │ │ + return cache │ │ │ │ } │ │ │ │ - var requestString = url; │ │ │ │ - var allParams = OpenLayers.Util.extend({}, this.params); │ │ │ │ - allParams = OpenLayers.Util.extend(allParams, newParams); │ │ │ │ - var urlParams = OpenLayers.Util.upperCaseObject(OpenLayers.Util.getParameters(url)); │ │ │ │ - for (var key in allParams) { │ │ │ │ - if (key.toUpperCase() in urlParams) { │ │ │ │ - delete allParams[key] │ │ │ │ - } │ │ │ │ + var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ + if (!symbol) { │ │ │ │ + throw new Error(graphicName + " is not a valid symbol name") │ │ │ │ } │ │ │ │ - var paramsString = OpenLayers.Util.getParameterString(allParams); │ │ │ │ - paramsString = paramsString.replace(/,/g, "+"); │ │ │ │ - if (paramsString != "") { │ │ │ │ - var lastServerChar = url.charAt(url.length - 1); │ │ │ │ - if (lastServerChar == "&" || lastServerChar == "?") { │ │ │ │ - requestString += paramsString │ │ │ │ - } else { │ │ │ │ - if (url.indexOf("?") == -1) { │ │ │ │ - requestString += "?" + paramsString │ │ │ │ - } else { │ │ │ │ - requestString += "&" + paramsString │ │ │ │ - } │ │ │ │ + var symbolExtent = new OpenLayers.Bounds(Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ + var pathitems = ["m"]; │ │ │ │ + for (var i = 0; i < symbol.length; i = i + 2) { │ │ │ │ + var x = symbol[i]; │ │ │ │ + var y = symbol[i + 1]; │ │ │ │ + symbolExtent.left = Math.min(symbolExtent.left, x); │ │ │ │ + symbolExtent.bottom = Math.min(symbolExtent.bottom, y); │ │ │ │ + symbolExtent.right = Math.max(symbolExtent.right, x); │ │ │ │ + symbolExtent.top = Math.max(symbolExtent.top, y); │ │ │ │ + pathitems.push(x); │ │ │ │ + pathitems.push(y); │ │ │ │ + if (i == 0) { │ │ │ │ + pathitems.push("l") │ │ │ │ } │ │ │ │ } │ │ │ │ - return requestString │ │ │ │ - }, │ │ │ │ - getImageFilePath: function(newParams, altUrl) { │ │ │ │ - var url = altUrl == null ? this.url : altUrl; │ │ │ │ - if (typeof url == "object") { │ │ │ │ - url = url[Math.floor(Math.random() * url.length)] │ │ │ │ - } │ │ │ │ - var requestString = url; │ │ │ │ - var tileRowGroup = ""; │ │ │ │ - var tileColGroup = ""; │ │ │ │ - if (newParams.tilerow < 0) { │ │ │ │ - tileRowGroup = "-" │ │ │ │ - } │ │ │ │ - if (newParams.tilerow == 0) { │ │ │ │ - tileRowGroup += "0" │ │ │ │ - } else { │ │ │ │ - tileRowGroup += Math.floor(Math.abs(newParams.tilerow / this.params.tileRowsPerFolder)) * this.params.tileRowsPerFolder │ │ │ │ - } │ │ │ │ - if (newParams.tilecol < 0) { │ │ │ │ - tileColGroup = "-" │ │ │ │ - } │ │ │ │ - if (newParams.tilecol == 0) { │ │ │ │ - tileColGroup += "0" │ │ │ │ + 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 { │ │ │ │ - tileColGroup += Math.floor(Math.abs(newParams.tilecol / this.params.tileColumnsPerFolder)) * this.params.tileColumnsPerFolder │ │ │ │ - } │ │ │ │ - var tilePath = "/S" + Math.floor(newParams.scaleindex) + "/" + this.params.basemaplayergroupname + "/R" + tileRowGroup + "/C" + tileColGroup + "/" + newParams.tilerow % this.params.tileRowsPerFolder + "_" + newParams.tilecol % this.params.tileColumnsPerFolder + "." + this.params.format; │ │ │ │ - if (this.params.querystring) { │ │ │ │ - tilePath += "?" + this.params.querystring │ │ │ │ + symbolExtent.left = symbolExtent.left + diff; │ │ │ │ + symbolExtent.right = symbolExtent.right - diff │ │ │ │ } │ │ │ │ - requestString += tilePath; │ │ │ │ - return requestString │ │ │ │ + cache = { │ │ │ │ + path: path, │ │ │ │ + size: symbolExtent.getWidth(), │ │ │ │ + left: symbolExtent.left, │ │ │ │ + bottom: symbolExtent.bottom │ │ │ │ + }; │ │ │ │ + this.symbolCache[id] = cache; │ │ │ │ + return cache │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.MapGuide" │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.VML" │ │ │ │ }); │ │ │ │ -OpenLayers.Layer.Vector.RootContainer = OpenLayers.Class(OpenLayers.Layer.Vector, { │ │ │ │ - displayInLayerSwitcher: false, │ │ │ │ - layers: null, │ │ │ │ - display: function() {}, │ │ │ │ - getFeatureFromEvent: function(evt) { │ │ │ │ - var layers = this.layers; │ │ │ │ - var feature; │ │ │ │ - for (var i = 0; i < layers.length; i++) { │ │ │ │ - feature = layers[i].getFeatureFromEvent(evt); │ │ │ │ - if (feature) { │ │ │ │ - return feature │ │ │ │ - } │ │ │ │ +OpenLayers.Renderer.VML.LABEL_SHIFT = { │ │ │ │ + l: 0, │ │ │ │ + c: .5, │ │ │ │ + r: 1, │ │ │ │ + t: 0, │ │ │ │ + m: .5, │ │ │ │ + b: 1 │ │ │ │ +}; │ │ │ │ +OpenLayers.Renderer.SVG = OpenLayers.Class(OpenLayers.Renderer.Elements, { │ │ │ │ + xmlns: "http://www.w3.org/2000/svg", │ │ │ │ + xlinkns: "http://www.w3.org/1999/xlink", │ │ │ │ + MAX_PIXEL: 15e3, │ │ │ │ + translationParameters: null, │ │ │ │ + symbolMetrics: null, │ │ │ │ + initialize: function(containerID) { │ │ │ │ + if (!this.supported()) { │ │ │ │ + return │ │ │ │ } │ │ │ │ + OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments); │ │ │ │ + this.translationParameters = { │ │ │ │ + x: 0, │ │ │ │ + y: 0 │ │ │ │ + }; │ │ │ │ + this.symbolMetrics = {} │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Layer.Vector.prototype.setMap.apply(this, arguments); │ │ │ │ - this.collectRoots(); │ │ │ │ - map.events.register("changelayer", this, this.handleChangeLayer) │ │ │ │ + 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")) │ │ │ │ }, │ │ │ │ - removeMap: function(map) { │ │ │ │ - map.events.unregister("changelayer", this, this.handleChangeLayer); │ │ │ │ - this.resetRoots(); │ │ │ │ - OpenLayers.Layer.Vector.prototype.removeMap.apply(this, arguments) │ │ │ │ + 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 │ │ │ │ }, │ │ │ │ - collectRoots: function() { │ │ │ │ - var layer; │ │ │ │ - for (var i = 0; i < this.map.layers.length; ++i) { │ │ │ │ - layer = this.map.layers[i]; │ │ │ │ - if (OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ - layer.renderer.moveRoot(this.renderer) │ │ │ │ + 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 (resolutionChanged) { │ │ │ │ + this.left = left; │ │ │ │ + this.top = top; │ │ │ │ + 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) { │ │ │ │ + this.setExtent(extent, true) │ │ │ │ } │ │ │ │ + return coordSysUnchanged && inRange │ │ │ │ } │ │ │ │ }, │ │ │ │ - resetRoots: function() { │ │ │ │ - var layer; │ │ │ │ - for (var i = 0; i < this.layers.length; ++i) { │ │ │ │ - layer = this.layers[i]; │ │ │ │ - if (this.renderer && layer.renderer.getRenderLayerId() == this.id) { │ │ │ │ - this.renderer.moveRoot(layer.renderer) │ │ │ │ + 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 │ │ │ │ } │ │ │ │ }, │ │ │ │ - handleChangeLayer: function(evt) { │ │ │ │ - var layer = evt.layer; │ │ │ │ - if (evt.property == "order" && OpenLayers.Util.indexOf(this.layers, layer) != -1) { │ │ │ │ - this.resetRoots(); │ │ │ │ - this.collectRoots() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Layer.Vector.RootContainer" │ │ │ │ -}); │ │ │ │ -OpenLayers.Layer.Google.v3 = { │ │ │ │ - DEFAULTS: { │ │ │ │ - sphericalMercator: true, │ │ │ │ - projection: "EPSG:900913" │ │ │ │ + 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) │ │ │ │ }, │ │ │ │ - animationEnabled: true, │ │ │ │ - loadMapObject: function() { │ │ │ │ - if (!this.type) { │ │ │ │ - this.type = google.maps.MapTypeId.ROADMAP │ │ │ │ - } │ │ │ │ - var mapObject; │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - if (cache) { │ │ │ │ - mapObject = cache.mapObject; │ │ │ │ - ++cache.count │ │ │ │ - } else { │ │ │ │ - var center = this.map.getCenter(); │ │ │ │ - var container = document.createElement("div"); │ │ │ │ - container.className = "olForeignContainer"; │ │ │ │ - container.style.width = "100%"; │ │ │ │ - container.style.height = "100%"; │ │ │ │ - mapObject = new google.maps.Map(container, { │ │ │ │ - center: center ? new google.maps.LatLng(center.lat, center.lon) : new google.maps.LatLng(0, 0), │ │ │ │ - zoom: this.map.getZoom() || 0, │ │ │ │ - mapTypeId: this.type, │ │ │ │ - disableDefaultUI: true, │ │ │ │ - keyboardShortcuts: false, │ │ │ │ - draggable: false, │ │ │ │ - disableDoubleClickZoom: true, │ │ │ │ - scrollwheel: false, │ │ │ │ - streetViewControl: false │ │ │ │ - }); │ │ │ │ - var googleControl = document.createElement("div"); │ │ │ │ - googleControl.style.width = "100%"; │ │ │ │ - googleControl.style.height = "100%"; │ │ │ │ - mapObject.controls[google.maps.ControlPosition.TOP_LEFT].push(googleControl); │ │ │ │ - cache = { │ │ │ │ - googleControl: googleControl, │ │ │ │ - mapObject: mapObject, │ │ │ │ - count: 1 │ │ │ │ - }; │ │ │ │ - OpenLayers.Layer.Google.cache[this.map.id] = cache │ │ │ │ + getNodeType: function(geometry, style) { │ │ │ │ + var nodeType = null; │ │ │ │ + switch (geometry.CLASS_NAME) { │ │ │ │ + case "OpenLayers.Geometry.Point": │ │ │ │ + if (style.externalGraphic) { │ │ │ │ + nodeType = "image" │ │ │ │ + } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ + nodeType = "svg" │ │ │ │ + } else { │ │ │ │ + nodeType = "circle" │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Rectangle": │ │ │ │ + nodeType = "rect"; │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LineString": │ │ │ │ + nodeType = "polyline"; │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.LinearRing": │ │ │ │ + nodeType = "polygon"; │ │ │ │ + break; │ │ │ │ + case "OpenLayers.Geometry.Polygon": │ │ │ │ + case "OpenLayers.Geometry.Curve": │ │ │ │ + nodeType = "path"; │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + break │ │ │ │ } │ │ │ │ - this.mapObject = mapObject; │ │ │ │ - this.setGMapVisibility(this.visibility) │ │ │ │ + return nodeType │ │ │ │ }, │ │ │ │ - onMapResize: function() { │ │ │ │ - if (this.visibility) { │ │ │ │ - google.maps.event.trigger(this.mapObject, "resize") │ │ │ │ + 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); │ │ │ │ + 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) │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - setGMapVisibility: function(visible) { │ │ │ │ - var cache = OpenLayers.Layer.Google.cache[this.map.id]; │ │ │ │ - var map = this.map; │ │ │ │ - if (cache) { │ │ │ │ - var type = this.type; │ │ │ │ - var layers = map.layers; │ │ │ │ - var layer; │ │ │ │ - for (var i = layers.length - 1; i >= 0; --i) { │ │ │ │ - layer = layers[i]; │ │ │ │ - if (layer instanceof OpenLayers.Layer.Google && layer.visibility === true && layer.inRange === true) { │ │ │ │ - type = layer.type; │ │ │ │ - visible = true; │ │ │ │ - break │ │ │ │ + var 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 : -(.5 * width); │ │ │ │ + var yOffset = style.graphicYOffset != undefined ? style.graphicYOffset : -(.5 * height); │ │ │ │ + var opacity = style.graphicOpacity || style.fillOpacity; │ │ │ │ + node.setAttributeNS(null, "x", (pos.x + xOffset).toFixed()); │ │ │ │ + node.setAttributeNS(null, "y", (pos.y + yOffset).toFixed()); │ │ │ │ + node.setAttributeNS(null, "width", width); │ │ │ │ + node.setAttributeNS(null, "height", height); │ │ │ │ + node.setAttributeNS(this.xlinkns, "xlink:href", style.externalGraphic); │ │ │ │ + node.setAttributeNS(null, "style", "opacity: " + opacity); │ │ │ │ + node.onclick = OpenLayers.Event.preventDefault │ │ │ │ + } else if (this.isComplexSymbol(style.graphicName)) { │ │ │ │ + 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; │ │ │ │ + var parent = node.parentNode; │ │ │ │ + var nextSibling = node.nextSibling; │ │ │ │ + if (parent) { │ │ │ │ + parent.removeChild(node) │ │ │ │ + } │ │ │ │ + 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); │ │ │ │ + if (nextSibling) { │ │ │ │ + parent.insertBefore(node, nextSibling) │ │ │ │ + } else if (parent) { │ │ │ │ + parent.appendChild(node) │ │ │ │ } │ │ │ │ + } else { │ │ │ │ + node.setAttributeNS(null, "r", style.pointRadius) │ │ │ │ } │ │ │ │ - var container = this.mapObject.getDiv(); │ │ │ │ - if (visible === true) { │ │ │ │ - if (container.parentNode !== map.div) { │ │ │ │ - if (!cache.rendered) { │ │ │ │ - var me = this; │ │ │ │ - google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function() { │ │ │ │ - cache.rendered = true; │ │ │ │ - me.setGMapVisibility(me.getVisibility()); │ │ │ │ - me.moveTo(me.map.getCenter()) │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - map.div.appendChild(container); │ │ │ │ - cache.googleControl.appendChild(map.viewPortDiv); │ │ │ │ - google.maps.event.trigger(this.mapObject, "resize") │ │ │ │ - } │ │ │ │ + var rotation = style.rotation; │ │ │ │ + if ((rotation !== undefined || node._rotation !== undefined) && pos) { │ │ │ │ + node._rotation = rotation; │ │ │ │ + rotation |= 0; │ │ │ │ + if (node.nodeName !== "svg") { │ │ │ │ + node.setAttributeNS(null, "transform", "rotate(" + rotation + " " + pos.x + " " + pos.y + ")") │ │ │ │ + } else { │ │ │ │ + var metrics = this.symbolMetrics[src.id]; │ │ │ │ + node.firstChild.setAttributeNS(null, "transform", "rotate(" + rotation + " " + metrics[1] + " " + metrics[2] + ")") │ │ │ │ } │ │ │ │ - this.mapObject.setMapTypeId(type) │ │ │ │ - } else if (cache.googleControl.hasChildNodes()) { │ │ │ │ - map.div.appendChild(map.viewPortDiv); │ │ │ │ - map.div.removeChild(container) │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - getMapContainer: function() { │ │ │ │ - return this.mapObject.getDiv() │ │ │ │ - }, │ │ │ │ - getMapObjectBoundsFromOLBounds: function(olBounds) { │ │ │ │ - var moBounds = null; │ │ │ │ - if (olBounds != null) { │ │ │ │ - var sw = this.sphericalMercator ? this.inverseMercator(olBounds.bottom, olBounds.left) : new OpenLayers.LonLat(olBounds.bottom, olBounds.left); │ │ │ │ - var ne = this.sphericalMercator ? this.inverseMercator(olBounds.top, olBounds.right) : new OpenLayers.LonLat(olBounds.top, olBounds.right); │ │ │ │ - moBounds = new google.maps.LatLngBounds(new google.maps.LatLng(sw.lat, sw.lon), new google.maps.LatLng(ne.lat, ne.lon)) │ │ │ │ + if (options.isFilled) { │ │ │ │ + node.setAttributeNS(null, "fill", style.fillColor); │ │ │ │ + node.setAttributeNS(null, "fill-opacity", style.fillOpacity) │ │ │ │ + } else { │ │ │ │ + node.setAttributeNS(null, "fill", "none") │ │ │ │ } │ │ │ │ - return moBounds │ │ │ │ - }, │ │ │ │ - getMapObjectLonLatFromMapObjectPixel: function(moPixel) { │ │ │ │ - var size = this.map.getSize(); │ │ │ │ - var lon = this.getLongitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ - var lat = this.getLatitudeFromMapObjectLonLat(this.mapObject.center); │ │ │ │ - var res = this.map.getResolution(); │ │ │ │ - var delta_x = moPixel.x - size.w / 2; │ │ │ │ - var delta_y = moPixel.y - size.h / 2; │ │ │ │ - var lonlat = new OpenLayers.LonLat(lon + delta_x * res, lat - delta_y * res); │ │ │ │ - if (this.wrapDateLine) { │ │ │ │ - lonlat = lonlat.wrapDateLine(this.maxExtent) │ │ │ │ + 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"); │ │ │ │ + node.setAttributeNS(null, "stroke-linejoin", "round"); │ │ │ │ + style.strokeDashstyle && node.setAttributeNS(null, "stroke-dasharray", this.dashStyle(style, widthFactor)) │ │ │ │ + } else { │ │ │ │ + node.setAttributeNS(null, "stroke", "none") │ │ │ │ } │ │ │ │ - return this.getMapObjectLonLatFromLonLat(lonlat.lon, lonlat.lat) │ │ │ │ - }, │ │ │ │ - getMapObjectPixelFromMapObjectLonLat: function(moLonLat) { │ │ │ │ - var lon = this.getLongitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - var lat = this.getLatitudeFromMapObjectLonLat(moLonLat); │ │ │ │ - var res = this.map.getResolution(); │ │ │ │ - var extent = this.map.getExtent(); │ │ │ │ - return this.getMapObjectPixelFromXY(1 / res * (lon - extent.left), 1 / res * (extent.top - lat)) │ │ │ │ - }, │ │ │ │ - setMapObjectCenter: function(center, zoom) { │ │ │ │ - if (this.animationEnabled === false && zoom != this.mapObject.zoom) { │ │ │ │ - var mapContainer = this.getMapContainer(); │ │ │ │ - google.maps.event.addListenerOnce(this.mapObject, "idle", function() { │ │ │ │ - mapContainer.style.visibility = "" │ │ │ │ - }); │ │ │ │ - mapContainer.style.visibility = "hidden" │ │ │ │ + if (style.pointerEvents) { │ │ │ │ + node.setAttributeNS(null, "pointer-events", style.pointerEvents) │ │ │ │ } │ │ │ │ - this.mapObject.setOptions({ │ │ │ │ - center: center, │ │ │ │ - zoom: zoom │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - getMapObjectZoomFromMapObjectBounds: function(moBounds) { │ │ │ │ - return this.mapObject.getBoundsZoomLevel(moBounds) │ │ │ │ - }, │ │ │ │ - getMapObjectLonLatFromLonLat: function(lon, lat) { │ │ │ │ - var gLatLng; │ │ │ │ - if (this.sphericalMercator) { │ │ │ │ - var lonlat = this.inverseMercator(lon, lat); │ │ │ │ - gLatLng = new google.maps.LatLng(lonlat.lat, lonlat.lon) │ │ │ │ - } else { │ │ │ │ - gLatLng = new google.maps.LatLng(lat, lon) │ │ │ │ + if (style.cursor != null) { │ │ │ │ + node.setAttributeNS(null, "cursor", style.cursor) │ │ │ │ } │ │ │ │ - return gLatLng │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - getMapObjectPixelFromXY: function(x, y) { │ │ │ │ - return new google.maps.Point(x, y) │ │ │ │ - } │ │ │ │ -}; │ │ │ │ -OpenLayers.Events.buttonclick = OpenLayers.Class({ │ │ │ │ - target: null, │ │ │ │ - events: ["mousedown", "mouseup", "click", "dblclick", "touchstart", "touchmove", "touchend", "keydown"], │ │ │ │ - startRegEx: /^mousedown|touchstart$/, │ │ │ │ - cancelRegEx: /^touchmove$/, │ │ │ │ - completeRegEx: /^mouseup|touchend$/, │ │ │ │ - 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 │ │ │ │ - }) │ │ │ │ + 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, ",") │ │ │ │ } │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - for (var i = this.events.length - 1; i >= 0; --i) { │ │ │ │ - this.target.unregister(this.events[i], this, this.buttonClick) │ │ │ │ + createNode: function(type, id) { │ │ │ │ + var node = document.createElementNS(this.xmlns, type); │ │ │ │ + if (id) { │ │ │ │ + node.setAttributeNS(null, "id", id) │ │ │ │ } │ │ │ │ - delete this.target │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - getPressedButton: function(element) { │ │ │ │ - var depth = 3, │ │ │ │ - button; │ │ │ │ - do { │ │ │ │ - if (OpenLayers.Element.hasClass(element, "olButton")) { │ │ │ │ - button = element; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - element = element.parentNode │ │ │ │ - } while (--depth > 0 && element); │ │ │ │ - return button │ │ │ │ + nodeTypeCompare: function(node, type) { │ │ │ │ + return type == node.nodeName │ │ │ │ }, │ │ │ │ - ignore: function(element) { │ │ │ │ - var depth = 3, │ │ │ │ - ignore = false; │ │ │ │ - do { │ │ │ │ - if (element.nodeName.toLowerCase() === "a") { │ │ │ │ - ignore = true; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - element = element.parentNode │ │ │ │ - } while (--depth > 0 && element); │ │ │ │ - return ignore │ │ │ │ + createRenderRoot: function() { │ │ │ │ + var svg = this.nodeFactory(this.container.id + "_svgRoot", "svg"); │ │ │ │ + svg.style.display = "block"; │ │ │ │ + return svg │ │ │ │ }, │ │ │ │ - buttonClick: function(evt) { │ │ │ │ - var propagate = true, │ │ │ │ - element = OpenLayers.Event.element(evt); │ │ │ │ - if (element && (OpenLayers.Event.isLeftClick(evt) || !~evt.type.indexOf("mouse"))) { │ │ │ │ - var button = this.getPressedButton(element); │ │ │ │ - if (button) { │ │ │ │ - if (evt.type === "keydown") { │ │ │ │ - switch (evt.keyCode) { │ │ │ │ - case OpenLayers.Event.KEY_RETURN: │ │ │ │ - case OpenLayers.Event.KEY_SPACE: │ │ │ │ - this.target.triggerEvent("buttonclick", { │ │ │ │ - buttonElement: button │ │ │ │ - }); │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - propagate = false; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } else if (this.startEvt) { │ │ │ │ - if (this.completeRegEx.test(evt.type)) { │ │ │ │ - var pos = OpenLayers.Util.pagePosition(button); │ │ │ │ - var viewportElement = OpenLayers.Util.getViewportElement(); │ │ │ │ - var scrollTop = window.pageYOffset || viewportElement.scrollTop; │ │ │ │ - var scrollLeft = window.pageXOffset || viewportElement.scrollLeft; │ │ │ │ - pos[0] = pos[0] - scrollLeft; │ │ │ │ - pos[1] = pos[1] - scrollTop; │ │ │ │ - this.target.triggerEvent("buttonclick", { │ │ │ │ - buttonElement: button, │ │ │ │ - buttonXY: { │ │ │ │ - x: this.startEvt.clientX - pos[0], │ │ │ │ - y: this.startEvt.clientY - pos[1] │ │ │ │ - } │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - if (this.cancelRegEx.test(evt.type)) { │ │ │ │ - delete this.startEvt │ │ │ │ - } │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - propagate = false │ │ │ │ - } │ │ │ │ - if (this.startRegEx.test(evt.type)) { │ │ │ │ - this.startEvt = evt; │ │ │ │ - OpenLayers.Event.stop(evt); │ │ │ │ - propagate = false │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - propagate = !this.ignore(OpenLayers.Event.element(evt)); │ │ │ │ - delete this.startEvt │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return propagate │ │ │ │ - } │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Panel = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - controls: null, │ │ │ │ - autoActivate: true, │ │ │ │ - defaultControl: null, │ │ │ │ - saveState: false, │ │ │ │ - allowDepress: false, │ │ │ │ - activeState: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.controls = []; │ │ │ │ - this.activeState = {} │ │ │ │ + createRoot: function(suffix) { │ │ │ │ + return this.nodeFactory(this.container.id + suffix, "g") │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.unregister("buttonclick", this, this.onButtonClick) │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - for (var ctl, i = this.controls.length - 1; i >= 0; i--) { │ │ │ │ - ctl = this.controls[i]; │ │ │ │ - if (ctl.events) { │ │ │ │ - ctl.events.un({ │ │ │ │ - activate: this.iconOn, │ │ │ │ - deactivate: this.iconOff │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - ctl.panel_div = null │ │ │ │ - } │ │ │ │ - this.activeState = null │ │ │ │ + createDefs: function() { │ │ │ │ + var defs = this.nodeFactory(this.container.id + "_defs", "defs"); │ │ │ │ + this.rendererRoot.appendChild(defs); │ │ │ │ + return defs │ │ │ │ }, │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - var control; │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - control = this.controls[i]; │ │ │ │ - if (control === this.defaultControl || this.saveState && this.activeState[control.id]) { │ │ │ │ - control.activate() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.saveState === true) { │ │ │ │ - this.defaultControl = null │ │ │ │ - } │ │ │ │ - this.redraw(); │ │ │ │ - return true │ │ │ │ + drawPoint: function(node, geometry) { │ │ │ │ + return this.drawCircle(node, geometry, 1) │ │ │ │ + }, │ │ │ │ + drawCircle: function(node, geometry, radius) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var x = (geometry.x - this.featureDx) / resolution + this.left; │ │ │ │ + var y = this.top - geometry.y / resolution; │ │ │ │ + if (this.inValidRange(x, y)) { │ │ │ │ + node.setAttributeNS(null, "cx", x); │ │ │ │ + node.setAttributeNS(null, "cy", y); │ │ │ │ + node.setAttributeNS(null, "r", radius); │ │ │ │ + return node │ │ │ │ } else { │ │ │ │ return false │ │ │ │ } │ │ │ │ }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - var control; │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - control = this.controls[i]; │ │ │ │ - this.activeState[control.id] = control.deactivate() │ │ │ │ - } │ │ │ │ - this.redraw(); │ │ │ │ - return true │ │ │ │ + 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 │ │ │ │ } │ │ │ │ }, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (this.outsideViewport) { │ │ │ │ - this.events.attachToElement(this.div); │ │ │ │ - this.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ + 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 { │ │ │ │ - this.map.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ + return false │ │ │ │ } │ │ │ │ - this.addControlsToMap(this.controls); │ │ │ │ - return this.div │ │ │ │ }, │ │ │ │ - redraw: function() { │ │ │ │ - for (var l = this.div.childNodes.length, i = l - 1; i >= 0; i--) { │ │ │ │ - this.div.removeChild(this.div.childNodes[i]) │ │ │ │ - } │ │ │ │ - this.div.innerHTML = ""; │ │ │ │ - if (this.active) { │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - this.div.appendChild(this.controls[i].panel_div) │ │ │ │ + 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 │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - activateControl: function(control) { │ │ │ │ - if (!this.active) { │ │ │ │ + d += " z"; │ │ │ │ + if (draw) { │ │ │ │ + node.setAttributeNS(null, "d", d); │ │ │ │ + node.setAttributeNS(null, "fill-rule", "evenodd"); │ │ │ │ + return complete ? node : null │ │ │ │ + } else { │ │ │ │ return false │ │ │ │ } │ │ │ │ - if (control.type == OpenLayers.Control.TYPE_BUTTON) { │ │ │ │ - control.trigger(); │ │ │ │ - return │ │ │ │ + }, │ │ │ │ + drawRectangle: function(node, geometry) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var x = (geometry.x - this.featureDx) / resolution + this.left; │ │ │ │ + var y = this.top - geometry.y / resolution; │ │ │ │ + if (this.inValidRange(x, y)) { │ │ │ │ + node.setAttributeNS(null, "x", x); │ │ │ │ + node.setAttributeNS(null, "y", y); │ │ │ │ + node.setAttributeNS(null, "width", geometry.width / resolution); │ │ │ │ + node.setAttributeNS(null, "height", geometry.height / resolution); │ │ │ │ + return node │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ } │ │ │ │ - if (control.type == OpenLayers.Control.TYPE_TOGGLE) { │ │ │ │ - if (control.active) { │ │ │ │ - control.deactivate() │ │ │ │ - } else { │ │ │ │ - control.activate() │ │ │ │ + }, │ │ │ │ + drawText: function(featureId, style, location) { │ │ │ │ + var drawOutline = !!style.labelOutlineWidth; │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - return │ │ │ │ + delete outlineStyle.labelOutlineWidth; │ │ │ │ + this.drawText(featureId, outlineStyle, location) │ │ │ │ } │ │ │ │ - if (this.allowDepress && control.active) { │ │ │ │ - control.deactivate() │ │ │ │ - } else { │ │ │ │ - var c; │ │ │ │ - for (var i = 0, len = this.controls.length; i < len; i++) { │ │ │ │ - c = this.controls[i]; │ │ │ │ - if (c != control && (c.type === OpenLayers.Control.TYPE_TOOL || c.type == null)) { │ │ │ │ - c.deactivate() │ │ │ │ - } │ │ │ │ - } │ │ │ │ - control.activate() │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var x = (location.x - this.featureDx) / resolution + this.left; │ │ │ │ + var y = location.y / resolution - this.top; │ │ │ │ + var suffix = drawOutline ? this.LABEL_OUTLINE_SUFFIX : this.LABEL_ID_SUFFIX; │ │ │ │ + var label = this.nodeFactory(featureId + suffix, "text"); │ │ │ │ + label.setAttributeNS(null, "x", x); │ │ │ │ + label.setAttributeNS(null, "y", -y); │ │ │ │ + if (style.fontColor) { │ │ │ │ + label.setAttributeNS(null, "fill", style.fontColor) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - addControls: function(controls) { │ │ │ │ - if (!OpenLayers.Util.isArray(controls)) { │ │ │ │ - controls = [controls] │ │ │ │ + if (style.fontStrokeColor) { │ │ │ │ + label.setAttributeNS(null, "stroke", style.fontStrokeColor) │ │ │ │ } │ │ │ │ - this.controls = this.controls.concat(controls); │ │ │ │ - for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ - var control = controls[i], │ │ │ │ - element = this.createControlMarkup(control); │ │ │ │ - OpenLayers.Element.addClass(element, control.displayClass + "ItemInactive"); │ │ │ │ - OpenLayers.Element.addClass(element, "olButton"); │ │ │ │ - if (control.title != "" && !element.title) { │ │ │ │ - element.title = control.title │ │ │ │ - } │ │ │ │ - control.panel_div = element │ │ │ │ + if (style.fontStrokeWidth) { │ │ │ │ + label.setAttributeNS(null, "stroke-width", style.fontStrokeWidth) │ │ │ │ } │ │ │ │ - if (this.map) { │ │ │ │ - this.addControlsToMap(controls); │ │ │ │ - this.redraw() │ │ │ │ + if (style.fontOpacity) { │ │ │ │ + label.setAttributeNS(null, "opacity", style.fontOpacity) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - createControlMarkup: function(control) { │ │ │ │ - return document.createElement("div") │ │ │ │ - }, │ │ │ │ - addControlsToMap: function(controls) { │ │ │ │ - var control; │ │ │ │ - for (var i = 0, len = controls.length; i < len; i++) { │ │ │ │ - control = controls[i]; │ │ │ │ - if (control.autoActivate === true) { │ │ │ │ - control.autoActivate = false; │ │ │ │ - this.map.addControl(control); │ │ │ │ - control.autoActivate = true │ │ │ │ - } else { │ │ │ │ - this.map.addControl(control); │ │ │ │ - control.deactivate() │ │ │ │ - } │ │ │ │ - control.events.on({ │ │ │ │ - activate: this.iconOn, │ │ │ │ - deactivate: this.iconOff │ │ │ │ - }) │ │ │ │ + if (style.fontFamily) { │ │ │ │ + label.setAttributeNS(null, "font-family", style.fontFamily) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - iconOn: function() { │ │ │ │ - var d = this.panel_div; │ │ │ │ - var re = new RegExp("\\b(" + this.displayClass + "Item)Inactive\\b"); │ │ │ │ - d.className = d.className.replace(re, "$1Active") │ │ │ │ - }, │ │ │ │ - iconOff: function() { │ │ │ │ - var d = this.panel_div; │ │ │ │ - var re = new RegExp("\\b(" + this.displayClass + "Item)Active\\b"); │ │ │ │ - d.className = d.className.replace(re, "$1Inactive") │ │ │ │ - }, │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - var controls = this.controls, │ │ │ │ - button = evt.buttonElement; │ │ │ │ - for (var i = controls.length - 1; i >= 0; --i) { │ │ │ │ - if (controls[i].panel_div === button) { │ │ │ │ - this.activateControl(controls[i]); │ │ │ │ - break │ │ │ │ - } │ │ │ │ + if (style.fontSize) { │ │ │ │ + label.setAttributeNS(null, "font-size", style.fontSize) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - getControlsBy: function(property, match) { │ │ │ │ - var test = typeof match.test == "function"; │ │ │ │ - var found = OpenLayers.Array.filter(this.controls, function(item) { │ │ │ │ - return item[property] == match || test && match.test(item[property]) │ │ │ │ - }); │ │ │ │ - return found │ │ │ │ - }, │ │ │ │ - getControlsByName: function(match) { │ │ │ │ - return this.getControlsBy("name", match) │ │ │ │ - }, │ │ │ │ - getControlsByClass: function(match) { │ │ │ │ - return this.getControlsBy("CLASS_NAME", match) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Panel" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Snapping = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - DEFAULTS: { │ │ │ │ - tolerance: 10, │ │ │ │ - node: true, │ │ │ │ - edge: true, │ │ │ │ - vertex: true │ │ │ │ - }, │ │ │ │ - greedy: true, │ │ │ │ - precedence: ["node", "vertex", "edge"], │ │ │ │ - resolution: null, │ │ │ │ - geoToleranceCache: null, │ │ │ │ - layer: null, │ │ │ │ - feature: null, │ │ │ │ - point: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.options = options || {}; │ │ │ │ - if (this.options.layer) { │ │ │ │ - this.setLayer(this.options.layer) │ │ │ │ + if (style.fontWeight) { │ │ │ │ + label.setAttributeNS(null, "font-weight", style.fontWeight) │ │ │ │ } │ │ │ │ - var defaults = OpenLayers.Util.extend({}, this.options.defaults); │ │ │ │ - this.defaults = OpenLayers.Util.applyDefaults(defaults, this.DEFAULTS); │ │ │ │ - this.setTargets(this.options.targets); │ │ │ │ - if (this.targets.length === 0 && this.layer) { │ │ │ │ - this.addTargetLayer(this.layer) │ │ │ │ + if (style.fontStyle) { │ │ │ │ + label.setAttributeNS(null, "font-style", style.fontStyle) │ │ │ │ } │ │ │ │ - this.geoToleranceCache = {} │ │ │ │ - }, │ │ │ │ - setLayer: function(layer) { │ │ │ │ - if (this.active) { │ │ │ │ - this.deactivate(); │ │ │ │ - this.layer = layer; │ │ │ │ - this.activate() │ │ │ │ + if (style.labelSelect === true) { │ │ │ │ + label.setAttributeNS(null, "pointer-events", "visible"); │ │ │ │ + label._featureId = featureId │ │ │ │ } else { │ │ │ │ - this.layer = layer │ │ │ │ + label.setAttributeNS(null, "pointer-events", "none") │ │ │ │ } │ │ │ │ - }, │ │ │ │ - setTargets: function(targets) { │ │ │ │ - this.targets = []; │ │ │ │ - if (targets && targets.length) { │ │ │ │ - var target; │ │ │ │ - for (var i = 0, len = targets.length; i < len; ++i) { │ │ │ │ - target = targets[i]; │ │ │ │ - if (target instanceof OpenLayers.Layer.Vector) { │ │ │ │ - this.addTargetLayer(target) │ │ │ │ - } else { │ │ │ │ - this.addTarget(target) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + var align = style.labelAlign || OpenLayers.Renderer.defaultSymbolizer.labelAlign; │ │ │ │ + label.setAttributeNS(null, "text-anchor", OpenLayers.Renderer.SVG.LABEL_ALIGN[align[0]] || "middle"); │ │ │ │ + if (OpenLayers.IS_GECKO === true) { │ │ │ │ + label.setAttributeNS(null, "dominant-baseline", OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central") │ │ │ │ } │ │ │ │ - }, │ │ │ │ - addTargetLayer: function(layer) { │ │ │ │ - this.addTarget({ │ │ │ │ - layer: layer │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - addTarget: function(target) { │ │ │ │ - target = OpenLayers.Util.applyDefaults(target, this.defaults); │ │ │ │ - target.nodeTolerance = target.nodeTolerance || target.tolerance; │ │ │ │ - target.vertexTolerance = target.vertexTolerance || target.tolerance; │ │ │ │ - target.edgeTolerance = target.edgeTolerance || target.tolerance; │ │ │ │ - this.targets.push(target) │ │ │ │ - }, │ │ │ │ - removeTargetLayer: function(layer) { │ │ │ │ - var target; │ │ │ │ - for (var i = this.targets.length - 1; i >= 0; --i) { │ │ │ │ - target = this.targets[i]; │ │ │ │ - if (target.layer === layer) { │ │ │ │ - this.removeTarget(target) │ │ │ │ - } │ │ │ │ + var labelRows = style.label.split("\n"); │ │ │ │ + var numRows = labelRows.length; │ │ │ │ + while (label.childNodes.length > numRows) { │ │ │ │ + label.removeChild(label.lastChild) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - removeTarget: function(target) { │ │ │ │ - return OpenLayers.Util.removeItem(this.targets, target) │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Control.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - if (this.layer && this.layer.events) { │ │ │ │ - this.layer.events.on({ │ │ │ │ - sketchstarted: this.onSketchModified, │ │ │ │ - sketchmodified: this.onSketchModified, │ │ │ │ - vertexmodified: this.onVertexModified, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ + 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 │ │ │ │ } │ │ │ │ - } │ │ │ │ - return activated │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Control.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - if (this.layer && this.layer.events) { │ │ │ │ - this.layer.events.un({ │ │ │ │ - sketchstarted: this.onSketchModified, │ │ │ │ - sketchmodified: this.onSketchModified, │ │ │ │ - vertexmodified: this.onVertexModified, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ + if (OpenLayers.IS_GECKO === false) { │ │ │ │ + tspan.setAttributeNS(null, "baseline-shift", OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%") │ │ │ │ } │ │ │ │ - } │ │ │ │ - this.feature = null; │ │ │ │ - this.point = null; │ │ │ │ - return deactivated │ │ │ │ - }, │ │ │ │ - onSketchModified: function(event) { │ │ │ │ - this.feature = event.feature; │ │ │ │ - this.considerSnapping(event.vertex, event.vertex) │ │ │ │ - }, │ │ │ │ - onVertexModified: function(event) { │ │ │ │ - this.feature = event.feature; │ │ │ │ - var loc = this.layer.map.getLonLatFromViewPortPx(event.pixel); │ │ │ │ - this.considerSnapping(event.vertex, new OpenLayers.Geometry.Point(loc.lon, loc.lat)) │ │ │ │ - }, │ │ │ │ - considerSnapping: function(point, loc) { │ │ │ │ - var best = { │ │ │ │ - rank: Number.POSITIVE_INFINITY, │ │ │ │ - dist: Number.POSITIVE_INFINITY, │ │ │ │ - x: null, │ │ │ │ - y: null │ │ │ │ - }; │ │ │ │ - var snapped = false; │ │ │ │ - var result, target; │ │ │ │ - for (var i = 0, len = this.targets.length; i < len; ++i) { │ │ │ │ - target = this.targets[i]; │ │ │ │ - result = this.testTarget(target, loc); │ │ │ │ - if (result) { │ │ │ │ - if (this.greedy) { │ │ │ │ - best = result; │ │ │ │ - best.target = target; │ │ │ │ - snapped = true; │ │ │ │ - break │ │ │ │ - } else { │ │ │ │ - if (result.rank < best.rank || result.rank === best.rank && result.dist < best.dist) { │ │ │ │ - best = result; │ │ │ │ - best.target = target; │ │ │ │ - snapped = true │ │ │ │ - } │ │ │ │ + tspan.setAttribute("x", x); │ │ │ │ + if (i == 0) { │ │ │ │ + var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]]; │ │ │ │ + if (vfactor == null) { │ │ │ │ + vfactor = -.5 │ │ │ │ } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (snapped) { │ │ │ │ - var proceed = this.events.triggerEvent("beforesnap", { │ │ │ │ - point: point, │ │ │ │ - x: best.x, │ │ │ │ - y: best.y, │ │ │ │ - distance: best.dist, │ │ │ │ - layer: best.target.layer, │ │ │ │ - snapType: this.precedence[best.rank] │ │ │ │ - }); │ │ │ │ - if (proceed !== false) { │ │ │ │ - point.x = best.x; │ │ │ │ - point.y = best.y; │ │ │ │ - this.point = point; │ │ │ │ - this.events.triggerEvent("snap", { │ │ │ │ - point: point, │ │ │ │ - snapType: this.precedence[best.rank], │ │ │ │ - layer: best.target.layer, │ │ │ │ - distance: best.dist │ │ │ │ - }) │ │ │ │ + tspan.setAttribute("dy", vfactor * (numRows - 1) + "em") │ │ │ │ } else { │ │ │ │ - snapped = false │ │ │ │ + tspan.setAttribute("dy", "1em") │ │ │ │ } │ │ │ │ - } │ │ │ │ - if (this.point && !snapped) { │ │ │ │ - point.x = loc.x; │ │ │ │ - point.y = loc.y; │ │ │ │ - this.point = null; │ │ │ │ - this.events.triggerEvent("unsnap", { │ │ │ │ - point: point │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - testTarget: function(target, loc) { │ │ │ │ - var resolution = this.layer.map.getResolution(); │ │ │ │ - if ("minResolution" in target) { │ │ │ │ - if (resolution < target.minResolution) { │ │ │ │ - return null │ │ │ │ + tspan.textContent = labelRows[i] === "" ? " " : labelRows[i]; │ │ │ │ + if (!tspan.parentNode) { │ │ │ │ + label.appendChild(tspan) │ │ │ │ } │ │ │ │ } │ │ │ │ - if ("maxResolution" in target) { │ │ │ │ - if (resolution >= target.maxResolution) { │ │ │ │ - return null │ │ │ │ - } │ │ │ │ + if (!label.parentNode) { │ │ │ │ + this.textRoot.appendChild(label) │ │ │ │ } │ │ │ │ - var tolerance = { │ │ │ │ - node: this.getGeoTolerance(target.nodeTolerance, resolution), │ │ │ │ - vertex: this.getGeoTolerance(target.vertexTolerance, resolution), │ │ │ │ - edge: this.getGeoTolerance(target.edgeTolerance, resolution) │ │ │ │ - }; │ │ │ │ - var maxTolerance = Math.max(tolerance.node, tolerance.vertex, tolerance.edge); │ │ │ │ - var result = { │ │ │ │ - rank: Number.POSITIVE_INFINITY, │ │ │ │ - dist: Number.POSITIVE_INFINITY │ │ │ │ - }; │ │ │ │ - var eligible = false; │ │ │ │ - var features = target.layer.features; │ │ │ │ - var feature, type, vertices, vertex, closest, dist, found; │ │ │ │ - var numTypes = this.precedence.length; │ │ │ │ - var ll = new OpenLayers.LonLat(loc.x, loc.y); │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - feature = features[i]; │ │ │ │ - if (feature !== this.feature && !feature._sketch && feature.state !== OpenLayers.State.DELETE && (!target.filter || target.filter.evaluate(feature))) { │ │ │ │ - if (feature.atPoint(ll, maxTolerance, maxTolerance)) { │ │ │ │ - for (var j = 0, stop = Math.min(result.rank + 1, numTypes); j < stop; ++j) { │ │ │ │ - type = this.precedence[j]; │ │ │ │ - if (target[type]) { │ │ │ │ - if (type === "edge") { │ │ │ │ - closest = feature.geometry.distanceTo(loc, { │ │ │ │ - details: true │ │ │ │ - }); │ │ │ │ - dist = closest.distance; │ │ │ │ - if (dist <= tolerance[type] && dist < result.dist) { │ │ │ │ - result = { │ │ │ │ - rank: j, │ │ │ │ - dist: dist, │ │ │ │ - x: closest.x0, │ │ │ │ - y: closest.y0 │ │ │ │ - }; │ │ │ │ - eligible = true; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - vertices = feature.geometry.getVertices(type === "node"); │ │ │ │ - found = false; │ │ │ │ - for (var k = 0, klen = vertices.length; k < klen; ++k) { │ │ │ │ - vertex = vertices[k]; │ │ │ │ - dist = vertex.distanceTo(loc); │ │ │ │ - if (dist <= tolerance[type] && (j < result.rank || j === result.rank && dist < result.dist)) { │ │ │ │ - result = { │ │ │ │ - rank: j, │ │ │ │ - dist: dist, │ │ │ │ - x: vertex.x, │ │ │ │ - y: vertex.y │ │ │ │ - }; │ │ │ │ - eligible = true; │ │ │ │ - found = true │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (found) { │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + }, │ │ │ │ + getComponentsString: function(components, separator) { │ │ │ │ + var renderCmp = []; │ │ │ │ + var complete = true; │ │ │ │ + var len = components.length; │ │ │ │ + var strings = []; │ │ │ │ + var str, component; │ │ │ │ + for (var i = 0; i < len; i++) { │ │ │ │ + component = components[i]; │ │ │ │ + renderCmp.push(component); │ │ │ │ + str = this.getShortString(component); │ │ │ │ + if (str) { │ │ │ │ + strings.push(str) │ │ │ │ + } else { │ │ │ │ + if (i > 0) { │ │ │ │ + if (this.getShortString(components[i - 1])) { │ │ │ │ + strings.push(this.clipLine(components[i], components[i - 1])) │ │ │ │ } │ │ │ │ } │ │ │ │ + if (i < len - 1) { │ │ │ │ + if (this.getShortString(components[i + 1])) { │ │ │ │ + strings.push(this.clipLine(components[i], components[i + 1])) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + complete = false │ │ │ │ } │ │ │ │ } │ │ │ │ - return eligible ? result : null │ │ │ │ - }, │ │ │ │ - getGeoTolerance: function(tolerance, resolution) { │ │ │ │ - if (resolution !== this.resolution) { │ │ │ │ - this.resolution = resolution; │ │ │ │ - this.geoToleranceCache = {} │ │ │ │ - } │ │ │ │ - var geoTolerance = this.geoToleranceCache[tolerance]; │ │ │ │ - if (geoTolerance === undefined) { │ │ │ │ - geoTolerance = tolerance * resolution; │ │ │ │ - this.geoToleranceCache[tolerance] = geoTolerance │ │ │ │ + return { │ │ │ │ + path: strings.join(separator || ","), │ │ │ │ + complete: complete │ │ │ │ } │ │ │ │ - return geoTolerance │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.deactivate() │ │ │ │ + clipLine: function(badComponent, goodComponent) { │ │ │ │ + if (goodComponent.equals(badComponent)) { │ │ │ │ + return "" │ │ │ │ } │ │ │ │ - delete this.layer; │ │ │ │ - delete this.targets; │ │ │ │ - OpenLayers.Control.prototype.destroy.call(this) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Snapping" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - layers: null, │ │ │ │ - imageFormat: "image/png", │ │ │ │ - quotaRegEx: /quota/i, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - var i, layers = this.layers || map.layers; │ │ │ │ - for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ - this.addLayer({ │ │ │ │ - layer: layers[i] │ │ │ │ - }) │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var maxX = this.MAX_PIXEL - this.translationParameters.x; │ │ │ │ + var maxY = this.MAX_PIXEL - this.translationParameters.y; │ │ │ │ + var x1 = (goodComponent.x - this.featureDx) / resolution + this.left; │ │ │ │ + var y1 = this.top - goodComponent.y / resolution; │ │ │ │ + var x2 = (badComponent.x - this.featureDx) / resolution + this.left; │ │ │ │ + var y2 = this.top - badComponent.y / resolution; │ │ │ │ + var k; │ │ │ │ + if (x2 < -maxX || x2 > maxX) { │ │ │ │ + k = (y2 - y1) / (x2 - x1); │ │ │ │ + x2 = x2 < 0 ? -maxX : maxX; │ │ │ │ + y2 = y1 + (x2 - x1) * k │ │ │ │ } │ │ │ │ - if (!this.layers) { │ │ │ │ - map.events.on({ │ │ │ │ - addlayer: this.addLayer, │ │ │ │ - removeLayer: this.removeLayer, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ + if (y2 < -maxY || y2 > maxY) { │ │ │ │ + k = (x2 - x1) / (y2 - y1); │ │ │ │ + y2 = y2 < 0 ? -maxY : maxY; │ │ │ │ + x2 = x1 + (y2 - y1) * k │ │ │ │ } │ │ │ │ + return x2 + "," + y2 │ │ │ │ }, │ │ │ │ - addLayer: function(evt) { │ │ │ │ - evt.layer.events.on({ │ │ │ │ - tileloadstart: this.makeSameOrigin, │ │ │ │ - tileloaded: this.onTileLoaded, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - removeLayer: function(evt) { │ │ │ │ - evt.layer.events.un({ │ │ │ │ - tileloadstart: this.makeSameOrigin, │ │ │ │ - tileloaded: this.onTileLoaded, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - makeSameOrigin: function(evt) { │ │ │ │ - if (this.active) { │ │ │ │ - var tile = evt.tile; │ │ │ │ - if (tile instanceof OpenLayers.Tile.Image && !tile.crossOriginKeyword && tile.url.substr(0, 5) !== "data:") { │ │ │ │ - var sameOriginUrl = OpenLayers.Request.makeSameOrigin(tile.url, OpenLayers.ProxyHost); │ │ │ │ - OpenLayers.Control.CacheWrite.urlMap[sameOriginUrl] = tile.url; │ │ │ │ - tile.url = sameOriginUrl │ │ │ │ - } │ │ │ │ + getShortString: function(point) { │ │ │ │ + var resolution = this.getResolution(); │ │ │ │ + var x = (point.x - this.featureDx) / resolution + this.left; │ │ │ │ + var y = this.top - point.y / resolution; │ │ │ │ + if (this.inValidRange(x, y)) { │ │ │ │ + return x + "," + y │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ } │ │ │ │ }, │ │ │ │ - onTileLoaded: function(evt) { │ │ │ │ - if (this.active && !evt.aborted && evt.tile instanceof OpenLayers.Tile.Image && evt.tile.url.substr(0, 5) !== "data:") { │ │ │ │ - this.cache({ │ │ │ │ - tile: evt.tile │ │ │ │ - }); │ │ │ │ - delete OpenLayers.Control.CacheWrite.urlMap[evt.tile.url] │ │ │ │ + getPosition: function(node) { │ │ │ │ + return { │ │ │ │ + x: parseFloat(node.getAttributeNS(null, "cx")), │ │ │ │ + y: parseFloat(node.getAttributeNS(null, "cy")) │ │ │ │ } │ │ │ │ }, │ │ │ │ - cache: function(obj) { │ │ │ │ - if (window.localStorage) { │ │ │ │ - var tile = obj.tile; │ │ │ │ - try { │ │ │ │ - var canvasContext = tile.getCanvasContext(); │ │ │ │ - if (canvasContext) { │ │ │ │ - var urlMap = OpenLayers.Control.CacheWrite.urlMap; │ │ │ │ - var url = urlMap[tile.url] || tile.url; │ │ │ │ - window.localStorage.setItem("olCache_" + url, canvasContext.canvas.toDataURL(this.imageFormat)) │ │ │ │ - } │ │ │ │ - } catch (e) { │ │ │ │ - var reason = e.name || e.message; │ │ │ │ - if (reason && this.quotaRegEx.test(reason)) { │ │ │ │ - this.events.triggerEvent("cachefull", { │ │ │ │ - tile: tile │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - OpenLayers.Console.error(e.toString()) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + importSymbol: function(graphicName) { │ │ │ │ + if (!this.defs) { │ │ │ │ + this.defs = this.createDefs() │ │ │ │ } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.layers || this.map) { │ │ │ │ - var i, layers = this.layers || this.map.layers; │ │ │ │ - for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ - this.removeLayer({ │ │ │ │ - layer: layers[i] │ │ │ │ - }) │ │ │ │ - } │ │ │ │ + var id = this.container.id + "-" + graphicName; │ │ │ │ + var existing = document.getElementById(id); │ │ │ │ + if (existing != null) { │ │ │ │ + return existing │ │ │ │ } │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.un({ │ │ │ │ - addlayer: this.addLayer, │ │ │ │ - removeLayer: this.removeLayer, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ + var symbol = OpenLayers.Renderer.symbol[graphicName]; │ │ │ │ + if (!symbol) { │ │ │ │ + throw new Error(graphicName + " is not a valid symbol name") │ │ │ │ } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ + var symbolNode = this.nodeFactory(id, "symbol"); │ │ │ │ + var node = this.nodeFactory(null, "polygon"); │ │ │ │ + symbolNode.appendChild(node); │ │ │ │ + var symbolExtent = new OpenLayers.Bounds(Number.MAX_VALUE, Number.MAX_VALUE, 0, 0); │ │ │ │ + var points = []; │ │ │ │ + var x, y; │ │ │ │ + for (var i = 0; i < symbol.length; i = i + 2) { │ │ │ │ + x = symbol[i]; │ │ │ │ + y = symbol[i + 1]; │ │ │ │ + symbolExtent.left = Math.min(symbolExtent.left, x); │ │ │ │ + symbolExtent.bottom = Math.min(symbolExtent.bottom, y); │ │ │ │ + symbolExtent.right = Math.max(symbolExtent.right, x); │ │ │ │ + symbolExtent.top = Math.max(symbolExtent.top, y); │ │ │ │ + points.push(x, ",", y) │ │ │ │ + } │ │ │ │ + node.setAttributeNS(null, "points", points.join(" ")); │ │ │ │ + var width = symbolExtent.getWidth(); │ │ │ │ + var height = symbolExtent.getHeight(); │ │ │ │ + var viewBox = [symbolExtent.left - width, symbolExtent.bottom - height, width * 3, height * 3]; │ │ │ │ + symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" ")); │ │ │ │ + this.symbolMetrics[id] = [Math.max(width, height), symbolExtent.getCenterLonLat().lon, symbolExtent.getCenterLonLat().lat]; │ │ │ │ + this.defs.appendChild(symbolNode); │ │ │ │ + return symbolNode │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.CacheWrite" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.CacheWrite.clearCache = function() { │ │ │ │ - if (!window.localStorage) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var i, key; │ │ │ │ - for (i = window.localStorage.length - 1; i >= 0; --i) { │ │ │ │ - key = window.localStorage.key(i); │ │ │ │ - if (key.substr(0, 8) === "olCache_") { │ │ │ │ - window.localStorage.removeItem(key) │ │ │ │ + getFeatureIdFromEvent: function(evt) { │ │ │ │ + var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments); │ │ │ │ + if (!featureId) { │ │ │ │ + var target = evt.target; │ │ │ │ + featureId = target.parentNode && target != this.rendererRoot ? target.parentNode._featureId : undefined │ │ │ │ } │ │ │ │ - } │ │ │ │ + return featureId │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Renderer.SVG" │ │ │ │ +}); │ │ │ │ +OpenLayers.Renderer.SVG.LABEL_ALIGN = { │ │ │ │ + l: "start", │ │ │ │ + r: "end", │ │ │ │ + b: "bottom", │ │ │ │ + t: "hanging" │ │ │ │ +}; │ │ │ │ +OpenLayers.Renderer.SVG.LABEL_VSHIFT = { │ │ │ │ + t: "-70%", │ │ │ │ + b: "0" │ │ │ │ +}; │ │ │ │ +OpenLayers.Renderer.SVG.LABEL_VFACTOR = { │ │ │ │ + t: 0, │ │ │ │ + b: -1 │ │ │ │ +}; │ │ │ │ +OpenLayers.Renderer.SVG.preventDefault = function(e) { │ │ │ │ + OpenLayers.Event.preventDefault(e) │ │ │ │ }; │ │ │ │ -OpenLayers.Control.CacheWrite.urlMap = {}; │ │ │ │ OpenLayers.Handler.Drag = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ started: false, │ │ │ │ stopDown: true, │ │ │ │ dragging: false, │ │ │ │ last: null, │ │ │ │ start: null, │ │ │ │ lastMoveEvt: null, │ │ │ │ @@ -19488,138 +17792,125 @@ │ │ │ │ this.layer.setZIndex(index) │ │ │ │ } else { │ │ │ │ this.map.setLayerZIndex(this.layer, this.map.getLayerIndex(this.layer)) │ │ │ │ } │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.Handler.Feature" │ │ │ │ }); │ │ │ │ -OpenLayers.Control.DragFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - geometryTypes: null, │ │ │ │ - onStart: function(feature, pixel) {}, │ │ │ │ - onDrag: function(feature, pixel) {}, │ │ │ │ - onComplete: function(feature, pixel) {}, │ │ │ │ - onEnter: function(feature) {}, │ │ │ │ - onLeave: function(feature) {}, │ │ │ │ - documentDrag: false, │ │ │ │ - layer: null, │ │ │ │ - feature: null, │ │ │ │ - dragCallbacks: {}, │ │ │ │ - featureCallbacks: {}, │ │ │ │ - lastPixel: null, │ │ │ │ - initialize: function(layer, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.layer = layer; │ │ │ │ - this.handlers = { │ │ │ │ - drag: new OpenLayers.Handler.Drag(this, OpenLayers.Util.extend({ │ │ │ │ - down: this.downFeature, │ │ │ │ - move: this.moveFeature, │ │ │ │ - up: this.upFeature, │ │ │ │ - out: this.cancel, │ │ │ │ - done: this.doneDragging │ │ │ │ - }, this.dragCallbacks), { │ │ │ │ - documentDrag: this.documentDrag │ │ │ │ - }), │ │ │ │ - feature: new OpenLayers.Handler.Feature(this, this.layer, OpenLayers.Util.extend({ │ │ │ │ - click: this.clickFeature, │ │ │ │ - clickout: this.clickoutFeature, │ │ │ │ - over: this.overFeature, │ │ │ │ - out: this.outFeature │ │ │ │ - }, this.featureCallbacks), { │ │ │ │ - geometryTypes: this.geometryTypes │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - clickFeature: function(feature) { │ │ │ │ - if (this.handlers.feature.touch && !this.over && this.overFeature(feature)) { │ │ │ │ - this.handlers.drag.dragstart(this.handlers.feature.evt); │ │ │ │ - this.handlers.drag.stopDown = false │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - clickoutFeature: function(feature) { │ │ │ │ - if (this.handlers.feature.touch && this.over) { │ │ │ │ - this.outFeature(feature); │ │ │ │ - this.handlers.drag.stopDown = true │ │ │ │ - } │ │ │ │ +OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + wheelListener: null, │ │ │ │ + interval: 0, │ │ │ │ + maxDelta: Number.POSITIVE_INFINITY, │ │ │ │ + delta: 0, │ │ │ │ + cumulative: true, │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ + this.wheelListener = OpenLayers.Function.bindAsEventListener(this.onWheelEvent, this) │ │ │ │ }, │ │ │ │ destroy: function() { │ │ │ │ - this.layer = null; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, []) │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - return this.handlers.feature.activate() && OpenLayers.Control.prototype.activate.apply(this, arguments) │ │ │ │ + OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ + this.wheelListener = null │ │ │ │ }, │ │ │ │ - deactivate: function() { │ │ │ │ - this.handlers.drag.deactivate(); │ │ │ │ - this.handlers.feature.deactivate(); │ │ │ │ - this.feature = null; │ │ │ │ - this.dragging = false; │ │ │ │ - this.lastPixel = null; │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass + "Over"); │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply(this, arguments) │ │ │ │ + onWheelEvent: function(e) { │ │ │ │ + if (!this.map || !this.checkModifiers(e)) { │ │ │ │ + return │ │ │ │ + } │ │ │ │ + var overScrollableDiv = false; │ │ │ │ + var allowScroll = false; │ │ │ │ + var overMapDiv = false; │ │ │ │ + var elem = OpenLayers.Event.element(e); │ │ │ │ + while (elem != null && !overMapDiv && !overScrollableDiv) { │ │ │ │ + if (!overScrollableDiv) { │ │ │ │ + try { │ │ │ │ + var overflow; │ │ │ │ + if (elem.currentStyle) { │ │ │ │ + overflow = elem.currentStyle["overflow"] │ │ │ │ + } else { │ │ │ │ + var style = document.defaultView.getComputedStyle(elem, null); │ │ │ │ + overflow = style.getPropertyValue("overflow") │ │ │ │ + } │ │ │ │ + overScrollableDiv = overflow && overflow == "auto" || overflow == "scroll" │ │ │ │ + } catch (err) {} │ │ │ │ + } │ │ │ │ + if (!allowScroll) { │ │ │ │ + allowScroll = OpenLayers.Element.hasClass(elem, "olScrollable"); │ │ │ │ + if (!allowScroll) { │ │ │ │ + for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ + var layer = this.map.layers[i]; │ │ │ │ + if (elem == layer.div || elem == layer.pane) { │ │ │ │ + allowScroll = true; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + overMapDiv = elem == this.map.div; │ │ │ │ + elem = elem.parentNode │ │ │ │ + } │ │ │ │ + if (!overScrollableDiv && overMapDiv) { │ │ │ │ + if (allowScroll) { │ │ │ │ + var delta = 0; │ │ │ │ + if (e.wheelDelta) { │ │ │ │ + delta = e.wheelDelta; │ │ │ │ + if (delta % 160 === 0) { │ │ │ │ + delta = delta * .75 │ │ │ │ + } │ │ │ │ + delta = delta / 120 │ │ │ │ + } else if (e.detail) { │ │ │ │ + delta = -(e.detail / Math.abs(e.detail)) │ │ │ │ + } │ │ │ │ + this.delta += delta; │ │ │ │ + window.clearTimeout(this._timeoutId); │ │ │ │ + if (this.interval && Math.abs(this.delta) < this.maxDelta) { │ │ │ │ + var evt = OpenLayers.Util.extend({}, e); │ │ │ │ + this._timeoutId = window.setTimeout(OpenLayers.Function.bind(function() { │ │ │ │ + this.wheelZoom(evt) │ │ │ │ + }, this), this.interval) │ │ │ │ + } else { │ │ │ │ + this.wheelZoom(e) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + OpenLayers.Event.stop(e) │ │ │ │ + } │ │ │ │ }, │ │ │ │ - overFeature: function(feature) { │ │ │ │ - var activated = false; │ │ │ │ - if (!this.handlers.drag.dragging) { │ │ │ │ - this.feature = feature; │ │ │ │ - this.handlers.drag.activate(); │ │ │ │ - activated = true; │ │ │ │ - this.over = true; │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + "Over"); │ │ │ │ - this.onEnter(feature) │ │ │ │ - } else { │ │ │ │ - if (this.feature.id == feature.id) { │ │ │ │ - this.over = true │ │ │ │ + wheelZoom: function(e) { │ │ │ │ + var delta = this.delta; │ │ │ │ + this.delta = 0; │ │ │ │ + if (delta) { │ │ │ │ + e.xy = this.map.events.getMousePosition(e); │ │ │ │ + if (delta < 0) { │ │ │ │ + this.callback("down", [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]) │ │ │ │ } else { │ │ │ │ - this.over = false │ │ │ │ + this.callback("up", [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]) │ │ │ │ } │ │ │ │ } │ │ │ │ - return activated │ │ │ │ }, │ │ │ │ - downFeature: function(pixel) { │ │ │ │ - this.lastPixel = pixel; │ │ │ │ - this.onStart(this.feature, pixel) │ │ │ │ - }, │ │ │ │ - moveFeature: function(pixel) { │ │ │ │ - var res = this.map.getResolution(); │ │ │ │ - this.feature.geometry.move(res * (pixel.x - this.lastPixel.x), res * (this.lastPixel.y - pixel.y)); │ │ │ │ - this.layer.drawFeature(this.feature); │ │ │ │ - this.lastPixel = pixel; │ │ │ │ - this.onDrag(this.feature, pixel) │ │ │ │ - }, │ │ │ │ - upFeature: function(pixel) { │ │ │ │ - if (!this.over) { │ │ │ │ - this.handlers.drag.deactivate() │ │ │ │ + activate: function(evt) { │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + var wheelListener = this.wheelListener; │ │ │ │ + OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener); │ │ │ │ + OpenLayers.Event.observe(window, "mousewheel", wheelListener); │ │ │ │ + OpenLayers.Event.observe(document, "mousewheel", wheelListener); │ │ │ │ + return true │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ } │ │ │ │ }, │ │ │ │ - doneDragging: function(pixel) { │ │ │ │ - this.onComplete(this.feature, pixel) │ │ │ │ - }, │ │ │ │ - outFeature: function(feature) { │ │ │ │ - if (!this.handlers.drag.dragging) { │ │ │ │ - this.over = false; │ │ │ │ - this.handlers.drag.deactivate(); │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, this.displayClass + "Over"); │ │ │ │ - this.onLeave(feature); │ │ │ │ - this.feature = null │ │ │ │ + deactivate: function(evt) { │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + var wheelListener = this.wheelListener; │ │ │ │ + OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener); │ │ │ │ + OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener); │ │ │ │ + OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener); │ │ │ │ + return true │ │ │ │ } else { │ │ │ │ - if (this.feature.id == feature.id) { │ │ │ │ - this.over = false │ │ │ │ - } │ │ │ │ + return false │ │ │ │ } │ │ │ │ }, │ │ │ │ - cancel: function() { │ │ │ │ - this.handlers.drag.deactivate(); │ │ │ │ - this.over = false │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - this.handlers.drag.setMap(map); │ │ │ │ - this.handlers.feature.setMap(map); │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.DragFeature" │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.MouseWheel" │ │ │ │ }); │ │ │ │ OpenLayers.Handler.RegularPolygon = OpenLayers.Class(OpenLayers.Handler.Drag, { │ │ │ │ sides: 4, │ │ │ │ radius: null, │ │ │ │ snapAngle: null, │ │ │ │ snapToggle: "shiftKey", │ │ │ │ layerOptions: null, │ │ │ │ @@ -20198,14 +18489,233 @@ │ │ │ │ if (!this.freehandMode(evt)) { │ │ │ │ this.finishGeometry() │ │ │ │ } │ │ │ │ return false │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.Handler.Path" │ │ │ │ }); │ │ │ │ +OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + delay: 300, │ │ │ │ + single: true, │ │ │ │ + double: false, │ │ │ │ + pixelTolerance: 0, │ │ │ │ + dblclickTolerance: 13, │ │ │ │ + stopSingle: false, │ │ │ │ + stopDouble: false, │ │ │ │ + timerId: null, │ │ │ │ + down: null, │ │ │ │ + last: null, │ │ │ │ + first: null, │ │ │ │ + rightclickTimerId: null, │ │ │ │ + touchstart: function(evt) { │ │ │ │ + this.startTouch(); │ │ │ │ + this.down = this.getEventInfo(evt); │ │ │ │ + this.last = this.getEventInfo(evt); │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + touchmove: function(evt) { │ │ │ │ + this.last = this.getEventInfo(evt); │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + touchend: function(evt) { │ │ │ │ + if (this.down) { │ │ │ │ + evt.xy = this.last.xy; │ │ │ │ + evt.lastTouches = this.last.touches; │ │ │ │ + this.handleSingle(evt); │ │ │ │ + this.down = null │ │ │ │ + } │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + mousedown: function(evt) { │ │ │ │ + this.down = this.getEventInfo(evt); │ │ │ │ + this.last = this.getEventInfo(evt); │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + mouseup: function(evt) { │ │ │ │ + var propagate = true; │ │ │ │ + if (this.checkModifiers(evt) && this.control.handleRightClicks && OpenLayers.Event.isRightClick(evt)) { │ │ │ │ + propagate = this.rightclick(evt) │ │ │ │ + } │ │ │ │ + return propagate │ │ │ │ + }, │ │ │ │ + rightclick: function(evt) { │ │ │ │ + if (this.passesTolerance(evt)) { │ │ │ │ + if (this.rightclickTimerId != null) { │ │ │ │ + this.clearTimer(); │ │ │ │ + this.callback("dblrightclick", [evt]); │ │ │ │ + return !this.stopDouble │ │ │ │ + } else { │ │ │ │ + var clickEvent = this["double"] ? OpenLayers.Util.extend({}, evt) : this.callback("rightclick", [evt]); │ │ │ │ + var delayedRightCall = OpenLayers.Function.bind(this.delayedRightCall, this, clickEvent); │ │ │ │ + this.rightclickTimerId = window.setTimeout(delayedRightCall, this.delay) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return !this.stopSingle │ │ │ │ + }, │ │ │ │ + delayedRightCall: function(evt) { │ │ │ │ + this.rightclickTimerId = null; │ │ │ │ + if (evt) { │ │ │ │ + this.callback("rightclick", [evt]) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + click: function(evt) { │ │ │ │ + if (!this.last) { │ │ │ │ + this.last = this.getEventInfo(evt) │ │ │ │ + } │ │ │ │ + this.handleSingle(evt); │ │ │ │ + return !this.stopSingle │ │ │ │ + }, │ │ │ │ + dblclick: function(evt) { │ │ │ │ + this.handleDouble(evt); │ │ │ │ + return !this.stopDouble │ │ │ │ + }, │ │ │ │ + handleDouble: function(evt) { │ │ │ │ + if (this.passesDblclickTolerance(evt)) { │ │ │ │ + if (this["double"]) { │ │ │ │ + this.callback("dblclick", [evt]) │ │ │ │ + } │ │ │ │ + this.clearTimer() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + handleSingle: function(evt) { │ │ │ │ + if (this.passesTolerance(evt)) { │ │ │ │ + if (this.timerId != null) { │ │ │ │ + if (this.last.touches && this.last.touches.length === 1) { │ │ │ │ + if (this["double"]) { │ │ │ │ + OpenLayers.Event.preventDefault(evt) │ │ │ │ + } │ │ │ │ + this.handleDouble(evt) │ │ │ │ + } │ │ │ │ + if (!this.last.touches || this.last.touches.length !== 2) { │ │ │ │ + this.clearTimer() │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + this.first = this.getEventInfo(evt); │ │ │ │ + var clickEvent = this.single ? OpenLayers.Util.extend({}, evt) : null; │ │ │ │ + this.queuePotentialClick(clickEvent) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + queuePotentialClick: function(evt) { │ │ │ │ + this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay) │ │ │ │ + }, │ │ │ │ + passesTolerance: function(evt) { │ │ │ │ + var passes = true; │ │ │ │ + if (this.pixelTolerance != null && this.down && this.down.xy) { │ │ │ │ + passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy); │ │ │ │ + if (passes && this.touch && this.down.touches.length === this.last.touches.length) { │ │ │ │ + for (var i = 0, ii = this.down.touches.length; i < ii; ++i) { │ │ │ │ + if (this.getTouchDistance(this.down.touches[i], this.last.touches[i]) > this.pixelTolerance) { │ │ │ │ + passes = false; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return passes │ │ │ │ + }, │ │ │ │ + getTouchDistance: function(from, to) { │ │ │ │ + return Math.sqrt(Math.pow(from.clientX - to.clientX, 2) + Math.pow(from.clientY - to.clientY, 2)) │ │ │ │ + }, │ │ │ │ + passesDblclickTolerance: function(evt) { │ │ │ │ + var passes = true; │ │ │ │ + if (this.down && this.first) { │ │ │ │ + passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance │ │ │ │ + } │ │ │ │ + return passes │ │ │ │ + }, │ │ │ │ + clearTimer: function() { │ │ │ │ + if (this.timerId != null) { │ │ │ │ + window.clearTimeout(this.timerId); │ │ │ │ + this.timerId = null │ │ │ │ + } │ │ │ │ + if (this.rightclickTimerId != null) { │ │ │ │ + window.clearTimeout(this.rightclickTimerId); │ │ │ │ + this.rightclickTimerId = null │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + delayedCall: function(evt) { │ │ │ │ + this.timerId = null; │ │ │ │ + if (evt) { │ │ │ │ + this.callback("click", [evt]) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + getEventInfo: function(evt) { │ │ │ │ + var touches; │ │ │ │ + if (evt.touches) { │ │ │ │ + var len = evt.touches.length; │ │ │ │ + touches = new Array(len); │ │ │ │ + var touch; │ │ │ │ + for (var i = 0; i < len; i++) { │ │ │ │ + touch = evt.touches[i]; │ │ │ │ + touches[i] = { │ │ │ │ + clientX: touch.olClientX, │ │ │ │ + clientY: touch.olClientY │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return { │ │ │ │ + xy: evt.xy, │ │ │ │ + touches: touches │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.clearTimer(); │ │ │ │ + this.down = null; │ │ │ │ + this.first = null; │ │ │ │ + this.last = null; │ │ │ │ + deactivated = true │ │ │ │ + } │ │ │ │ + return deactivated │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Click" │ │ │ │ +}); │ │ │ │ +OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + KEY_EVENTS: ["keydown", "keyup"], │ │ │ │ + eventListener: null, │ │ │ │ + observeElement: null, │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ + this.eventListener = OpenLayers.Function.bindAsEventListener(this.handleKeyEvent, this) │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.deactivate(); │ │ │ │ + this.eventListener = null; │ │ │ │ + OpenLayers.Handler.prototype.destroy.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + activate: function() { │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.observeElement = this.observeElement || document; │ │ │ │ + for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) { │ │ │ │ + OpenLayers.Event.observe(this.observeElement, this.KEY_EVENTS[i], this.eventListener) │ │ │ │ + } │ │ │ │ + return true │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) { │ │ │ │ + OpenLayers.Event.stopObserving(this.observeElement, this.KEY_EVENTS[i], this.eventListener) │ │ │ │ + } │ │ │ │ + deactivated = true │ │ │ │ + } │ │ │ │ + return deactivated │ │ │ │ + }, │ │ │ │ + handleKeyEvent: function(evt) { │ │ │ │ + if (this.checkModifiers(evt)) { │ │ │ │ + this.callback(evt.type, [evt]) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Keyboard" │ │ │ │ +}); │ │ │ │ OpenLayers.Handler.Polygon = OpenLayers.Class(OpenLayers.Handler.Path, { │ │ │ │ holeModifier: null, │ │ │ │ drawingHole: false, │ │ │ │ polygon: null, │ │ │ │ createFeature: function(pixel) { │ │ │ │ var lonlat = this.layer.getLonLatFromViewPortPx(pixel); │ │ │ │ var geometry = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat); │ │ │ │ @@ -20332,198 +18842,839 @@ │ │ │ │ if (geometry && this.multi) { │ │ │ │ geometry = new OpenLayers.Geometry.MultiPolygon([geometry]) │ │ │ │ } │ │ │ │ return geometry │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.Handler.Polygon" │ │ │ │ }); │ │ │ │ -OpenLayers.Handler.Click = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - delay: 300, │ │ │ │ - single: true, │ │ │ │ - double: false, │ │ │ │ - pixelTolerance: 0, │ │ │ │ - dblclickTolerance: 13, │ │ │ │ - stopSingle: false, │ │ │ │ - stopDouble: false, │ │ │ │ - timerId: null, │ │ │ │ - down: null, │ │ │ │ +OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + started: false, │ │ │ │ + stopDown: false, │ │ │ │ + pinching: false, │ │ │ │ last: null, │ │ │ │ - first: null, │ │ │ │ - rightclickTimerId: null, │ │ │ │ + start: null, │ │ │ │ touchstart: function(evt) { │ │ │ │ - this.startTouch(); │ │ │ │ - this.down = this.getEventInfo(evt); │ │ │ │ - this.last = this.getEventInfo(evt); │ │ │ │ - return true │ │ │ │ + var propagate = true; │ │ │ │ + this.pinching = false; │ │ │ │ + if (OpenLayers.Event.isMultiTouch(evt)) { │ │ │ │ + this.started = true; │ │ │ │ + this.last = this.start = { │ │ │ │ + distance: this.getDistance(evt.touches), │ │ │ │ + delta: 0, │ │ │ │ + scale: 1 │ │ │ │ + }; │ │ │ │ + this.callback("start", [evt, this.start]); │ │ │ │ + propagate = !this.stopDown │ │ │ │ + } else if (this.started) { │ │ │ │ + return false │ │ │ │ + } else { │ │ │ │ + this.started = false; │ │ │ │ + this.start = null; │ │ │ │ + this.last = null │ │ │ │ + } │ │ │ │ + OpenLayers.Event.preventDefault(evt); │ │ │ │ + return propagate │ │ │ │ }, │ │ │ │ touchmove: function(evt) { │ │ │ │ - this.last = this.getEventInfo(evt); │ │ │ │ + if (this.started && OpenLayers.Event.isMultiTouch(evt)) { │ │ │ │ + this.pinching = true; │ │ │ │ + var current = this.getPinchData(evt); │ │ │ │ + this.callback("move", [evt, current]); │ │ │ │ + this.last = current; │ │ │ │ + OpenLayers.Event.stop(evt) │ │ │ │ + } else if (this.started) { │ │ │ │ + return false │ │ │ │ + } │ │ │ │ return true │ │ │ │ }, │ │ │ │ touchend: function(evt) { │ │ │ │ - if (this.down) { │ │ │ │ - evt.xy = this.last.xy; │ │ │ │ - evt.lastTouches = this.last.touches; │ │ │ │ - this.handleSingle(evt); │ │ │ │ - this.down = null │ │ │ │ + if (this.started && !OpenLayers.Event.isMultiTouch(evt)) { │ │ │ │ + this.started = false; │ │ │ │ + this.pinching = false; │ │ │ │ + this.callback("done", [evt, this.start, this.last]); │ │ │ │ + this.start = null; │ │ │ │ + this.last = null; │ │ │ │ + return false │ │ │ │ } │ │ │ │ return true │ │ │ │ }, │ │ │ │ - mousedown: function(evt) { │ │ │ │ - this.down = this.getEventInfo(evt); │ │ │ │ - this.last = this.getEventInfo(evt); │ │ │ │ - return true │ │ │ │ + activate: function() { │ │ │ │ + var activated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.pinching = false; │ │ │ │ + activated = true │ │ │ │ + } │ │ │ │ + return activated │ │ │ │ }, │ │ │ │ - mouseup: function(evt) { │ │ │ │ - var propagate = true; │ │ │ │ - if (this.checkModifiers(evt) && this.control.handleRightClicks && OpenLayers.Event.isRightClick(evt)) { │ │ │ │ - propagate = this.rightclick(evt) │ │ │ │ + deactivate: function() { │ │ │ │ + var deactivated = false; │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + this.started = false; │ │ │ │ + this.pinching = false; │ │ │ │ + this.start = null; │ │ │ │ + this.last = null; │ │ │ │ + deactivated = true │ │ │ │ } │ │ │ │ - return propagate │ │ │ │ + return deactivated │ │ │ │ }, │ │ │ │ - rightclick: function(evt) { │ │ │ │ - if (this.passesTolerance(evt)) { │ │ │ │ - if (this.rightclickTimerId != null) { │ │ │ │ - this.clearTimer(); │ │ │ │ - this.callback("dblrightclick", [evt]); │ │ │ │ - return !this.stopDouble │ │ │ │ - } else { │ │ │ │ - var clickEvent = this["double"] ? OpenLayers.Util.extend({}, evt) : this.callback("rightclick", [evt]); │ │ │ │ - var delayedRightCall = OpenLayers.Function.bind(this.delayedRightCall, this, clickEvent); │ │ │ │ - this.rightclickTimerId = window.setTimeout(delayedRightCall, this.delay) │ │ │ │ - } │ │ │ │ + getDistance: function(touches) { │ │ │ │ + var t0 = touches[0]; │ │ │ │ + var t1 = touches[1]; │ │ │ │ + return Math.sqrt(Math.pow(t0.olClientX - t1.olClientX, 2) + Math.pow(t0.olClientY - t1.olClientY, 2)) │ │ │ │ + }, │ │ │ │ + getPinchData: function(evt) { │ │ │ │ + var distance = this.getDistance(evt.touches); │ │ │ │ + var scale = distance / this.start.distance; │ │ │ │ + return { │ │ │ │ + distance: distance, │ │ │ │ + delta: this.last.distance - distance, │ │ │ │ + scale: scale │ │ │ │ } │ │ │ │ - return !this.stopSingle │ │ │ │ }, │ │ │ │ - delayedRightCall: function(evt) { │ │ │ │ - this.rightclickTimerId = null; │ │ │ │ - if (evt) { │ │ │ │ - this.callback("rightclick", [evt]) │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Pinch" │ │ │ │ +}); │ │ │ │ +OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + dragHandler: null, │ │ │ │ + boxDivClassName: "olHandlerBoxZoomBox", │ │ │ │ + boxOffsets: null, │ │ │ │ + initialize: function(control, callbacks, options) { │ │ │ │ + OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ + this.dragHandler = new OpenLayers.Handler.Drag(this, { │ │ │ │ + down: this.startBox, │ │ │ │ + move: this.moveBox, │ │ │ │ + out: this.removeBox, │ │ │ │ + up: this.endBox │ │ │ │ + }, { │ │ │ │ + keyMask: this.keyMask │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ + if (this.dragHandler) { │ │ │ │ + this.dragHandler.destroy(); │ │ │ │ + this.dragHandler = null │ │ │ │ } │ │ │ │ }, │ │ │ │ - click: function(evt) { │ │ │ │ - if (!this.last) { │ │ │ │ - this.last = this.getEventInfo(evt) │ │ │ │ + setMap: function(map) { │ │ │ │ + OpenLayers.Handler.prototype.setMap.apply(this, arguments); │ │ │ │ + if (this.dragHandler) { │ │ │ │ + this.dragHandler.setMap(map) │ │ │ │ } │ │ │ │ - this.handleSingle(evt); │ │ │ │ - return !this.stopSingle │ │ │ │ }, │ │ │ │ - dblclick: function(evt) { │ │ │ │ - this.handleDouble(evt); │ │ │ │ - return !this.stopDouble │ │ │ │ + startBox: function(xy) { │ │ │ │ + this.callback("start", []); │ │ │ │ + this.zoomBox = OpenLayers.Util.createDiv("zoomBox", { │ │ │ │ + x: -9999, │ │ │ │ + y: -9999 │ │ │ │ + }); │ │ │ │ + this.zoomBox.className = this.boxDivClassName; │ │ │ │ + this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1; │ │ │ │ + this.map.viewPortDiv.appendChild(this.zoomBox); │ │ │ │ + OpenLayers.Element.addClass(this.map.viewPortDiv, "olDrawBox") │ │ │ │ }, │ │ │ │ - handleDouble: function(evt) { │ │ │ │ - if (this.passesDblclickTolerance(evt)) { │ │ │ │ - if (this["double"]) { │ │ │ │ - this.callback("dblclick", [evt]) │ │ │ │ - } │ │ │ │ - this.clearTimer() │ │ │ │ + moveBox: function(xy) { │ │ │ │ + var startX = this.dragHandler.start.x; │ │ │ │ + var startY = this.dragHandler.start.y; │ │ │ │ + var deltaX = Math.abs(startX - xy.x); │ │ │ │ + var deltaY = Math.abs(startY - xy.y); │ │ │ │ + var offset = this.getBoxOffsets(); │ │ │ │ + this.zoomBox.style.width = deltaX + offset.width + 1 + "px"; │ │ │ │ + this.zoomBox.style.height = deltaY + offset.height + 1 + "px"; │ │ │ │ + this.zoomBox.style.left = (xy.x < startX ? startX - deltaX - offset.left : startX - offset.left) + "px"; │ │ │ │ + this.zoomBox.style.top = (xy.y < startY ? startY - deltaY - offset.top : startY - offset.top) + "px" │ │ │ │ + }, │ │ │ │ + endBox: function(end) { │ │ │ │ + var result; │ │ │ │ + if (Math.abs(this.dragHandler.start.x - end.x) > 5 || Math.abs(this.dragHandler.start.y - end.y) > 5) { │ │ │ │ + var start = this.dragHandler.start; │ │ │ │ + var top = Math.min(start.y, end.y); │ │ │ │ + var bottom = Math.max(start.y, end.y); │ │ │ │ + var left = Math.min(start.x, end.x); │ │ │ │ + var right = Math.max(start.x, end.x); │ │ │ │ + result = new OpenLayers.Bounds(left, bottom, right, top) │ │ │ │ + } else { │ │ │ │ + result = this.dragHandler.start.clone() │ │ │ │ + } │ │ │ │ + this.removeBox(); │ │ │ │ + this.callback("done", [result]) │ │ │ │ + }, │ │ │ │ + removeBox: function() { │ │ │ │ + this.map.viewPortDiv.removeChild(this.zoomBox); │ │ │ │ + this.zoomBox = null; │ │ │ │ + this.boxOffsets = null; │ │ │ │ + OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDrawBox") │ │ │ │ + }, │ │ │ │ + activate: function() { │ │ │ │ + if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ + this.dragHandler.activate(); │ │ │ │ + return true │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ } │ │ │ │ }, │ │ │ │ - handleSingle: function(evt) { │ │ │ │ - if (this.passesTolerance(evt)) { │ │ │ │ - if (this.timerId != null) { │ │ │ │ - if (this.last.touches && this.last.touches.length === 1) { │ │ │ │ - if (this["double"]) { │ │ │ │ - OpenLayers.Event.preventDefault(evt) │ │ │ │ - } │ │ │ │ - this.handleDouble(evt) │ │ │ │ - } │ │ │ │ - if (!this.last.touches || this.last.touches.length !== 2) { │ │ │ │ - this.clearTimer() │ │ │ │ + deactivate: function() { │ │ │ │ + if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ + if (this.dragHandler.deactivate()) { │ │ │ │ + if (this.zoomBox) { │ │ │ │ + this.removeBox() │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - this.first = this.getEventInfo(evt); │ │ │ │ - var clickEvent = this.single ? OpenLayers.Util.extend({}, evt) : null; │ │ │ │ - this.queuePotentialClick(clickEvent) │ │ │ │ } │ │ │ │ + return true │ │ │ │ + } else { │ │ │ │ + return false │ │ │ │ } │ │ │ │ }, │ │ │ │ - queuePotentialClick: function(evt) { │ │ │ │ - this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay) │ │ │ │ - }, │ │ │ │ - passesTolerance: function(evt) { │ │ │ │ - var passes = true; │ │ │ │ - if (this.pixelTolerance != null && this.down && this.down.xy) { │ │ │ │ - passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy); │ │ │ │ - if (passes && this.touch && this.down.touches.length === this.last.touches.length) { │ │ │ │ - for (var i = 0, ii = this.down.touches.length; i < ii; ++i) { │ │ │ │ - if (this.getTouchDistance(this.down.touches[i], this.last.touches[i]) > this.pixelTolerance) { │ │ │ │ - passes = false; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ + getBoxOffsets: function() { │ │ │ │ + if (!this.boxOffsets) { │ │ │ │ + var testDiv = document.createElement("div"); │ │ │ │ + testDiv.style.position = "absolute"; │ │ │ │ + testDiv.style.border = "1px solid black"; │ │ │ │ + testDiv.style.width = "3px"; │ │ │ │ + document.body.appendChild(testDiv); │ │ │ │ + var w3cBoxModel = testDiv.clientWidth == 3; │ │ │ │ + document.body.removeChild(testDiv); │ │ │ │ + var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-left-width")); │ │ │ │ + var right = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-right-width")); │ │ │ │ + var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-top-width")); │ │ │ │ + var bottom = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-bottom-width")); │ │ │ │ + this.boxOffsets = { │ │ │ │ + left: left, │ │ │ │ + right: right, │ │ │ │ + top: top, │ │ │ │ + bottom: bottom, │ │ │ │ + width: w3cBoxModel === false ? left + right : 0, │ │ │ │ + height: w3cBoxModel === false ? top + bottom : 0 │ │ │ │ } │ │ │ │ } │ │ │ │ - return passes │ │ │ │ + return this.boxOffsets │ │ │ │ }, │ │ │ │ - getTouchDistance: function(from, to) { │ │ │ │ - return Math.sqrt(Math.pow(from.clientX - to.clientX, 2) + Math.pow(from.clientY - to.clientY, 2)) │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Box" │ │ │ │ +}); │ │ │ │ +OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ + delay: 500, │ │ │ │ + pixelTolerance: null, │ │ │ │ + stopMove: false, │ │ │ │ + px: null, │ │ │ │ + timerId: null, │ │ │ │ + mousemove: function(evt) { │ │ │ │ + if (this.passesTolerance(evt.xy)) { │ │ │ │ + this.clearTimer(); │ │ │ │ + this.callback("move", [evt]); │ │ │ │ + this.px = evt.xy; │ │ │ │ + evt = OpenLayers.Util.extend({}, evt); │ │ │ │ + this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay) │ │ │ │ + } │ │ │ │ + return !this.stopMove │ │ │ │ }, │ │ │ │ - passesDblclickTolerance: function(evt) { │ │ │ │ + mouseout: function(evt) { │ │ │ │ + if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) { │ │ │ │ + this.clearTimer(); │ │ │ │ + this.callback("move", [evt]) │ │ │ │ + } │ │ │ │ + return true │ │ │ │ + }, │ │ │ │ + passesTolerance: function(px) { │ │ │ │ var passes = true; │ │ │ │ - if (this.down && this.first) { │ │ │ │ - passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance │ │ │ │ + if (this.pixelTolerance && this.px) { │ │ │ │ + var dpx = Math.sqrt(Math.pow(this.px.x - px.x, 2) + Math.pow(this.px.y - px.y, 2)); │ │ │ │ + if (dpx < this.pixelTolerance) { │ │ │ │ + passes = false │ │ │ │ + } │ │ │ │ } │ │ │ │ return passes │ │ │ │ }, │ │ │ │ clearTimer: function() { │ │ │ │ if (this.timerId != null) { │ │ │ │ window.clearTimeout(this.timerId); │ │ │ │ this.timerId = null │ │ │ │ } │ │ │ │ - if (this.rightclickTimerId != null) { │ │ │ │ - window.clearTimeout(this.rightclickTimerId); │ │ │ │ - this.rightclickTimerId = null │ │ │ │ - } │ │ │ │ }, │ │ │ │ delayedCall: function(evt) { │ │ │ │ - this.timerId = null; │ │ │ │ - if (evt) { │ │ │ │ - this.callback("click", [evt]) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - getEventInfo: function(evt) { │ │ │ │ - var touches; │ │ │ │ - if (evt.touches) { │ │ │ │ - var len = evt.touches.length; │ │ │ │ - touches = new Array(len); │ │ │ │ - var touch; │ │ │ │ - for (var i = 0; i < len; i++) { │ │ │ │ - touch = evt.touches[i]; │ │ │ │ - touches[i] = { │ │ │ │ - clientX: touch.olClientX, │ │ │ │ - clientY: touch.olClientY │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return { │ │ │ │ - xy: evt.xy, │ │ │ │ - touches: touches │ │ │ │ - } │ │ │ │ + this.callback("pause", [evt]) │ │ │ │ }, │ │ │ │ deactivate: function() { │ │ │ │ var deactivated = false; │ │ │ │ if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ this.clearTimer(); │ │ │ │ - this.down = null; │ │ │ │ - this.first = null; │ │ │ │ - this.last = null; │ │ │ │ deactivated = true │ │ │ │ } │ │ │ │ return deactivated │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Click" │ │ │ │ + CLASS_NAME: "OpenLayers.Handler.Hover" │ │ │ │ }); │ │ │ │ -OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ - profile: null, │ │ │ │ - defaultVersion: "1.0.0", │ │ │ │ - stringifyOutput: true, │ │ │ │ - namedLayersAsArray: false, │ │ │ │ - CLASS_NAME: "OpenLayers.Format.SLD" │ │ │ │ +OpenLayers.Protocol.WFS = function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.WFS.DEFAULTS); │ │ │ │ + var cls = OpenLayers.Protocol.WFS["v" + options.version.replace(/\./g, "_")]; │ │ │ │ + if (!cls) { │ │ │ │ + throw "Unsupported WFS version: " + options.version │ │ │ │ + } │ │ │ │ + return new cls(options) │ │ │ │ +}; │ │ │ │ +OpenLayers.Protocol.WFS.fromWMSLayer = function(layer, options) { │ │ │ │ + var typeName, featurePrefix; │ │ │ │ + var param = layer.params["LAYERS"]; │ │ │ │ + var parts = (OpenLayers.Util.isArray(param) ? param[0] : param).split(":"); │ │ │ │ + if (parts.length > 1) { │ │ │ │ + featurePrefix = parts[0] │ │ │ │ + } │ │ │ │ + typeName = parts.pop(); │ │ │ │ + var protocolOptions = { │ │ │ │ + url: layer.url, │ │ │ │ + featureType: typeName, │ │ │ │ + featurePrefix: featurePrefix, │ │ │ │ + srsName: layer.projection && layer.projection.getCode() || layer.map && layer.map.getProjectionObject().getCode(), │ │ │ │ + version: "1.1.0" │ │ │ │ + }; │ │ │ │ + return new OpenLayers.Protocol.WFS(OpenLayers.Util.applyDefaults(options, protocolOptions)) │ │ │ │ +}; │ │ │ │ +OpenLayers.Protocol.WFS.DEFAULTS = { │ │ │ │ + version: "1.0.0" │ │ │ │ +}; │ │ │ │ +OpenLayers.Protocol.CSW = function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.CSW.DEFAULTS); │ │ │ │ + var cls = OpenLayers.Protocol.CSW["v" + options.version.replace(/\./g, "_")]; │ │ │ │ + if (!cls) { │ │ │ │ + throw "Unsupported CSW version: " + options.version │ │ │ │ + } │ │ │ │ + return new cls(options) │ │ │ │ +}; │ │ │ │ +OpenLayers.Protocol.CSW.DEFAULTS = { │ │ │ │ + version: "2.0.2" │ │ │ │ +}; │ │ │ │ +OpenLayers.Protocol.HTTP = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ + url: null, │ │ │ │ + headers: null, │ │ │ │ + params: null, │ │ │ │ + callback: null, │ │ │ │ + scope: null, │ │ │ │ + readWithPOST: false, │ │ │ │ + updateWithPOST: false, │ │ │ │ + deleteWithPOST: false, │ │ │ │ + wildcarded: false, │ │ │ │ + srsInBBOX: false, │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + this.params = {}; │ │ │ │ + this.headers = {}; │ │ │ │ + OpenLayers.Protocol.prototype.initialize.apply(this, arguments); │ │ │ │ + if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { │ │ │ │ + var format = new OpenLayers.Format.QueryStringFilter({ │ │ │ │ + wildcarded: this.wildcarded, │ │ │ │ + srsInBBOX: this.srsInBBOX │ │ │ │ + }); │ │ │ │ + this.filterToParams = function(filter, params) { │ │ │ │ + return format.write(filter, params) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.params = null; │ │ │ │ + this.headers = null; │ │ │ │ + OpenLayers.Protocol.prototype.destroy.apply(this) │ │ │ │ + }, │ │ │ │ + read: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ + options = options || {}; │ │ │ │ + options.params = OpenLayers.Util.applyDefaults(options.params, this.options.params); │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + if (options.filter && this.filterToParams) { │ │ │ │ + options.params = this.filterToParams(options.filter, options.params) │ │ │ │ + } │ │ │ │ + var readWithPOST = options.readWithPOST !== undefined ? options.readWithPOST : this.readWithPOST; │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "read" │ │ │ │ + }); │ │ │ │ + if (readWithPOST) { │ │ │ │ + var headers = options.headers || {}; │ │ │ │ + headers["Content-Type"] = "application/x-www-form-urlencoded"; │ │ │ │ + resp.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ + data: OpenLayers.Util.getParameterString(options.params), │ │ │ │ + headers: headers │ │ │ │ + }) │ │ │ │ + } else { │ │ │ │ + resp.priv = OpenLayers.Request.GET({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, resp, options), │ │ │ │ + params: options.params, │ │ │ │ + headers: options.headers │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + return resp │ │ │ │ + }, │ │ │ │ + handleRead: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options) │ │ │ │ + }, │ │ │ │ + create: function(features, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: features, │ │ │ │ + requestType: "create" │ │ │ │ + }); │ │ │ │ + resp.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleCreate, resp, options), │ │ │ │ + headers: options.headers, │ │ │ │ + data: this.format.write(features) │ │ │ │ + }); │ │ │ │ + return resp │ │ │ │ + }, │ │ │ │ + handleCreate: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options) │ │ │ │ + }, │ │ │ │ + update: function(feature, options) { │ │ │ │ + options = options || {}; │ │ │ │ + var url = options.url || feature.url || this.options.url + "/" + feature.fid; │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: feature, │ │ │ │ + requestType: "update" │ │ │ │ + }); │ │ │ │ + var method = this.updateWithPOST ? "POST" : "PUT"; │ │ │ │ + resp.priv = OpenLayers.Request[method]({ │ │ │ │ + url: url, │ │ │ │ + callback: this.createCallback(this.handleUpdate, resp, options), │ │ │ │ + headers: options.headers, │ │ │ │ + data: this.format.write(feature) │ │ │ │ + }); │ │ │ │ + return resp │ │ │ │ + }, │ │ │ │ + handleUpdate: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options) │ │ │ │ + }, │ │ │ │ + delete: function(feature, options) { │ │ │ │ + options = options || {}; │ │ │ │ + var url = options.url || feature.url || this.options.url + "/" + feature.fid; │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var resp = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: feature, │ │ │ │ + requestType: "delete" │ │ │ │ + }); │ │ │ │ + var method = this.deleteWithPOST ? "POST" : "DELETE"; │ │ │ │ + var requestOptions = { │ │ │ │ + url: url, │ │ │ │ + callback: this.createCallback(this.handleDelete, resp, options), │ │ │ │ + headers: options.headers │ │ │ │ + }; │ │ │ │ + if (this.deleteWithPOST) { │ │ │ │ + requestOptions.data = this.format.write(feature) │ │ │ │ + } │ │ │ │ + resp.priv = OpenLayers.Request[method](requestOptions); │ │ │ │ + return resp │ │ │ │ + }, │ │ │ │ + handleDelete: function(resp, options) { │ │ │ │ + this.handleResponse(resp, options) │ │ │ │ + }, │ │ │ │ + handleResponse: function(resp, options) { │ │ │ │ + var request = resp.priv; │ │ │ │ + if (options.callback) { │ │ │ │ + if (request.status >= 200 && request.status < 300) { │ │ │ │ + if (resp.requestType != "delete") { │ │ │ │ + resp.features = this.parseFeatures(request) │ │ │ │ + } │ │ │ │ + resp.code = OpenLayers.Protocol.Response.SUCCESS │ │ │ │ + } else { │ │ │ │ + resp.code = OpenLayers.Protocol.Response.FAILURE │ │ │ │ + } │ │ │ │ + options.callback.call(options.scope, resp) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + parseFeatures: function(request) { │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText │ │ │ │ + } │ │ │ │ + if (!doc || doc.length <= 0) { │ │ │ │ + return null │ │ │ │ + } │ │ │ │ + return this.format.read(doc) │ │ │ │ + }, │ │ │ │ + commit: function(features, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var resp = [], │ │ │ │ + nResponses = 0; │ │ │ │ + var types = {}; │ │ │ │ + types[OpenLayers.State.INSERT] = []; │ │ │ │ + types[OpenLayers.State.UPDATE] = []; │ │ │ │ + types[OpenLayers.State.DELETE] = []; │ │ │ │ + var feature, list, requestFeatures = []; │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + feature = features[i]; │ │ │ │ + list = types[feature.state]; │ │ │ │ + if (list) { │ │ │ │ + list.push(feature); │ │ │ │ + requestFeatures.push(feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) + types[OpenLayers.State.UPDATE].length + types[OpenLayers.State.DELETE].length; │ │ │ │ + var success = true; │ │ │ │ + var finalResponse = new OpenLayers.Protocol.Response({ │ │ │ │ + reqFeatures: requestFeatures │ │ │ │ + }); │ │ │ │ + │ │ │ │ + function insertCallback(response) { │ │ │ │ + var len = response.features ? response.features.length : 0; │ │ │ │ + var fids = new Array(len); │ │ │ │ + for (var i = 0; i < len; ++i) { │ │ │ │ + fids[i] = response.features[i].fid │ │ │ │ + } │ │ │ │ + finalResponse.insertIds = fids; │ │ │ │ + callback.apply(this, [response]) │ │ │ │ + } │ │ │ │ + │ │ │ │ + function callback(response) { │ │ │ │ + this.callUserCallback(response, options); │ │ │ │ + success = success && response.success(); │ │ │ │ + nResponses++; │ │ │ │ + if (nResponses >= nRequests) { │ │ │ │ + if (options.callback) { │ │ │ │ + finalResponse.code = success ? OpenLayers.Protocol.Response.SUCCESS : OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + options.callback.apply(options.scope, [finalResponse]) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var queue = types[OpenLayers.State.INSERT]; │ │ │ │ + if (queue.length > 0) { │ │ │ │ + resp.push(this.create(queue, OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: insertCallback, │ │ │ │ + scope: this │ │ │ │ + }, options.create))) │ │ │ │ + } │ │ │ │ + queue = types[OpenLayers.State.UPDATE]; │ │ │ │ + for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ + resp.push(this.update(queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: callback, │ │ │ │ + scope: this │ │ │ │ + }, options.update))) │ │ │ │ + } │ │ │ │ + queue = types[OpenLayers.State.DELETE]; │ │ │ │ + for (var i = queue.length - 1; i >= 0; --i) { │ │ │ │ + resp.push(this["delete"](queue[i], OpenLayers.Util.applyDefaults({ │ │ │ │ + callback: callback, │ │ │ │ + scope: this │ │ │ │ + }, options["delete"]))) │ │ │ │ + } │ │ │ │ + return resp │ │ │ │ + }, │ │ │ │ + abort: function(response) { │ │ │ │ + if (response) { │ │ │ │ + response.priv.abort() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + callUserCallback: function(resp, options) { │ │ │ │ + var opt = options[resp.requestType]; │ │ │ │ + if (opt && opt.callback) { │ │ │ │ + opt.callback.call(opt.scope, resp) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.HTTP" │ │ │ │ +}); │ │ │ │ +OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ + url: null, │ │ │ │ + params: null, │ │ │ │ + callback: null, │ │ │ │ + callbackTemplate: "OpenLayers.Protocol.Script.registry.${id}", │ │ │ │ + callbackKey: "callback", │ │ │ │ + callbackPrefix: "", │ │ │ │ + scope: null, │ │ │ │ + format: null, │ │ │ │ + pendingRequests: null, │ │ │ │ + srsInBBOX: false, │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + this.params = {}; │ │ │ │ + this.pendingRequests = {}; │ │ │ │ + OpenLayers.Protocol.prototype.initialize.apply(this, arguments); │ │ │ │ + if (!this.format) { │ │ │ │ + this.format = new OpenLayers.Format.GeoJSON │ │ │ │ + } │ │ │ │ + if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) { │ │ │ │ + var format = new OpenLayers.Format.QueryStringFilter({ │ │ │ │ + srsInBBOX: this.srsInBBOX │ │ │ │ + }); │ │ │ │ + this.filterToParams = function(filter, params) { │ │ │ │ + return format.write(filter, params) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + read: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + options.params = OpenLayers.Util.applyDefaults(options.params, this.options.params); │ │ │ │ + if (options.filter && this.filterToParams) { │ │ │ │ + options.params = this.filterToParams(options.filter, options.params) │ │ │ │ + } │ │ │ │ + var response = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "read" │ │ │ │ + }); │ │ │ │ + var request = this.createRequest(options.url, options.params, OpenLayers.Function.bind(function(data) { │ │ │ │ + response.data = data; │ │ │ │ + this.handleRead(response, options) │ │ │ │ + }, this)); │ │ │ │ + response.priv = request; │ │ │ │ + return response │ │ │ │ + }, │ │ │ │ + createRequest: function(url, params, callback) { │ │ │ │ + var id = OpenLayers.Protocol.Script.register(callback); │ │ │ │ + var name = OpenLayers.String.format(this.callbackTemplate, { │ │ │ │ + id: id │ │ │ │ + }); │ │ │ │ + params = OpenLayers.Util.extend({}, params); │ │ │ │ + params[this.callbackKey] = this.callbackPrefix + name; │ │ │ │ + url = OpenLayers.Util.urlAppend(url, OpenLayers.Util.getParameterString(params)); │ │ │ │ + var script = document.createElement("script"); │ │ │ │ + script.type = "text/javascript"; │ │ │ │ + script.src = url; │ │ │ │ + script.id = "OpenLayers_Protocol_Script_" + id; │ │ │ │ + this.pendingRequests[script.id] = script; │ │ │ │ + var head = document.getElementsByTagName("head")[0]; │ │ │ │ + head.appendChild(script); │ │ │ │ + return script │ │ │ │ + }, │ │ │ │ + destroyRequest: function(script) { │ │ │ │ + OpenLayers.Protocol.Script.unregister(script.id.split("_").pop()); │ │ │ │ + delete this.pendingRequests[script.id]; │ │ │ │ + if (script.parentNode) { │ │ │ │ + script.parentNode.removeChild(script) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + handleRead: function(response, options) { │ │ │ │ + this.handleResponse(response, options) │ │ │ │ + }, │ │ │ │ + handleResponse: function(response, options) { │ │ │ │ + if (options.callback) { │ │ │ │ + if (response.data) { │ │ │ │ + response.features = this.parseFeatures(response.data); │ │ │ │ + response.code = OpenLayers.Protocol.Response.SUCCESS │ │ │ │ + } else { │ │ │ │ + response.code = OpenLayers.Protocol.Response.FAILURE │ │ │ │ + } │ │ │ │ + this.destroyRequest(response.priv); │ │ │ │ + options.callback.call(options.scope, response) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + parseFeatures: function(data) { │ │ │ │ + return this.format.read(data) │ │ │ │ + }, │ │ │ │ + abort: function(response) { │ │ │ │ + if (response) { │ │ │ │ + this.destroyRequest(response.priv) │ │ │ │ + } else { │ │ │ │ + for (var key in this.pendingRequests) { │ │ │ │ + this.destroyRequest(this.pendingRequests[key]) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.abort(); │ │ │ │ + delete this.params; │ │ │ │ + delete this.format; │ │ │ │ + OpenLayers.Protocol.prototype.destroy.apply(this) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.Script" │ │ │ │ +}); │ │ │ │ +(function() { │ │ │ │ + var o = OpenLayers.Protocol.Script; │ │ │ │ + var counter = 0; │ │ │ │ + o.registry = {}; │ │ │ │ + o.register = function(callback) { │ │ │ │ + var id = "c" + ++counter; │ │ │ │ + o.registry[id] = function() { │ │ │ │ + callback.apply(this, arguments) │ │ │ │ + }; │ │ │ │ + return id │ │ │ │ + }; │ │ │ │ + o.unregister = function(id) { │ │ │ │ + delete o.registry[id] │ │ │ │ + } │ │ │ │ +})(); │ │ │ │ +OpenLayers.Protocol.SOS = function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, OpenLayers.Protocol.SOS.DEFAULTS); │ │ │ │ + var cls = OpenLayers.Protocol.SOS["v" + options.version.replace(/\./g, "_")]; │ │ │ │ + if (!cls) { │ │ │ │ + throw "Unsupported SOS version: " + options.version │ │ │ │ + } │ │ │ │ + return new cls(options) │ │ │ │ +}; │ │ │ │ +OpenLayers.Protocol.SOS.DEFAULTS = { │ │ │ │ + version: "1.0.0" │ │ │ │ +}; │ │ │ │ +OpenLayers.Protocol.WFS.v1 = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ + version: null, │ │ │ │ + srsName: "EPSG:4326", │ │ │ │ + featureType: null, │ │ │ │ + featureNS: null, │ │ │ │ + geometryName: "the_geom", │ │ │ │ + schema: null, │ │ │ │ + featurePrefix: "feature", │ │ │ │ + formatOptions: null, │ │ │ │ + readFormat: null, │ │ │ │ + readOptions: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.initialize.apply(this, [options]); │ │ │ │ + if (!options.format) { │ │ │ │ + this.format = OpenLayers.Format.WFST(OpenLayers.Util.extend({ │ │ │ │ + version: this.version, │ │ │ │ + featureType: this.featureType, │ │ │ │ + featureNS: this.featureNS, │ │ │ │ + featurePrefix: this.featurePrefix, │ │ │ │ + geometryName: this.geometryName, │ │ │ │ + srsName: this.srsName, │ │ │ │ + schema: this.schema │ │ │ │ + }, this.formatOptions)) │ │ │ │ + } │ │ │ │ + if (!options.geometryName && parseFloat(this.format.version) > 1) { │ │ │ │ + this.setGeometryName(null) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + if (this.options && !this.options.format) { │ │ │ │ + this.format.destroy() │ │ │ │ + } │ │ │ │ + this.format = null; │ │ │ │ + OpenLayers.Protocol.prototype.destroy.apply(this) │ │ │ │ + }, │ │ │ │ + read: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.read.apply(this, arguments); │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.options || {}); │ │ │ │ + var response = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "read" │ │ │ │ + }); │ │ │ │ + var data = OpenLayers.Format.XML.prototype.write.apply(this.format, [this.format.writeNode("wfs:GetFeature", options)]); │ │ │ │ + response.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, response, options), │ │ │ │ + params: options.params, │ │ │ │ + headers: options.headers, │ │ │ │ + data: data │ │ │ │ + }); │ │ │ │ + return response │ │ │ │ + }, │ │ │ │ + setFeatureType: function(featureType) { │ │ │ │ + this.featureType = featureType; │ │ │ │ + this.format.featureType = featureType │ │ │ │ + }, │ │ │ │ + setGeometryName: function(geometryName) { │ │ │ │ + this.geometryName = geometryName; │ │ │ │ + this.format.geometryName = geometryName │ │ │ │ + }, │ │ │ │ + handleRead: function(response, options) { │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + if (options.callback) { │ │ │ │ + var request = response.priv; │ │ │ │ + if (request.status >= 200 && request.status < 300) { │ │ │ │ + var result = this.parseResponse(request, options.readOptions); │ │ │ │ + if (result && result.success !== false) { │ │ │ │ + if (options.readOptions && options.readOptions.output == "object") { │ │ │ │ + OpenLayers.Util.extend(response, result) │ │ │ │ + } else { │ │ │ │ + response.features = result │ │ │ │ + } │ │ │ │ + response.code = OpenLayers.Protocol.Response.SUCCESS │ │ │ │ + } else { │ │ │ │ + response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + response.error = result │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + response.code = OpenLayers.Protocol.Response.FAILURE │ │ │ │ + } │ │ │ │ + options.callback.call(options.scope, response) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + parseResponse: function(request, options) { │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText │ │ │ │ + } │ │ │ │ + if (!doc || doc.length <= 0) { │ │ │ │ + return null │ │ │ │ + } │ │ │ │ + var result = this.readFormat !== null ? this.readFormat.read(doc) : this.format.read(doc, options); │ │ │ │ + if (!this.featureNS) { │ │ │ │ + var format = this.readFormat || this.format; │ │ │ │ + this.featureNS = format.featureNS; │ │ │ │ + format.autoConfig = false; │ │ │ │ + if (!this.geometryName) { │ │ │ │ + this.setGeometryName(format.geometryName) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return result │ │ │ │ + }, │ │ │ │ + commit: function(features, options) { │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var response = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "commit", │ │ │ │ + reqFeatures: features │ │ │ │ + }); │ │ │ │ + response.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + headers: options.headers, │ │ │ │ + data: this.format.write(features, options), │ │ │ │ + callback: this.createCallback(this.handleCommit, response, options) │ │ │ │ + }); │ │ │ │ + return response │ │ │ │ + }, │ │ │ │ + handleCommit: function(response, options) { │ │ │ │ + if (options.callback) { │ │ │ │ + var request = response.priv; │ │ │ │ + var data = request.responseXML; │ │ │ │ + if (!data || !data.documentElement) { │ │ │ │ + data = request.responseText │ │ │ │ + } │ │ │ │ + var obj = this.format.read(data) || {}; │ │ │ │ + response.insertIds = obj.insertIds || []; │ │ │ │ + if (obj.success) { │ │ │ │ + response.code = OpenLayers.Protocol.Response.SUCCESS │ │ │ │ + } else { │ │ │ │ + response.code = OpenLayers.Protocol.Response.FAILURE; │ │ │ │ + response.error = obj │ │ │ │ + } │ │ │ │ + options.callback.call(options.scope, response) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + filterDelete: function(filter, options) { │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var response = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "commit" │ │ │ │ + }); │ │ │ │ + var root = this.format.createElementNSPlus("wfs:Transaction", { │ │ │ │ + attributes: { │ │ │ │ + service: "WFS", │ │ │ │ + version: this.version │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + var deleteNode = this.format.createElementNSPlus("wfs:Delete", { │ │ │ │ + attributes: { │ │ │ │ + typeName: (options.featureNS ? this.featurePrefix + ":" : "") + options.featureType │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + if (options.featureNS) { │ │ │ │ + deleteNode.setAttribute("xmlns:" + this.featurePrefix, options.featureNS) │ │ │ │ + } │ │ │ │ + var filterNode = this.format.writeNode("ogc:Filter", filter); │ │ │ │ + deleteNode.appendChild(filterNode); │ │ │ │ + root.appendChild(deleteNode); │ │ │ │ + var data = OpenLayers.Format.XML.prototype.write.apply(this.format, [root]); │ │ │ │ + return OpenLayers.Request.POST({ │ │ │ │ + url: this.url, │ │ │ │ + callback: options.callback || function() {}, │ │ │ │ + data: data │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + abort: function(response) { │ │ │ │ + if (response) { │ │ │ │ + response.priv.abort() │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.WFS.v1" │ │ │ │ }); │ │ │ │ OpenLayers.Format.GML.v2 = OpenLayers.Class(OpenLayers.Format.GML.Base, { │ │ │ │ schemaLocation: "http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd", │ │ │ │ initialize: function(options) { │ │ │ │ OpenLayers.Format.GML.Base.prototype.initialize.apply(this, [options]) │ │ │ │ }, │ │ │ │ readers: { │ │ │ │ @@ -20735,17415 +19886,18264 @@ │ │ │ │ } │ │ │ │ node.appendChild(child) │ │ │ │ } │ │ │ │ return node │ │ │ │ }, │ │ │ │ CLASS_NAME: "OpenLayers.Format.Filter.v1_0_0" │ │ │ │ }); │ │ │ │ -OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, { │ │ │ │ - namespaces: { │ │ │ │ - sld: "http://www.opengis.net/sld", │ │ │ │ - ogc: "http://www.opengis.net/ogc", │ │ │ │ - gml: "http://www.opengis.net/gml", │ │ │ │ - xlink: "http://www.w3.org/1999/xlink", │ │ │ │ - xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ +OpenLayers.Format.WFST.v1_0_0 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, OpenLayers.Format.WFST.v1, { │ │ │ │ + version: "1.0.0", │ │ │ │ + srsNameInQuery: false, │ │ │ │ + schemaLocations: { │ │ │ │ + wfs: "http://schemas.opengis.net/wfs/1.0.0/WFS-transaction.xsd" │ │ │ │ }, │ │ │ │ - defaultPrefix: "sld", │ │ │ │ - schemaLocation: null, │ │ │ │ - multipleSymbolizers: false, │ │ │ │ - featureTypeCounter: null, │ │ │ │ - defaultSymbolizer: { │ │ │ │ - fillColor: "#808080", │ │ │ │ - fillOpacity: 1, │ │ │ │ - strokeColor: "#000000", │ │ │ │ - strokeOpacity: 1, │ │ │ │ - strokeWidth: 1, │ │ │ │ - strokeDashstyle: "solid", │ │ │ │ - pointRadius: 3, │ │ │ │ - graphicName: "square" │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.Filter.v1_0_0.prototype.initialize.apply(this, [options]); │ │ │ │ + OpenLayers.Format.WFST.v1.prototype.initialize.apply(this, [options]) │ │ │ │ }, │ │ │ │ - read: function(data, options) { │ │ │ │ - options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ - var sld = { │ │ │ │ - namedLayers: options.namedLayersAsArray === true ? [] : {} │ │ │ │ - }; │ │ │ │ - this.readChildNodes(data, sld); │ │ │ │ - return sld │ │ │ │ + readNode: function(node, obj, first) { │ │ │ │ + return OpenLayers.Format.GML.v2.prototype.readNode.apply(this, arguments) │ │ │ │ }, │ │ │ │ - readers: OpenLayers.Util.applyDefaults({ │ │ │ │ - sld: { │ │ │ │ - StyledLayerDescriptor: function(node, sld) { │ │ │ │ - sld.version = node.getAttribute("version"); │ │ │ │ - this.readChildNodes(node, sld) │ │ │ │ - }, │ │ │ │ - Name: function(node, obj) { │ │ │ │ - obj.name = this.getChildValue(node) │ │ │ │ - }, │ │ │ │ - Title: function(node, obj) { │ │ │ │ - obj.title = this.getChildValue(node) │ │ │ │ - }, │ │ │ │ - Abstract: function(node, obj) { │ │ │ │ - obj.description = this.getChildValue(node) │ │ │ │ - }, │ │ │ │ - NamedLayer: function(node, sld) { │ │ │ │ - var layer = { │ │ │ │ - userStyles: [], │ │ │ │ - namedStyles: [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, layer); │ │ │ │ - for (var i = 0, len = layer.userStyles.length; i < len; ++i) { │ │ │ │ - layer.userStyles[i].layerName = layer.name │ │ │ │ - } │ │ │ │ - if (OpenLayers.Util.isArray(sld.namedLayers)) { │ │ │ │ - sld.namedLayers.push(layer) │ │ │ │ - } else { │ │ │ │ - sld.namedLayers[layer.name] = layer │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - NamedStyle: function(node, layer) { │ │ │ │ - layer.namedStyles.push(this.getChildName(node.firstChild)) │ │ │ │ + readers: { │ │ │ │ + wfs: OpenLayers.Util.applyDefaults({ │ │ │ │ + WFS_TransactionResponse: function(node, obj) { │ │ │ │ + obj.insertIds = []; │ │ │ │ + obj.success = false; │ │ │ │ + this.readChildNodes(node, obj) │ │ │ │ }, │ │ │ │ - UserStyle: function(node, layer) { │ │ │ │ + InsertResult: function(node, container) { │ │ │ │ var obj = { │ │ │ │ - defaultsPerSymbolizer: true, │ │ │ │ - rules: [] │ │ │ │ + fids: [] │ │ │ │ }; │ │ │ │ - this.featureTypeCounter = -1; │ │ │ │ this.readChildNodes(node, obj); │ │ │ │ - var style; │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - delete obj.defaultsPerSymbolizer; │ │ │ │ - style = new OpenLayers.Style2(obj) │ │ │ │ - } else { │ │ │ │ - style = new OpenLayers.Style(this.defaultSymbolizer, obj) │ │ │ │ - } │ │ │ │ - layer.userStyles.push(style) │ │ │ │ + container.insertIds = container.insertIds.concat(obj.fids) │ │ │ │ }, │ │ │ │ - IsDefault: function(node, style) { │ │ │ │ - if (this.getChildValue(node) == "1") { │ │ │ │ - style.isDefault = true │ │ │ │ - } │ │ │ │ + TransactionResult: function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj) │ │ │ │ }, │ │ │ │ - FeatureTypeStyle: function(node, style) { │ │ │ │ - ++this.featureTypeCounter; │ │ │ │ - var obj = { │ │ │ │ - rules: this.multipleSymbolizers ? style.rules : [] │ │ │ │ - }; │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - if (!this.multipleSymbolizers) { │ │ │ │ - style.rules = obj.rules │ │ │ │ - } │ │ │ │ + Status: function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj) │ │ │ │ }, │ │ │ │ - Rule: function(node, obj) { │ │ │ │ - var config; │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - config = { │ │ │ │ - symbolizers: [] │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var rule = new OpenLayers.Rule(config); │ │ │ │ - this.readChildNodes(node, rule); │ │ │ │ - obj.rules.push(rule) │ │ │ │ - }, │ │ │ │ - ElseFilter: function(node, rule) { │ │ │ │ - rule.elseFilter = true │ │ │ │ - }, │ │ │ │ - MinScaleDenominator: function(node, rule) { │ │ │ │ - rule.minScaleDenominator = parseFloat(this.getChildValue(node)) │ │ │ │ - }, │ │ │ │ - MaxScaleDenominator: function(node, rule) { │ │ │ │ - rule.maxScaleDenominator = parseFloat(this.getChildValue(node)) │ │ │ │ - }, │ │ │ │ - TextSymbolizer: function(node, rule) { │ │ │ │ - var config = {}; │ │ │ │ - this.readChildNodes(node, config); │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - config.zIndex = this.featureTypeCounter; │ │ │ │ - rule.symbolizers.push(new OpenLayers.Symbolizer.Text(config)) │ │ │ │ - } else { │ │ │ │ - rule.symbolizer["Text"] = OpenLayers.Util.applyDefaults(config, rule.symbolizer["Text"]) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - LabelPlacement: function(node, symbolizer) { │ │ │ │ - this.readChildNodes(node, symbolizer) │ │ │ │ - }, │ │ │ │ - PointPlacement: function(node, symbolizer) { │ │ │ │ - var config = {}; │ │ │ │ - this.readChildNodes(node, config); │ │ │ │ - config.labelRotation = config.rotation; │ │ │ │ - delete config.rotation; │ │ │ │ - var labelAlign, x = symbolizer.labelAnchorPointX, │ │ │ │ - y = symbolizer.labelAnchorPointY; │ │ │ │ - if (x <= 1 / 3) { │ │ │ │ - labelAlign = "l" │ │ │ │ - } else if (x > 1 / 3 && x < 2 / 3) { │ │ │ │ - labelAlign = "c" │ │ │ │ - } else if (x >= 2 / 3) { │ │ │ │ - labelAlign = "r" │ │ │ │ - } │ │ │ │ - if (y <= 1 / 3) { │ │ │ │ - labelAlign += "b" │ │ │ │ - } else if (y > 1 / 3 && y < 2 / 3) { │ │ │ │ - labelAlign += "m" │ │ │ │ - } else if (y >= 2 / 3) { │ │ │ │ - labelAlign += "t" │ │ │ │ - } │ │ │ │ - config.labelAlign = labelAlign; │ │ │ │ - OpenLayers.Util.applyDefaults(symbolizer, config) │ │ │ │ - }, │ │ │ │ - AnchorPoint: function(node, symbolizer) { │ │ │ │ - this.readChildNodes(node, symbolizer) │ │ │ │ - }, │ │ │ │ - AnchorPointX: function(node, symbolizer) { │ │ │ │ - var labelAnchorPointX = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (labelAnchorPointX) { │ │ │ │ - symbolizer.labelAnchorPointX = labelAnchorPointX │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - AnchorPointY: function(node, symbolizer) { │ │ │ │ - var labelAnchorPointY = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (labelAnchorPointY) { │ │ │ │ - symbolizer.labelAnchorPointY = labelAnchorPointY │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - Displacement: function(node, symbolizer) { │ │ │ │ - this.readChildNodes(node, symbolizer) │ │ │ │ - }, │ │ │ │ - DisplacementX: function(node, symbolizer) { │ │ │ │ - var labelXOffset = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (labelXOffset) { │ │ │ │ - symbolizer.labelXOffset = labelXOffset │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - DisplacementY: function(node, symbolizer) { │ │ │ │ - var labelYOffset = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (labelYOffset) { │ │ │ │ - symbolizer.labelYOffset = labelYOffset │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - LinePlacement: function(node, symbolizer) { │ │ │ │ - this.readChildNodes(node, symbolizer) │ │ │ │ - }, │ │ │ │ - PerpendicularOffset: function(node, symbolizer) { │ │ │ │ - var labelPerpendicularOffset = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (labelPerpendicularOffset) { │ │ │ │ - symbolizer.labelPerpendicularOffset = labelPerpendicularOffset │ │ │ │ + SUCCESS: function(node, obj) { │ │ │ │ + obj.success = true │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.WFST.v1.prototype.readers["wfs"]), │ │ │ │ + gml: OpenLayers.Format.GML.v2.prototype.readers["gml"], │ │ │ │ + feature: OpenLayers.Format.GML.v2.prototype.readers["feature"], │ │ │ │ + ogc: OpenLayers.Format.Filter.v1_0_0.prototype.readers["ogc"] │ │ │ │ + }, │ │ │ │ + writers: { │ │ │ │ + wfs: OpenLayers.Util.applyDefaults({ │ │ │ │ + Query: function(options) { │ │ │ │ + options = OpenLayers.Util.extend({ │ │ │ │ + featureNS: this.featureNS, │ │ │ │ + featurePrefix: this.featurePrefix, │ │ │ │ + featureType: this.featureType, │ │ │ │ + srsName: this.srsName, │ │ │ │ + srsNameInQuery: this.srsNameInQuery │ │ │ │ + }, options); │ │ │ │ + var prefix = options.featurePrefix; │ │ │ │ + var node = this.createElementNSPlus("wfs:Query", { │ │ │ │ + attributes: { │ │ │ │ + typeName: (prefix ? prefix + ":" : "") + options.featureType │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + if (options.srsNameInQuery && options.srsName) { │ │ │ │ + node.setAttribute("srsName", options.srsName) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - Label: function(node, symbolizer) { │ │ │ │ - var value = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (value) { │ │ │ │ - symbolizer.label = value │ │ │ │ + if (options.featureNS) { │ │ │ │ + node.setAttribute("xmlns:" + prefix, options.featureNS) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - Font: function(node, symbolizer) { │ │ │ │ - this.readChildNodes(node, symbolizer) │ │ │ │ - }, │ │ │ │ - Halo: function(node, symbolizer) { │ │ │ │ - var obj = {}; │ │ │ │ - this.readChildNodes(node, obj); │ │ │ │ - symbolizer.haloRadius = obj.haloRadius; │ │ │ │ - symbolizer.haloColor = obj.fillColor; │ │ │ │ - symbolizer.haloOpacity = obj.fillOpacity │ │ │ │ - }, │ │ │ │ - Radius: function(node, symbolizer) { │ │ │ │ - var radius = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (radius != null) { │ │ │ │ - symbolizer.haloRadius = radius │ │ │ │ + if (options.propertyNames) { │ │ │ │ + for (var i = 0, len = options.propertyNames.length; i < len; i++) { │ │ │ │ + this.writeNode("ogc:PropertyName", { │ │ │ │ + property: options.propertyNames[i] │ │ │ │ + }, node) │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - RasterSymbolizer: function(node, rule) { │ │ │ │ - var config = {}; │ │ │ │ - this.readChildNodes(node, config); │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - config.zIndex = this.featureTypeCounter; │ │ │ │ - rule.symbolizers.push(new OpenLayers.Symbolizer.Raster(config)) │ │ │ │ - } else { │ │ │ │ - rule.symbolizer["Raster"] = OpenLayers.Util.applyDefaults(config, rule.symbolizer["Raster"]) │ │ │ │ + if (options.filter) { │ │ │ │ + this.setFilterProperty(options.filter); │ │ │ │ + this.writeNode("ogc:Filter", options.filter, node) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - Geometry: function(node, obj) { │ │ │ │ - obj.geometry = {}; │ │ │ │ - this.readChildNodes(node, obj.geometry) │ │ │ │ - }, │ │ │ │ - ColorMap: function(node, symbolizer) { │ │ │ │ - symbolizer.colorMap = []; │ │ │ │ - this.readChildNodes(node, symbolizer.colorMap) │ │ │ │ - }, │ │ │ │ - ColorMapEntry: function(node, colorMap) { │ │ │ │ - var q = node.getAttribute("quantity"); │ │ │ │ - var o = node.getAttribute("opacity"); │ │ │ │ - colorMap.push({ │ │ │ │ - color: node.getAttribute("color"), │ │ │ │ - quantity: q !== null ? parseFloat(q) : undefined, │ │ │ │ - label: node.getAttribute("label") || undefined, │ │ │ │ - opacity: o !== null ? parseFloat(o) : undefined │ │ │ │ + return node │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.WFST.v1.prototype.writers["wfs"]), │ │ │ │ + gml: OpenLayers.Format.GML.v2.prototype.writers["gml"], │ │ │ │ + feature: OpenLayers.Format.GML.v2.prototype.writers["feature"], │ │ │ │ + ogc: OpenLayers.Format.Filter.v1_0_0.prototype.writers["ogc"] │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFST.v1_0_0" │ │ │ │ +}); │ │ │ │ +OpenLayers.Protocol.WFS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, { │ │ │ │ + version: "1.0.0", │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.WFS.v1_0_0" │ │ │ │ +}); │ │ │ │ +OpenLayers.Protocol.WFS.v1_1_0 = OpenLayers.Class(OpenLayers.Protocol.WFS.v1, { │ │ │ │ + version: "1.1.0", │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Protocol.WFS.v1.prototype.initialize.apply(this, arguments); │ │ │ │ + if (this.outputFormat && !this.readFormat) { │ │ │ │ + if (this.outputFormat.toLowerCase() == "gml2") { │ │ │ │ + this.readFormat = new OpenLayers.Format.GML.v2({ │ │ │ │ + featureType: this.featureType, │ │ │ │ + featureNS: this.featureNS, │ │ │ │ + geometryName: this.geometryName │ │ │ │ }) │ │ │ │ - }, │ │ │ │ - LineSymbolizer: function(node, rule) { │ │ │ │ - var config = {}; │ │ │ │ - this.readChildNodes(node, config); │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - config.zIndex = this.featureTypeCounter; │ │ │ │ - rule.symbolizers.push(new OpenLayers.Symbolizer.Line(config)) │ │ │ │ - } else { │ │ │ │ - rule.symbolizer["Line"] = OpenLayers.Util.applyDefaults(config, rule.symbolizer["Line"]) │ │ │ │ + } else if (this.outputFormat.toLowerCase() == "json") { │ │ │ │ + this.readFormat = new OpenLayers.Format.GeoJSON │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.WFS.v1_1_0" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.SOSGetFeatureOfInterest = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + VERSION: "1.0.0", │ │ │ │ + namespaces: { │ │ │ │ + sos: "http://www.opengis.net/sos/1.0", │ │ │ │ + gml: "http://www.opengis.net/gml", │ │ │ │ + sa: "http://www.opengis.net/sampling/1.0", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + }, │ │ │ │ + schemaLocation: "http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosAll.xsd", │ │ │ │ + defaultPrefix: "sos", │ │ │ │ + regExes: { │ │ │ │ + trimSpace: /^\s*|\s*$/g, │ │ │ │ + removeSpace: /\s*/g, │ │ │ │ + splitSpace: /\s+/, │ │ │ │ + trimComma: /\s*,\s*/g │ │ │ │ + }, │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]) │ │ │ │ + } │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement │ │ │ │ + } │ │ │ │ + var info = { │ │ │ │ + features: [] │ │ │ │ + }; │ │ │ │ + this.readNode(data, info); │ │ │ │ + var features = []; │ │ │ │ + for (var i = 0, len = info.features.length; i < len; i++) { │ │ │ │ + var container = info.features[i]; │ │ │ │ + if (this.internalProjection && this.externalProjection && container.components[0]) { │ │ │ │ + container.components[0].transform(this.externalProjection, this.internalProjection) │ │ │ │ + } │ │ │ │ + var feature = new OpenLayers.Feature.Vector(container.components[0], container.attributes); │ │ │ │ + features.push(feature) │ │ │ │ + } │ │ │ │ + return features │ │ │ │ + }, │ │ │ │ + readers: { │ │ │ │ + sa: { │ │ │ │ + SamplingPoint: function(node, obj) { │ │ │ │ + if (!obj.attributes) { │ │ │ │ + var feature = { │ │ │ │ + attributes: {} │ │ │ │ + }; │ │ │ │ + obj.features.push(feature); │ │ │ │ + obj = feature │ │ │ │ } │ │ │ │ + obj.attributes.id = this.getAttributeNS(node, this.namespaces.gml, "id"); │ │ │ │ + this.readChildNodes(node, obj) │ │ │ │ }, │ │ │ │ - PolygonSymbolizer: function(node, rule) { │ │ │ │ - var config = { │ │ │ │ - fill: false, │ │ │ │ - stroke: false │ │ │ │ - }; │ │ │ │ - if (!this.multipleSymbolizers) { │ │ │ │ - config = rule.symbolizer["Polygon"] || config │ │ │ │ - } │ │ │ │ - this.readChildNodes(node, config); │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - config.zIndex = this.featureTypeCounter; │ │ │ │ - rule.symbolizers.push(new OpenLayers.Symbolizer.Polygon(config)) │ │ │ │ - } else { │ │ │ │ - rule.symbolizer["Polygon"] = config │ │ │ │ - } │ │ │ │ + position: function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + gml: OpenLayers.Util.applyDefaults({ │ │ │ │ + FeatureCollection: function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj) │ │ │ │ }, │ │ │ │ - PointSymbolizer: function(node, rule) { │ │ │ │ - var config = { │ │ │ │ - fill: false, │ │ │ │ - stroke: false, │ │ │ │ - graphic: false │ │ │ │ + featureMember: function(node, obj) { │ │ │ │ + var feature = { │ │ │ │ + attributes: {} │ │ │ │ }; │ │ │ │ - if (!this.multipleSymbolizers) { │ │ │ │ - config = rule.symbolizer["Point"] || config │ │ │ │ - } │ │ │ │ - this.readChildNodes(node, config); │ │ │ │ - if (this.multipleSymbolizers) { │ │ │ │ - config.zIndex = this.featureTypeCounter; │ │ │ │ - rule.symbolizers.push(new OpenLayers.Symbolizer.Point(config)) │ │ │ │ - } else { │ │ │ │ - rule.symbolizer["Point"] = config │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - Stroke: function(node, symbolizer) { │ │ │ │ - symbolizer.stroke = true; │ │ │ │ - this.readChildNodes(node, symbolizer) │ │ │ │ - }, │ │ │ │ - Fill: function(node, symbolizer) { │ │ │ │ - symbolizer.fill = true; │ │ │ │ - this.readChildNodes(node, symbolizer) │ │ │ │ + obj.features.push(feature); │ │ │ │ + this.readChildNodes(node, feature) │ │ │ │ }, │ │ │ │ - CssParameter: function(node, symbolizer) { │ │ │ │ - var cssProperty = node.getAttribute("name"); │ │ │ │ - var symProperty = this.cssMap[cssProperty]; │ │ │ │ - if (symbolizer.label) { │ │ │ │ - if (cssProperty === "fill") { │ │ │ │ - symProperty = "fontColor" │ │ │ │ - } else if (cssProperty === "fill-opacity") { │ │ │ │ - symProperty = "fontOpacity" │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (symProperty) { │ │ │ │ - var value = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (value) { │ │ │ │ - symbolizer[symProperty] = value │ │ │ │ - } │ │ │ │ - } │ │ │ │ + name: function(node, obj) { │ │ │ │ + obj.attributes.name = this.getChildValue(node) │ │ │ │ }, │ │ │ │ - Graphic: function(node, symbolizer) { │ │ │ │ - symbolizer.graphic = true; │ │ │ │ - var graphic = {}; │ │ │ │ - this.readChildNodes(node, graphic); │ │ │ │ - var properties = ["stroke", "strokeColor", "strokeWidth", "strokeOpacity", "strokeLinecap", "fill", "fillColor", "fillOpacity", "graphicName", "rotation", "graphicFormat"]; │ │ │ │ - var prop, value; │ │ │ │ - for (var i = 0, len = properties.length; i < len; ++i) { │ │ │ │ - prop = properties[i]; │ │ │ │ - value = graphic[prop]; │ │ │ │ - if (value != undefined) { │ │ │ │ - symbolizer[prop] = value │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (graphic.opacity != undefined) { │ │ │ │ - symbolizer.graphicOpacity = graphic.opacity │ │ │ │ + pos: function(node, obj) { │ │ │ │ + if (!this.externalProjection) { │ │ │ │ + this.externalProjection = new OpenLayers.Projection(node.getAttribute("srsName")) │ │ │ │ } │ │ │ │ - if (graphic.size != undefined) { │ │ │ │ - var pointRadius = graphic.size / 2; │ │ │ │ - if (isNaN(pointRadius)) { │ │ │ │ - symbolizer.graphicWidth = graphic.size │ │ │ │ - } else { │ │ │ │ - symbolizer.pointRadius = graphic.size / 2 │ │ │ │ + OpenLayers.Format.GML.v3.prototype.readers.gml.pos.apply(this, [node, obj]) │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.GML.v3.prototype.readers.gml) │ │ │ │ + }, │ │ │ │ + writers: { │ │ │ │ + sos: { │ │ │ │ + GetFeatureOfInterest: function(options) { │ │ │ │ + var node = this.createElementNSPlus("GetFeatureOfInterest", { │ │ │ │ + attributes: { │ │ │ │ + version: this.VERSION, │ │ │ │ + service: "SOS", │ │ │ │ + "xsi:schemaLocation": this.schemaLocation │ │ │ │ } │ │ │ │ + }); │ │ │ │ + for (var i = 0, len = options.fois.length; i < len; i++) { │ │ │ │ + this.writeNode("FeatureOfInterestId", { │ │ │ │ + foi: options.fois[i] │ │ │ │ + }, node) │ │ │ │ } │ │ │ │ - if (graphic.href != undefined) { │ │ │ │ - symbolizer.externalGraphic = graphic.href │ │ │ │ - } │ │ │ │ - if (graphic.rotation != undefined) { │ │ │ │ - symbolizer.rotation = graphic.rotation │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - ExternalGraphic: function(node, graphic) { │ │ │ │ - this.readChildNodes(node, graphic) │ │ │ │ - }, │ │ │ │ - Mark: function(node, graphic) { │ │ │ │ - this.readChildNodes(node, graphic) │ │ │ │ - }, │ │ │ │ - WellKnownName: function(node, graphic) { │ │ │ │ - graphic.graphicName = this.getChildValue(node) │ │ │ │ - }, │ │ │ │ - Opacity: function(node, obj) { │ │ │ │ - var opacity = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (opacity) { │ │ │ │ - obj.opacity = opacity │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - Size: function(node, obj) { │ │ │ │ - var size = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (size) { │ │ │ │ - obj.size = size │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - Rotation: function(node, obj) { │ │ │ │ - var rotation = this.readers.ogc._expression.call(this, node); │ │ │ │ - if (rotation) { │ │ │ │ - obj.rotation = rotation │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - OnlineResource: function(node, obj) { │ │ │ │ - obj.href = this.getAttributeNS(node, this.namespaces.xlink, "href") │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - Format: function(node, graphic) { │ │ │ │ - graphic.graphicFormat = this.getChildValue(node) │ │ │ │ + FeatureOfInterestId: function(options) { │ │ │ │ + var node = this.createElementNSPlus("FeatureOfInterestId", { │ │ │ │ + value: options.foi │ │ │ │ + }); │ │ │ │ + return node │ │ │ │ } │ │ │ │ } │ │ │ │ - }, OpenLayers.Format.Filter.v1_0_0.prototype.readers), │ │ │ │ - cssMap: { │ │ │ │ - stroke: "strokeColor", │ │ │ │ - "stroke-opacity": "strokeOpacity", │ │ │ │ - "stroke-width": "strokeWidth", │ │ │ │ - "stroke-linecap": "strokeLinecap", │ │ │ │ - "stroke-dasharray": "strokeDashstyle", │ │ │ │ - fill: "fillColor", │ │ │ │ - "fill-opacity": "fillOpacity", │ │ │ │ - "font-family": "fontFamily", │ │ │ │ - "font-size": "fontSize", │ │ │ │ - "font-weight": "fontWeight", │ │ │ │ - "font-style": "fontStyle" │ │ │ │ }, │ │ │ │ - getCssProperty: function(sym) { │ │ │ │ - var css = null; │ │ │ │ - for (var prop in this.cssMap) { │ │ │ │ - if (this.cssMap[prop] == sym) { │ │ │ │ - css = prop; │ │ │ │ - break │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Format.SOSGetFeatureOfInterest" │ │ │ │ +}); │ │ │ │ +OpenLayers.Protocol.SOS.v1_0_0 = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ + fois: null, │ │ │ │ + formatOptions: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.initialize.apply(this, [options]); │ │ │ │ + if (!options.format) { │ │ │ │ + this.format = new OpenLayers.Format.SOSGetFeatureOfInterest(this.formatOptions) │ │ │ │ } │ │ │ │ - return css │ │ │ │ }, │ │ │ │ - getGraphicFormat: function(href) { │ │ │ │ - var format, regex; │ │ │ │ - for (var key in this.graphicFormats) { │ │ │ │ - if (this.graphicFormats[key].test(href)) { │ │ │ │ - format = key; │ │ │ │ - break │ │ │ │ + destroy: function() { │ │ │ │ + if (this.options && !this.options.format) { │ │ │ │ + this.format.destroy() │ │ │ │ + } │ │ │ │ + this.format = null; │ │ │ │ + OpenLayers.Protocol.prototype.destroy.apply(this) │ │ │ │ + }, │ │ │ │ + read: function(options) { │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.options || {}); │ │ │ │ + var response = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "read" │ │ │ │ + }); │ │ │ │ + var format = this.format; │ │ │ │ + var data = OpenLayers.Format.XML.prototype.write.apply(format, [format.writeNode("sos:GetFeatureOfInterest", { │ │ │ │ + fois: this.fois │ │ │ │ + })]); │ │ │ │ + response.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, response, options), │ │ │ │ + data: data │ │ │ │ + }); │ │ │ │ + return response │ │ │ │ + }, │ │ │ │ + handleRead: function(response, options) { │ │ │ │ + if (options.callback) { │ │ │ │ + var request = response.priv; │ │ │ │ + if (request.status >= 200 && request.status < 300) { │ │ │ │ + response.features = this.parseFeatures(request); │ │ │ │ + response.code = OpenLayers.Protocol.Response.SUCCESS │ │ │ │ + } else { │ │ │ │ + response.code = OpenLayers.Protocol.Response.FAILURE │ │ │ │ } │ │ │ │ + options.callback.call(options.scope, response) │ │ │ │ } │ │ │ │ - return format || this.defaultGraphicFormat │ │ │ │ }, │ │ │ │ - defaultGraphicFormat: "image/png", │ │ │ │ - graphicFormats: { │ │ │ │ - "image/jpeg": /\.jpe?g$/i, │ │ │ │ - "image/gif": /\.gif$/i, │ │ │ │ - "image/png": /\.png$/i │ │ │ │ + parseFeatures: function(request) { │ │ │ │ + var doc = request.responseXML; │ │ │ │ + if (!doc || !doc.documentElement) { │ │ │ │ + doc = request.responseText │ │ │ │ + } │ │ │ │ + if (!doc || doc.length <= 0) { │ │ │ │ + return null │ │ │ │ + } │ │ │ │ + return this.format.read(doc) │ │ │ │ }, │ │ │ │ - write: function(sld) { │ │ │ │ - return this.writers.sld.StyledLayerDescriptor.apply(this, [sld]) │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.SOS.v1_0_0" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.CSWGetRecords = function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, OpenLayers.Format.CSWGetRecords.DEFAULTS); │ │ │ │ + var cls = OpenLayers.Format.CSWGetRecords["v" + options.version.replace(/\./g, "_")]; │ │ │ │ + if (!cls) { │ │ │ │ + throw "Unsupported CSWGetRecords version: " + options.version │ │ │ │ + } │ │ │ │ + return new cls(options) │ │ │ │ +}; │ │ │ │ +OpenLayers.Format.CSWGetRecords.DEFAULTS = { │ │ │ │ + version: "2.0.2" │ │ │ │ +}; │ │ │ │ +OpenLayers.Format.CSWGetRecords.v2_0_2 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + namespaces: { │ │ │ │ + csw: "http://www.opengis.net/cat/csw/2.0.2", │ │ │ │ + dc: "http://purl.org/dc/elements/1.1/", │ │ │ │ + dct: "http://purl.org/dc/terms/", │ │ │ │ + gmd: "http://www.isotc211.org/2005/gmd", │ │ │ │ + geonet: "http://www.fao.org/geonetwork", │ │ │ │ + ogc: "http://www.opengis.net/ogc", │ │ │ │ + ows: "http://www.opengis.net/ows", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ }, │ │ │ │ - writers: OpenLayers.Util.applyDefaults({ │ │ │ │ - sld: { │ │ │ │ - _OGCExpression: function(nodeName, value) { │ │ │ │ - var node = this.createElementNSPlus(nodeName); │ │ │ │ - var tokens = typeof value == "string" ? value.split("${") : [value]; │ │ │ │ - node.appendChild(this.createTextNode(tokens[0])); │ │ │ │ - var item, last; │ │ │ │ - for (var i = 1, len = tokens.length; i < len; i++) { │ │ │ │ - item = tokens[i]; │ │ │ │ - last = item.indexOf("}"); │ │ │ │ - if (last > 0) { │ │ │ │ - this.writeNode("ogc:PropertyName", { │ │ │ │ - property: item.substring(0, last) │ │ │ │ - }, node); │ │ │ │ - node.appendChild(this.createTextNode(item.substring(++last))) │ │ │ │ - } else { │ │ │ │ - node.appendChild(this.createTextNode("${" + item)) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - StyledLayerDescriptor: function(sld) { │ │ │ │ - var root = this.createElementNSPlus("sld:StyledLayerDescriptor", { │ │ │ │ - attributes: { │ │ │ │ - version: this.VERSION, │ │ │ │ - "xsi:schemaLocation": this.schemaLocation │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - root.setAttribute("xmlns:ogc", this.namespaces.ogc); │ │ │ │ - root.setAttribute("xmlns:gml", this.namespaces.gml); │ │ │ │ - if (sld.name) { │ │ │ │ - this.writeNode("Name", sld.name, root) │ │ │ │ - } │ │ │ │ - if (sld.title) { │ │ │ │ - this.writeNode("Title", sld.title, root) │ │ │ │ - } │ │ │ │ - if (sld.description) { │ │ │ │ - this.writeNode("Abstract", sld.description, root) │ │ │ │ - } │ │ │ │ - if (OpenLayers.Util.isArray(sld.namedLayers)) { │ │ │ │ - for (var i = 0, len = sld.namedLayers.length; i < len; ++i) { │ │ │ │ - this.writeNode("NamedLayer", sld.namedLayers[i], root) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - for (var name in sld.namedLayers) { │ │ │ │ - this.writeNode("NamedLayer", sld.namedLayers[name], root) │ │ │ │ - } │ │ │ │ + defaultPrefix: "csw", │ │ │ │ + version: "2.0.2", │ │ │ │ + schemaLocation: "http://www.opengis.net/cat/csw/2.0.2 http://schemas.opengis.net/csw/2.0.2/CSW-discovery.xsd", │ │ │ │ + requestId: null, │ │ │ │ + resultType: null, │ │ │ │ + outputFormat: null, │ │ │ │ + outputSchema: null, │ │ │ │ + startPosition: null, │ │ │ │ + maxRecords: null, │ │ │ │ + DistributedSearch: null, │ │ │ │ + ResponseHandler: null, │ │ │ │ + Query: null, │ │ │ │ + regExes: { │ │ │ │ + trimSpace: /^\s*|\s*$/g, │ │ │ │ + removeSpace: /\s*/g, │ │ │ │ + splitSpace: /\s+/, │ │ │ │ + trimComma: /\s*,\s*/g │ │ │ │ + }, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]) │ │ │ │ + }, │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]) │ │ │ │ + } │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement │ │ │ │ + } │ │ │ │ + var obj = {}; │ │ │ │ + this.readNode(data, obj); │ │ │ │ + return obj │ │ │ │ + }, │ │ │ │ + readers: { │ │ │ │ + csw: { │ │ │ │ + GetRecordsResponse: function(node, obj) { │ │ │ │ + obj.records = []; │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + var version = this.getAttributeNS(node, "", "version"); │ │ │ │ + if (version != "") { │ │ │ │ + obj.version = version │ │ │ │ } │ │ │ │ - return root │ │ │ │ }, │ │ │ │ - Name: function(name) { │ │ │ │ - return this.createElementNSPlus("sld:Name", { │ │ │ │ - value: name │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - Title: function(title) { │ │ │ │ - return this.createElementNSPlus("sld:Title", { │ │ │ │ - value: title │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - Abstract: function(description) { │ │ │ │ - return this.createElementNSPlus("sld:Abstract", { │ │ │ │ - value: description │ │ │ │ - }) │ │ │ │ + RequestId: function(node, obj) { │ │ │ │ + obj.RequestId = this.getChildValue(node) │ │ │ │ }, │ │ │ │ - NamedLayer: function(layer) { │ │ │ │ - var node = this.createElementNSPlus("sld:NamedLayer"); │ │ │ │ - this.writeNode("Name", layer.name, node); │ │ │ │ - if (layer.namedStyles) { │ │ │ │ - for (var i = 0, len = layer.namedStyles.length; i < len; ++i) { │ │ │ │ - this.writeNode("NamedStyle", layer.namedStyles[i], node) │ │ │ │ - } │ │ │ │ + SearchStatus: function(node, obj) { │ │ │ │ + obj.SearchStatus = {}; │ │ │ │ + var timestamp = this.getAttributeNS(node, "", "timestamp"); │ │ │ │ + if (timestamp != "") { │ │ │ │ + obj.SearchStatus.timestamp = timestamp │ │ │ │ } │ │ │ │ - if (layer.userStyles) { │ │ │ │ - for (var i = 0, len = layer.userStyles.length; i < len; ++i) { │ │ │ │ - this.writeNode("UserStyle", layer.userStyles[i], node) │ │ │ │ + }, │ │ │ │ + SearchResults: function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + var attrs = node.attributes; │ │ │ │ + var SearchResults = {}; │ │ │ │ + for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ + if (attrs[i].name == "numberOfRecordsMatched" || attrs[i].name == "numberOfRecordsReturned" || attrs[i].name == "nextRecord") { │ │ │ │ + SearchResults[attrs[i].name] = parseInt(attrs[i].nodeValue) │ │ │ │ + } else { │ │ │ │ + SearchResults[attrs[i].name] = attrs[i].nodeValue │ │ │ │ } │ │ │ │ } │ │ │ │ - return node │ │ │ │ + obj.SearchResults = SearchResults │ │ │ │ }, │ │ │ │ - NamedStyle: function(name) { │ │ │ │ - var node = this.createElementNSPlus("sld:NamedStyle"); │ │ │ │ - this.writeNode("Name", name, node); │ │ │ │ - return node │ │ │ │ + SummaryRecord: function(node, obj) { │ │ │ │ + var record = { │ │ │ │ + type: "SummaryRecord" │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, record); │ │ │ │ + obj.records.push(record) │ │ │ │ }, │ │ │ │ - UserStyle: function(style) { │ │ │ │ - var node = this.createElementNSPlus("sld:UserStyle"); │ │ │ │ - if (style.name) { │ │ │ │ - this.writeNode("Name", style.name, node) │ │ │ │ - } │ │ │ │ - if (style.title) { │ │ │ │ - this.writeNode("Title", style.title, node) │ │ │ │ - } │ │ │ │ - if (style.description) { │ │ │ │ - this.writeNode("Abstract", style.description, node) │ │ │ │ - } │ │ │ │ - if (style.isDefault) { │ │ │ │ - this.writeNode("IsDefault", style.isDefault, node) │ │ │ │ - } │ │ │ │ - if (this.multipleSymbolizers && style.rules) { │ │ │ │ - var rulesByZ = { │ │ │ │ - 0: [] │ │ │ │ - }; │ │ │ │ - var zValues = [0]; │ │ │ │ - var rule, ruleMap, symbolizer, zIndex, clone; │ │ │ │ - for (var i = 0, ii = style.rules.length; i < ii; ++i) { │ │ │ │ - rule = style.rules[i]; │ │ │ │ - if (rule.symbolizers) { │ │ │ │ - ruleMap = {}; │ │ │ │ - for (var j = 0, jj = rule.symbolizers.length; j < jj; ++j) { │ │ │ │ - symbolizer = rule.symbolizers[j]; │ │ │ │ - zIndex = symbolizer.zIndex; │ │ │ │ - if (!(zIndex in ruleMap)) { │ │ │ │ - clone = rule.clone(); │ │ │ │ - clone.symbolizers = []; │ │ │ │ - ruleMap[zIndex] = clone │ │ │ │ - } │ │ │ │ - ruleMap[zIndex].symbolizers.push(symbolizer.clone()) │ │ │ │ - } │ │ │ │ - for (zIndex in ruleMap) { │ │ │ │ - if (!(zIndex in rulesByZ)) { │ │ │ │ - zValues.push(zIndex); │ │ │ │ - rulesByZ[zIndex] = [] │ │ │ │ - } │ │ │ │ - rulesByZ[zIndex].push(ruleMap[zIndex]) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - rulesByZ[0].push(rule.clone()) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - zValues.sort(); │ │ │ │ - var rules; │ │ │ │ - for (var i = 0, ii = zValues.length; i < ii; ++i) { │ │ │ │ - rules = rulesByZ[zValues[i]]; │ │ │ │ - if (rules.length > 0) { │ │ │ │ - clone = style.clone(); │ │ │ │ - clone.rules = rulesByZ[zValues[i]]; │ │ │ │ - this.writeNode("FeatureTypeStyle", clone, node) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.writeNode("FeatureTypeStyle", style, node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ + BriefRecord: function(node, obj) { │ │ │ │ + var record = { │ │ │ │ + type: "BriefRecord" │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, record); │ │ │ │ + obj.records.push(record) │ │ │ │ }, │ │ │ │ - IsDefault: function(bool) { │ │ │ │ - return this.createElementNSPlus("sld:IsDefault", { │ │ │ │ - value: bool ? "1" : "0" │ │ │ │ - }) │ │ │ │ + DCMIRecord: function(node, obj) { │ │ │ │ + var record = { │ │ │ │ + type: "DCMIRecord" │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, record); │ │ │ │ + obj.records.push(record) │ │ │ │ }, │ │ │ │ - FeatureTypeStyle: function(style) { │ │ │ │ - var node = this.createElementNSPlus("sld:FeatureTypeStyle"); │ │ │ │ - for (var i = 0, len = style.rules.length; i < len; ++i) { │ │ │ │ - this.writeNode("Rule", style.rules[i], node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ + Record: function(node, obj) { │ │ │ │ + var record = { │ │ │ │ + type: "Record" │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, record); │ │ │ │ + obj.records.push(record) │ │ │ │ }, │ │ │ │ - Rule: function(rule) { │ │ │ │ - var node = this.createElementNSPlus("sld:Rule"); │ │ │ │ - if (rule.name) { │ │ │ │ - this.writeNode("Name", rule.name, node) │ │ │ │ + "*": function(node, obj) { │ │ │ │ + var name = node.localName || node.nodeName.split(":").pop(); │ │ │ │ + obj[name] = this.getChildValue(node) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + geonet: { │ │ │ │ + info: function(node, obj) { │ │ │ │ + var gninfo = {}; │ │ │ │ + this.readChildNodes(node, gninfo); │ │ │ │ + obj.gninfo = gninfo │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + dc: { │ │ │ │ + "*": function(node, obj) { │ │ │ │ + var name = node.localName || node.nodeName.split(":").pop(); │ │ │ │ + if (!OpenLayers.Util.isArray(obj[name])) { │ │ │ │ + obj[name] = [] │ │ │ │ } │ │ │ │ - if (rule.title) { │ │ │ │ - this.writeNode("Title", rule.title, node) │ │ │ │ + var dc_element = {}; │ │ │ │ + var attrs = node.attributes; │ │ │ │ + for (var i = 0, len = attrs.length; i < len; ++i) { │ │ │ │ + dc_element[attrs[i].name] = attrs[i].nodeValue │ │ │ │ } │ │ │ │ - if (rule.description) { │ │ │ │ - this.writeNode("Abstract", rule.description, node) │ │ │ │ + dc_element.value = this.getChildValue(node); │ │ │ │ + if (dc_element.value != "") { │ │ │ │ + obj[name].push(dc_element) │ │ │ │ } │ │ │ │ - if (rule.elseFilter) { │ │ │ │ - this.writeNode("ElseFilter", null, node) │ │ │ │ - } else if (rule.filter) { │ │ │ │ - this.writeNode("ogc:Filter", rule.filter, node) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + dct: { │ │ │ │ + "*": function(node, obj) { │ │ │ │ + var name = node.localName || node.nodeName.split(":").pop(); │ │ │ │ + if (!OpenLayers.Util.isArray(obj[name])) { │ │ │ │ + obj[name] = [] │ │ │ │ } │ │ │ │ - if (rule.minScaleDenominator != undefined) { │ │ │ │ - this.writeNode("MinScaleDenominator", rule.minScaleDenominator, node) │ │ │ │ + obj[name].push(this.getChildValue(node)) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + ows: OpenLayers.Util.applyDefaults({ │ │ │ │ + BoundingBox: function(node, obj) { │ │ │ │ + if (obj.bounds) { │ │ │ │ + obj.BoundingBox = [{ │ │ │ │ + crs: obj.projection, │ │ │ │ + value: [obj.bounds.left, obj.bounds.bottom, obj.bounds.right, obj.bounds.top] │ │ │ │ + }]; │ │ │ │ + delete obj.projection; │ │ │ │ + delete obj.bounds │ │ │ │ } │ │ │ │ - if (rule.maxScaleDenominator != undefined) { │ │ │ │ - this.writeNode("MaxScaleDenominator", rule.maxScaleDenominator, node) │ │ │ │ + OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"]["BoundingBox"].apply(this, arguments) │ │ │ │ + } │ │ │ │ + }, OpenLayers.Format.OWSCommon.v1_0_0.prototype.readers["ows"]) │ │ │ │ + }, │ │ │ │ + write: function(options) { │ │ │ │ + var node = this.writeNode("csw:GetRecords", options); │ │ │ │ + node.setAttribute("xmlns:gmd", this.namespaces.gmd); │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [node]) │ │ │ │ + }, │ │ │ │ + writers: { │ │ │ │ + csw: { │ │ │ │ + GetRecords: function(options) { │ │ │ │ + if (!options) { │ │ │ │ + options = {} │ │ │ │ } │ │ │ │ - var type, symbolizer; │ │ │ │ - if (this.multipleSymbolizers && rule.symbolizers) { │ │ │ │ - var symbolizer; │ │ │ │ - for (var i = 0, ii = rule.symbolizers.length; i < ii; ++i) { │ │ │ │ - symbolizer = rule.symbolizers[i]; │ │ │ │ - type = symbolizer.CLASS_NAME.split(".").pop(); │ │ │ │ - this.writeNode(type + "Symbolizer", symbolizer, node) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - var types = OpenLayers.Style.SYMBOLIZER_PREFIXES; │ │ │ │ - for (var i = 0, len = types.length; i < len; ++i) { │ │ │ │ - type = types[i]; │ │ │ │ - symbolizer = rule.symbolizer[type]; │ │ │ │ - if (symbolizer) { │ │ │ │ - this.writeNode(type + "Symbolizer", symbolizer, node) │ │ │ │ - } │ │ │ │ + var node = this.createElementNSPlus("csw:GetRecords", { │ │ │ │ + attributes: { │ │ │ │ + service: "CSW", │ │ │ │ + version: this.version, │ │ │ │ + requestId: options.requestId || this.requestId, │ │ │ │ + resultType: options.resultType || this.resultType, │ │ │ │ + outputFormat: options.outputFormat || this.outputFormat, │ │ │ │ + outputSchema: options.outputSchema || this.outputSchema, │ │ │ │ + startPosition: options.startPosition || this.startPosition, │ │ │ │ + maxRecords: options.maxRecords || this.maxRecords │ │ │ │ } │ │ │ │ + }); │ │ │ │ + if (options.DistributedSearch || this.DistributedSearch) { │ │ │ │ + this.writeNode("csw:DistributedSearch", options.DistributedSearch || this.DistributedSearch, node) │ │ │ │ } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - ElseFilter: function() { │ │ │ │ - return this.createElementNSPlus("sld:ElseFilter") │ │ │ │ - }, │ │ │ │ - MinScaleDenominator: function(scale) { │ │ │ │ - return this.createElementNSPlus("sld:MinScaleDenominator", { │ │ │ │ - value: scale │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - MaxScaleDenominator: function(scale) { │ │ │ │ - return this.createElementNSPlus("sld:MaxScaleDenominator", { │ │ │ │ - value: scale │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - LineSymbolizer: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:LineSymbolizer"); │ │ │ │ - this.writeNode("Stroke", symbolizer, node); │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - Stroke: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Stroke"); │ │ │ │ - if (symbolizer.strokeColor != undefined) { │ │ │ │ - this.writeNode("CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "strokeColor" │ │ │ │ - }, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.strokeOpacity != undefined) { │ │ │ │ - this.writeNode("CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "strokeOpacity" │ │ │ │ - }, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.strokeWidth != undefined) { │ │ │ │ - this.writeNode("CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "strokeWidth" │ │ │ │ - }, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.strokeDashstyle != undefined && symbolizer.strokeDashstyle !== "solid") { │ │ │ │ - this.writeNode("CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "strokeDashstyle" │ │ │ │ - }, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.strokeLinecap != undefined) { │ │ │ │ - this.writeNode("CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "strokeLinecap" │ │ │ │ - }, node) │ │ │ │ + var ResponseHandler = options.ResponseHandler || this.ResponseHandler; │ │ │ │ + if (OpenLayers.Util.isArray(ResponseHandler) && ResponseHandler.length > 0) { │ │ │ │ + for (var i = 0, len = ResponseHandler.length; i < len; i++) { │ │ │ │ + this.writeNode("csw:ResponseHandler", ResponseHandler[i], node) │ │ │ │ + } │ │ │ │ } │ │ │ │ + this.writeNode("Query", options.Query || this.Query, node); │ │ │ │ return node │ │ │ │ }, │ │ │ │ - CssParameter: function(obj) { │ │ │ │ - return this.createElementNSPlus("sld:CssParameter", { │ │ │ │ + DistributedSearch: function(options) { │ │ │ │ + var node = this.createElementNSPlus("csw:DistributedSearch", { │ │ │ │ attributes: { │ │ │ │ - name: this.getCssProperty(obj.key) │ │ │ │ - }, │ │ │ │ - value: obj.symbolizer[obj.key] │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - TextSymbolizer: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:TextSymbolizer"); │ │ │ │ - if (symbolizer.label != null) { │ │ │ │ - this.writeNode("Label", symbolizer.label, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.fontFamily != null || symbolizer.fontSize != null || symbolizer.fontWeight != null || symbolizer.fontStyle != null) { │ │ │ │ - this.writeNode("Font", symbolizer, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.labelAnchorPointX != null || symbolizer.labelAnchorPointY != null || symbolizer.labelAlign != null || symbolizer.labelXOffset != null || symbolizer.labelYOffset != null || symbolizer.labelRotation != null || symbolizer.labelPerpendicularOffset != null) { │ │ │ │ - this.writeNode("LabelPlacement", symbolizer, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.haloRadius != null || symbolizer.haloColor != null || symbolizer.haloOpacity != null) { │ │ │ │ - this.writeNode("Halo", symbolizer, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.fontColor != null || symbolizer.fontOpacity != null) { │ │ │ │ - this.writeNode("Fill", { │ │ │ │ - fillColor: symbolizer.fontColor, │ │ │ │ - fillOpacity: symbolizer.fontOpacity │ │ │ │ - }, node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - LabelPlacement: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:LabelPlacement"); │ │ │ │ - if ((symbolizer.labelAnchorPointX != null || symbolizer.labelAnchorPointY != null || symbolizer.labelAlign != null || symbolizer.labelXOffset != null || symbolizer.labelYOffset != null || symbolizer.labelRotation != null) && symbolizer.labelPerpendicularOffset == null) { │ │ │ │ - this.writeNode("PointPlacement", symbolizer, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.labelPerpendicularOffset != null) { │ │ │ │ - this.writeNode("LinePlacement", symbolizer, node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - LinePlacement: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:LinePlacement"); │ │ │ │ - this.writeNode("PerpendicularOffset", symbolizer.labelPerpendicularOffset, node); │ │ │ │ + hopCount: options.hopCount │ │ │ │ + } │ │ │ │ + }); │ │ │ │ return node │ │ │ │ }, │ │ │ │ - PerpendicularOffset: function(value) { │ │ │ │ - return this.createElementNSPlus("sld:PerpendicularOffset", { │ │ │ │ - value: value │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - PointPlacement: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:PointPlacement"); │ │ │ │ - if (symbolizer.labelAnchorPointX != null || symbolizer.labelAnchorPointY != null || symbolizer.labelAlign != null) { │ │ │ │ - this.writeNode("AnchorPoint", symbolizer, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.labelXOffset != null || symbolizer.labelYOffset != null) { │ │ │ │ - this.writeNode("Displacement", symbolizer, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.labelRotation != null) { │ │ │ │ - this.writeNode("Rotation", symbolizer.labelRotation, node) │ │ │ │ - } │ │ │ │ + ResponseHandler: function(options) { │ │ │ │ + var node = this.createElementNSPlus("csw:ResponseHandler", { │ │ │ │ + value: options.value │ │ │ │ + }); │ │ │ │ return node │ │ │ │ }, │ │ │ │ - AnchorPoint: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:AnchorPoint"); │ │ │ │ - var x = symbolizer.labelAnchorPointX, │ │ │ │ - y = symbolizer.labelAnchorPointY; │ │ │ │ - if (x != null) { │ │ │ │ - this.writeNode("AnchorPointX", x, node) │ │ │ │ - } │ │ │ │ - if (y != null) { │ │ │ │ - this.writeNode("AnchorPointY", y, node) │ │ │ │ + Query: function(options) { │ │ │ │ + if (!options) { │ │ │ │ + options = {} │ │ │ │ } │ │ │ │ - if (x == null && y == null) { │ │ │ │ - var xAlign = symbolizer.labelAlign.substr(0, 1), │ │ │ │ - yAlign = symbolizer.labelAlign.substr(1, 1); │ │ │ │ - if (xAlign === "l") { │ │ │ │ - x = 0 │ │ │ │ - } else if (xAlign === "c") { │ │ │ │ - x = .5 │ │ │ │ - } else if (xAlign === "r") { │ │ │ │ - x = 1 │ │ │ │ + var node = this.createElementNSPlus("csw:Query", { │ │ │ │ + attributes: { │ │ │ │ + typeNames: options.typeNames || "csw:Record" │ │ │ │ } │ │ │ │ - if (yAlign === "b") { │ │ │ │ - y = 0 │ │ │ │ - } else if (yAlign === "m") { │ │ │ │ - y = .5 │ │ │ │ - } else if (yAlign === "t") { │ │ │ │ - y = 1 │ │ │ │ + }); │ │ │ │ + var ElementName = options.ElementName; │ │ │ │ + if (OpenLayers.Util.isArray(ElementName) && ElementName.length > 0) { │ │ │ │ + for (var i = 0, len = ElementName.length; i < len; i++) { │ │ │ │ + this.writeNode("csw:ElementName", ElementName[i], node) │ │ │ │ } │ │ │ │ - this.writeNode("AnchorPointX", x, node); │ │ │ │ - this.writeNode("AnchorPointY", y, node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - AnchorPointX: function(value) { │ │ │ │ - return this.createElementNSPlus("sld:AnchorPointX", { │ │ │ │ - value: value │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - AnchorPointY: function(value) { │ │ │ │ - return this.createElementNSPlus("sld:AnchorPointY", { │ │ │ │ - value: value │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - Displacement: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Displacement"); │ │ │ │ - if (symbolizer.labelXOffset != null) { │ │ │ │ - this.writeNode("DisplacementX", symbolizer.labelXOffset, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.labelYOffset != null) { │ │ │ │ - this.writeNode("DisplacementY", symbolizer.labelYOffset, node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - DisplacementX: function(value) { │ │ │ │ - return this.createElementNSPlus("sld:DisplacementX", { │ │ │ │ - value: value │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - DisplacementY: function(value) { │ │ │ │ - return this.createElementNSPlus("sld:DisplacementY", { │ │ │ │ - value: value │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - Font: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Font"); │ │ │ │ - if (symbolizer.fontFamily) { │ │ │ │ - this.writeNode("CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "fontFamily" │ │ │ │ - }, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.fontSize) { │ │ │ │ - this.writeNode("CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "fontSize" │ │ │ │ - }, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.fontWeight) { │ │ │ │ - this.writeNode("CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "fontWeight" │ │ │ │ - }, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.fontStyle) { │ │ │ │ - this.writeNode("CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "fontStyle" │ │ │ │ - }, node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - Label: function(label) { │ │ │ │ - return this.writers.sld._OGCExpression.call(this, "sld:Label", label) │ │ │ │ - }, │ │ │ │ - Halo: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Halo"); │ │ │ │ - if (symbolizer.haloRadius) { │ │ │ │ - this.writeNode("Radius", symbolizer.haloRadius, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.haloColor || symbolizer.haloOpacity) { │ │ │ │ - this.writeNode("Fill", { │ │ │ │ - fillColor: symbolizer.haloColor, │ │ │ │ - fillOpacity: symbolizer.haloOpacity │ │ │ │ - }, node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - Radius: function(value) { │ │ │ │ - return this.createElementNSPlus("sld:Radius", { │ │ │ │ - value: value │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - RasterSymbolizer: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:RasterSymbolizer"); │ │ │ │ - if (symbolizer.geometry) { │ │ │ │ - this.writeNode("Geometry", symbolizer.geometry, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.opacity) { │ │ │ │ - this.writeNode("Opacity", symbolizer.opacity, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.colorMap) { │ │ │ │ - this.writeNode("ColorMap", symbolizer.colorMap, node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - Geometry: function(geometry) { │ │ │ │ - var node = this.createElementNSPlus("sld:Geometry"); │ │ │ │ - if (geometry.property) { │ │ │ │ - this.writeNode("ogc:PropertyName", geometry, node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - ColorMap: function(colorMap) { │ │ │ │ - var node = this.createElementNSPlus("sld:ColorMap"); │ │ │ │ - for (var i = 0, len = colorMap.length; i < len; ++i) { │ │ │ │ - this.writeNode("ColorMapEntry", colorMap[i], node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - ColorMapEntry: function(colorMapEntry) { │ │ │ │ - var node = this.createElementNSPlus("sld:ColorMapEntry"); │ │ │ │ - var a = colorMapEntry; │ │ │ │ - node.setAttribute("color", a.color); │ │ │ │ - a.opacity !== undefined && node.setAttribute("opacity", parseFloat(a.opacity)); │ │ │ │ - a.quantity !== undefined && node.setAttribute("quantity", parseFloat(a.quantity)); │ │ │ │ - a.label !== undefined && node.setAttribute("label", a.label); │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - PolygonSymbolizer: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:PolygonSymbolizer"); │ │ │ │ - if (symbolizer.fill !== false) { │ │ │ │ - this.writeNode("Fill", symbolizer, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.stroke !== false) { │ │ │ │ - this.writeNode("Stroke", symbolizer, node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - Fill: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Fill"); │ │ │ │ - if (symbolizer.fillColor) { │ │ │ │ - this.writeNode("CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "fillColor" │ │ │ │ - }, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.fillOpacity != null) { │ │ │ │ - this.writeNode("CssParameter", { │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - key: "fillOpacity" │ │ │ │ - }, node) │ │ │ │ - } │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - PointSymbolizer: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:PointSymbolizer"); │ │ │ │ - this.writeNode("Graphic", symbolizer, node); │ │ │ │ - return node │ │ │ │ - }, │ │ │ │ - Graphic: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Graphic"); │ │ │ │ - if (symbolizer.externalGraphic != undefined) { │ │ │ │ - this.writeNode("ExternalGraphic", symbolizer, node) │ │ │ │ } else { │ │ │ │ - this.writeNode("Mark", symbolizer, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.graphicOpacity != undefined) { │ │ │ │ - this.writeNode("Opacity", symbolizer.graphicOpacity, node) │ │ │ │ + this.writeNode("csw:ElementSetName", options.ElementSetName || { │ │ │ │ + value: "summary" │ │ │ │ + }, node) │ │ │ │ } │ │ │ │ - if (symbolizer.pointRadius != undefined) { │ │ │ │ - this.writeNode("Size", symbolizer.pointRadius * 2, node) │ │ │ │ - } else if (symbolizer.graphicWidth != undefined) { │ │ │ │ - this.writeNode("Size", symbolizer.graphicWidth, node) │ │ │ │ + if (options.Constraint) { │ │ │ │ + this.writeNode("csw:Constraint", options.Constraint, node) │ │ │ │ } │ │ │ │ - if (symbolizer.rotation != undefined) { │ │ │ │ - this.writeNode("Rotation", symbolizer.rotation, node) │ │ │ │ + if (options.SortBy) { │ │ │ │ + this.writeNode("ogc:SortBy", options.SortBy, node) │ │ │ │ } │ │ │ │ return node │ │ │ │ }, │ │ │ │ - ExternalGraphic: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:ExternalGraphic"); │ │ │ │ - this.writeNode("OnlineResource", symbolizer.externalGraphic, node); │ │ │ │ - var format = symbolizer.graphicFormat || this.getGraphicFormat(symbolizer.externalGraphic); │ │ │ │ - this.writeNode("Format", format, node); │ │ │ │ + ElementName: function(options) { │ │ │ │ + var node = this.createElementNSPlus("csw:ElementName", { │ │ │ │ + value: options.value │ │ │ │ + }); │ │ │ │ return node │ │ │ │ }, │ │ │ │ - Mark: function(symbolizer) { │ │ │ │ - var node = this.createElementNSPlus("sld:Mark"); │ │ │ │ - if (symbolizer.graphicName) { │ │ │ │ - this.writeNode("WellKnownName", symbolizer.graphicName, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.fill !== false) { │ │ │ │ - this.writeNode("Fill", symbolizer, node) │ │ │ │ - } │ │ │ │ - if (symbolizer.stroke !== false) { │ │ │ │ - this.writeNode("Stroke", symbolizer, node) │ │ │ │ - } │ │ │ │ + ElementSetName: function(options) { │ │ │ │ + var node = this.createElementNSPlus("csw:ElementSetName", { │ │ │ │ + attributes: { │ │ │ │ + typeNames: options.typeNames │ │ │ │ + }, │ │ │ │ + value: options.value │ │ │ │ + }); │ │ │ │ return node │ │ │ │ }, │ │ │ │ - WellKnownName: function(name) { │ │ │ │ - return this.createElementNSPlus("sld:WellKnownName", { │ │ │ │ - value: name │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - Opacity: function(value) { │ │ │ │ - return this.createElementNSPlus("sld:Opacity", { │ │ │ │ - value: value │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - Size: function(value) { │ │ │ │ - return this.writers.sld._OGCExpression.call(this, "sld:Size", value) │ │ │ │ - }, │ │ │ │ - Rotation: function(value) { │ │ │ │ - return this.createElementNSPlus("sld:Rotation", { │ │ │ │ - value: value │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - OnlineResource: function(href) { │ │ │ │ - return this.createElementNSPlus("sld:OnlineResource", { │ │ │ │ + Constraint: function(options) { │ │ │ │ + var node = this.createElementNSPlus("csw:Constraint", { │ │ │ │ attributes: { │ │ │ │ - "xlink:type": "simple", │ │ │ │ - "xlink:href": href │ │ │ │ + version: options.version │ │ │ │ } │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - Format: function(format) { │ │ │ │ - return this.createElementNSPlus("sld:Format", { │ │ │ │ - value: format │ │ │ │ - }) │ │ │ │ + }); │ │ │ │ + if (options.Filter) { │ │ │ │ + var format = new OpenLayers.Format.Filter({ │ │ │ │ + version: options.version │ │ │ │ + }); │ │ │ │ + node.appendChild(format.write(options.Filter)) │ │ │ │ + } else if (options.CqlText) { │ │ │ │ + var child = this.createElementNSPlus("CqlText", { │ │ │ │ + value: options.CqlText.value │ │ │ │ + }); │ │ │ │ + node.appendChild(child) │ │ │ │ + } │ │ │ │ + return node │ │ │ │ } │ │ │ │ - } │ │ │ │ - }, OpenLayers.Format.Filter.v1_0_0.prototype.writers), │ │ │ │ - CLASS_NAME: "OpenLayers.Format.SLD.v1" │ │ │ │ -}); │ │ │ │ -OpenLayers.Format.SLD.v1_0_0 = OpenLayers.Class(OpenLayers.Format.SLD.v1, { │ │ │ │ - VERSION: "1.0.0", │ │ │ │ - schemaLocation: "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd", │ │ │ │ - CLASS_NAME: "OpenLayers.Format.SLD.v1_0_0" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.SLDSelect = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - clearOnDeactivate: false, │ │ │ │ - layers: null, │ │ │ │ - callbacks: null, │ │ │ │ - selectionSymbolizer: { │ │ │ │ - Polygon: { │ │ │ │ - fillColor: "#FF0000", │ │ │ │ - stroke: false │ │ │ │ - }, │ │ │ │ - Line: { │ │ │ │ - strokeColor: "#FF0000", │ │ │ │ - strokeWidth: 2 │ │ │ │ }, │ │ │ │ - Point: { │ │ │ │ - graphicName: "square", │ │ │ │ - fillColor: "#FF0000", │ │ │ │ - pointRadius: 5 │ │ │ │ - } │ │ │ │ + ogc: OpenLayers.Format.Filter.v1_1_0.prototype.writers["ogc"] │ │ │ │ }, │ │ │ │ - layerOptions: null, │ │ │ │ - sketchStyle: null, │ │ │ │ - wfsCache: {}, │ │ │ │ - layerCache: {}, │ │ │ │ - initialize: function(handler, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.callbacks = OpenLayers.Util.extend({ │ │ │ │ - done: this.select, │ │ │ │ - click: this.select │ │ │ │ - }, this.callbacks); │ │ │ │ - this.handlerOptions = this.handlerOptions || {}; │ │ │ │ - this.layerOptions = OpenLayers.Util.applyDefaults(this.layerOptions, { │ │ │ │ - displayInLayerSwitcher: false, │ │ │ │ - tileOptions: { │ │ │ │ - maxGetUrlLength: 2048 │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - if (this.sketchStyle) { │ │ │ │ - this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions, { │ │ │ │ - styleMap: new OpenLayers.StyleMap({ │ │ │ │ - default: this.sketchStyle │ │ │ │ - }) │ │ │ │ - }) │ │ │ │ + CLASS_NAME: "OpenLayers.Format.CSWGetRecords.v2_0_2" │ │ │ │ +}); │ │ │ │ +OpenLayers.Protocol.CSW.v2_0_2 = OpenLayers.Class(OpenLayers.Protocol, { │ │ │ │ + formatOptions: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Protocol.prototype.initialize.apply(this, [options]); │ │ │ │ + if (!options.format) { │ │ │ │ + this.format = new OpenLayers.Format.CSWGetRecords.v2_0_2(OpenLayers.Util.extend({}, this.formatOptions)) │ │ │ │ } │ │ │ │ - this.handler = new handler(this, this.callbacks, this.handlerOptions) │ │ │ │ }, │ │ │ │ destroy: function() { │ │ │ │ - for (var key in this.layerCache) { │ │ │ │ - delete this.layerCache[key] │ │ │ │ - } │ │ │ │ - for (var key in this.wfsCache) { │ │ │ │ - delete this.wfsCache[key] │ │ │ │ + if (this.options && !this.options.format) { │ │ │ │ + this.format.destroy() │ │ │ │ } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - coupleLayerVisiblity: function(evt) { │ │ │ │ - this.setVisibility(evt.object.getVisibility()) │ │ │ │ + this.format = null; │ │ │ │ + OpenLayers.Protocol.prototype.destroy.apply(this) │ │ │ │ }, │ │ │ │ - createSelectionLayer: function(source) { │ │ │ │ - var selectionLayer; │ │ │ │ - if (!this.layerCache[source.id]) { │ │ │ │ - selectionLayer = new OpenLayers.Layer.WMS(source.name, source.url, source.params, OpenLayers.Util.applyDefaults(this.layerOptions, source.getOptions())); │ │ │ │ - this.layerCache[source.id] = selectionLayer; │ │ │ │ - if (this.layerOptions.displayInLayerSwitcher === false) { │ │ │ │ - source.events.on({ │ │ │ │ - visibilitychanged: this.coupleLayerVisiblity, │ │ │ │ - scope: selectionLayer │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - this.map.addLayer(selectionLayer) │ │ │ │ - } else { │ │ │ │ - selectionLayer = this.layerCache[source.id] │ │ │ │ - } │ │ │ │ - return selectionLayer │ │ │ │ + read: function(options) { │ │ │ │ + options = OpenLayers.Util.extend({}, options); │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.options || {}); │ │ │ │ + var response = new OpenLayers.Protocol.Response({ │ │ │ │ + requestType: "read" │ │ │ │ + }); │ │ │ │ + var data = this.format.write(options.params || options); │ │ │ │ + response.priv = OpenLayers.Request.POST({ │ │ │ │ + url: options.url, │ │ │ │ + callback: this.createCallback(this.handleRead, response, options), │ │ │ │ + params: options.params, │ │ │ │ + headers: options.headers, │ │ │ │ + data: data │ │ │ │ + }); │ │ │ │ + return response │ │ │ │ }, │ │ │ │ - createSLD: function(layer, filters, geometryAttributes) { │ │ │ │ - var sld = { │ │ │ │ - version: "1.0.0", │ │ │ │ - namedLayers: {} │ │ │ │ - }; │ │ │ │ - var layerNames = [layer.params.LAYERS].join(",").split(","); │ │ │ │ - for (var i = 0, len = layerNames.length; i < len; i++) { │ │ │ │ - var name = layerNames[i]; │ │ │ │ - sld.namedLayers[name] = { │ │ │ │ - name: name, │ │ │ │ - userStyles: [] │ │ │ │ - }; │ │ │ │ - var symbolizer = this.selectionSymbolizer; │ │ │ │ - var geometryAttribute = geometryAttributes[i]; │ │ │ │ - if (geometryAttribute.type.indexOf("Polygon") >= 0) { │ │ │ │ - symbolizer = { │ │ │ │ - Polygon: this.selectionSymbolizer["Polygon"] │ │ │ │ - } │ │ │ │ - } else if (geometryAttribute.type.indexOf("LineString") >= 0) { │ │ │ │ - symbolizer = { │ │ │ │ - Line: this.selectionSymbolizer["Line"] │ │ │ │ - } │ │ │ │ - } else if (geometryAttribute.type.indexOf("Point") >= 0) { │ │ │ │ - symbolizer = { │ │ │ │ - Point: this.selectionSymbolizer["Point"] │ │ │ │ - } │ │ │ │ + handleRead: function(response, options) { │ │ │ │ + if (options.callback) { │ │ │ │ + var request = response.priv; │ │ │ │ + if (request.status >= 200 && request.status < 300) { │ │ │ │ + response.data = this.parseData(request); │ │ │ │ + response.code = OpenLayers.Protocol.Response.SUCCESS │ │ │ │ + } else { │ │ │ │ + response.code = OpenLayers.Protocol.Response.FAILURE │ │ │ │ } │ │ │ │ - var filter = filters[i]; │ │ │ │ - sld.namedLayers[name].userStyles.push({ │ │ │ │ - name: "default", │ │ │ │ - rules: [new OpenLayers.Rule({ │ │ │ │ - symbolizer: symbolizer, │ │ │ │ - filter: filter, │ │ │ │ - maxScaleDenominator: layer.options.minScale │ │ │ │ - })] │ │ │ │ - }) │ │ │ │ + options.callback.call(options.scope, response) │ │ │ │ } │ │ │ │ - return new OpenLayers.Format.SLD({ │ │ │ │ - srsName: this.map.getProjection() │ │ │ │ - }).write(sld) │ │ │ │ }, │ │ │ │ - parseDescribeLayer: function(request) { │ │ │ │ - var format = new OpenLayers.Format.WMSDescribeLayer; │ │ │ │ + parseData: function(request) { │ │ │ │ var doc = request.responseXML; │ │ │ │ if (!doc || !doc.documentElement) { │ │ │ │ doc = request.responseText │ │ │ │ } │ │ │ │ - var describeLayer = format.read(doc); │ │ │ │ - var typeNames = []; │ │ │ │ - var url = null; │ │ │ │ - for (var i = 0, len = describeLayer.length; i < len; i++) { │ │ │ │ - if (describeLayer[i].owsType == "WFS") { │ │ │ │ - typeNames.push(describeLayer[i].typeName); │ │ │ │ - url = describeLayer[i].owsURL │ │ │ │ - } │ │ │ │ + if (!doc || doc.length <= 0) { │ │ │ │ + return null │ │ │ │ } │ │ │ │ - var options = { │ │ │ │ - url: url, │ │ │ │ - params: { │ │ │ │ - SERVICE: "WFS", │ │ │ │ - TYPENAME: typeNames.toString(), │ │ │ │ - REQUEST: "DescribeFeatureType", │ │ │ │ - VERSION: "1.0.0" │ │ │ │ - }, │ │ │ │ - callback: function(request) { │ │ │ │ - var format = new OpenLayers.Format.WFSDescribeFeatureType; │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText │ │ │ │ - } │ │ │ │ - var describeFeatureType = format.read(doc); │ │ │ │ - this.control.wfsCache[this.layer.id] = describeFeatureType; │ │ │ │ - this.control._queue && this.control.applySelection() │ │ │ │ - }, │ │ │ │ - scope: this │ │ │ │ - }; │ │ │ │ - OpenLayers.Request.GET(options) │ │ │ │ + return this.format.read(doc) │ │ │ │ }, │ │ │ │ - getGeometryAttributes: function(layer) { │ │ │ │ - var result = []; │ │ │ │ - var cache = this.wfsCache[layer.id]; │ │ │ │ - for (var i = 0, len = cache.featureTypes.length; i < len; i++) { │ │ │ │ - var typeName = cache.featureTypes[i]; │ │ │ │ - var properties = typeName.properties; │ │ │ │ - for (var j = 0, lenj = properties.length; j < lenj; j++) { │ │ │ │ - var property = properties[j]; │ │ │ │ - var type = property.type; │ │ │ │ - if (type.indexOf("LineString") >= 0 || type.indexOf("GeometryAssociationType") >= 0 || type.indexOf("GeometryPropertyType") >= 0 || type.indexOf("Point") >= 0 || type.indexOf("Polygon") >= 0) { │ │ │ │ - result.push(property) │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Protocol.CSW.v2_0_2" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.Text = OpenLayers.Class(OpenLayers.Format, { │ │ │ │ + defaultStyle: null, │ │ │ │ + extractStyles: true, │ │ │ │ + initialize: function(options) { │ │ │ │ + options = options || {}; │ │ │ │ + if (options.extractStyles !== false) { │ │ │ │ + options.defaultStyle = { │ │ │ │ + externalGraphic: OpenLayers.Util.getImageLocation("marker.png"), │ │ │ │ + graphicWidth: 21, │ │ │ │ + graphicHeight: 25, │ │ │ │ + graphicXOffset: -10.5, │ │ │ │ + graphicYOffset: -12.5 │ │ │ │ } │ │ │ │ } │ │ │ │ - return result │ │ │ │ + OpenLayers.Format.prototype.initialize.apply(this, [options]) │ │ │ │ }, │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Control.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ - var layer = this.layers[i]; │ │ │ │ - if (layer && !this.wfsCache[layer.id]) { │ │ │ │ - var options = { │ │ │ │ - url: layer.url, │ │ │ │ - params: { │ │ │ │ - SERVICE: "WMS", │ │ │ │ - VERSION: layer.params.VERSION, │ │ │ │ - LAYERS: layer.params.LAYERS, │ │ │ │ - REQUEST: "DescribeLayer" │ │ │ │ - }, │ │ │ │ - callback: this.parseDescribeLayer, │ │ │ │ - scope: { │ │ │ │ - layer: layer, │ │ │ │ - control: this │ │ │ │ + read: function(text) { │ │ │ │ + var lines = text.split("\n"); │ │ │ │ + var columns; │ │ │ │ + var features = []; │ │ │ │ + for (var lcv = 0; lcv < lines.length - 1; lcv++) { │ │ │ │ + var currLine = lines[lcv].replace(/^\s*/, "").replace(/\s*$/, ""); │ │ │ │ + if (currLine.charAt(0) != "#") { │ │ │ │ + if (!columns) { │ │ │ │ + columns = currLine.split("\t") │ │ │ │ + } else { │ │ │ │ + var vals = currLine.split("\t"); │ │ │ │ + var geometry = new OpenLayers.Geometry.Point(0, 0); │ │ │ │ + var attributes = {}; │ │ │ │ + var style = this.defaultStyle ? OpenLayers.Util.applyDefaults({}, this.defaultStyle) : null; │ │ │ │ + var icon, iconSize, iconOffset, overflow; │ │ │ │ + var set = false; │ │ │ │ + for (var valIndex = 0; valIndex < vals.length; valIndex++) { │ │ │ │ + if (vals[valIndex]) { │ │ │ │ + if (columns[valIndex] == "point") { │ │ │ │ + var coords = vals[valIndex].split(","); │ │ │ │ + geometry.y = parseFloat(coords[0]); │ │ │ │ + geometry.x = parseFloat(coords[1]); │ │ │ │ + set = true │ │ │ │ + } else if (columns[valIndex] == "lat") { │ │ │ │ + geometry.y = parseFloat(vals[valIndex]); │ │ │ │ + set = true │ │ │ │ + } else if (columns[valIndex] == "lon") { │ │ │ │ + geometry.x = parseFloat(vals[valIndex]); │ │ │ │ + set = true │ │ │ │ + } else if (columns[valIndex] == "title") attributes["title"] = vals[valIndex]; │ │ │ │ + else if (columns[valIndex] == "image" || columns[valIndex] == "icon" && style) { │ │ │ │ + style["externalGraphic"] = vals[valIndex] │ │ │ │ + } else if (columns[valIndex] == "iconSize" && style) { │ │ │ │ + var size = vals[valIndex].split(","); │ │ │ │ + style["graphicWidth"] = parseFloat(size[0]); │ │ │ │ + style["graphicHeight"] = parseFloat(size[1]) │ │ │ │ + } else if (columns[valIndex] == "iconOffset" && style) { │ │ │ │ + var offset = vals[valIndex].split(","); │ │ │ │ + style["graphicXOffset"] = parseFloat(offset[0]); │ │ │ │ + style["graphicYOffset"] = parseFloat(offset[1]) │ │ │ │ + } else if (columns[valIndex] == "description") { │ │ │ │ + attributes["description"] = vals[valIndex] │ │ │ │ + } else if (columns[valIndex] == "overflow") { │ │ │ │ + attributes["overflow"] = vals[valIndex] │ │ │ │ + } else { │ │ │ │ + attributes[columns[valIndex]] = vals[valIndex] │ │ │ │ + } │ │ │ │ } │ │ │ │ - }; │ │ │ │ - OpenLayers.Request.GET(options) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return activated │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Control.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ - var layer = this.layers[i]; │ │ │ │ - if (layer && this.clearOnDeactivate === true) { │ │ │ │ - var layerCache = this.layerCache; │ │ │ │ - var selectionLayer = layerCache[layer.id]; │ │ │ │ - if (selectionLayer) { │ │ │ │ - layer.events.un({ │ │ │ │ - visibilitychanged: this.coupleLayerVisiblity, │ │ │ │ - scope: selectionLayer │ │ │ │ - }); │ │ │ │ - selectionLayer.destroy(); │ │ │ │ - delete layerCache[layer.id] │ │ │ │ } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return deactivated │ │ │ │ - }, │ │ │ │ - setLayers: function(layers) { │ │ │ │ - if (this.active) { │ │ │ │ - this.deactivate(); │ │ │ │ - this.layers = layers; │ │ │ │ - this.activate() │ │ │ │ - } else { │ │ │ │ - this.layers = layers │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - createFilter: function(geometryAttribute, geometry) { │ │ │ │ - var filter = null; │ │ │ │ - if (this.handler instanceof OpenLayers.Handler.RegularPolygon) { │ │ │ │ - if (this.handler.irregular === true) { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - value: geometry.getBounds() │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - value: geometry │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } else if (this.handler instanceof OpenLayers.Handler.Polygon) { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - value: geometry │ │ │ │ - }) │ │ │ │ - } else if (this.handler instanceof OpenLayers.Handler.Path) { │ │ │ │ - if (geometryAttribute.type.indexOf("Point") >= 0) { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.DWITHIN, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - distance: this.map.getExtent().getWidth() * .01, │ │ │ │ - distanceUnits: this.map.getUnits(), │ │ │ │ - value: geometry │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - value: geometry │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } else if (this.handler instanceof OpenLayers.Handler.Click) { │ │ │ │ - if (geometryAttribute.type.indexOf("Polygon") >= 0) { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - value: geometry │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: OpenLayers.Filter.Spatial.DWITHIN, │ │ │ │ - property: geometryAttribute.name, │ │ │ │ - distance: this.map.getExtent().getWidth() * .01, │ │ │ │ - distanceUnits: this.map.getUnits(), │ │ │ │ - value: geometry │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return filter │ │ │ │ - }, │ │ │ │ - select: function(geometry) { │ │ │ │ - this._queue = function() { │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ - var layer = this.layers[i]; │ │ │ │ - var geometryAttributes = this.getGeometryAttributes(layer); │ │ │ │ - var filters = []; │ │ │ │ - for (var j = 0, lenj = geometryAttributes.length; j < lenj; j++) { │ │ │ │ - var geometryAttribute = geometryAttributes[j]; │ │ │ │ - if (geometryAttribute !== null) { │ │ │ │ - if (!(geometry instanceof OpenLayers.Geometry)) { │ │ │ │ - var point = this.map.getLonLatFromPixel(geometry.xy); │ │ │ │ - geometry = new OpenLayers.Geometry.Point(point.lon, point.lat) │ │ │ │ - } │ │ │ │ - var filter = this.createFilter(geometryAttribute, geometry); │ │ │ │ - if (filter !== null) { │ │ │ │ - filters.push(filter) │ │ │ │ + if (set) { │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry.transform(this.externalProjection, this.internalProjection) │ │ │ │ } │ │ │ │ + var feature = new OpenLayers.Feature.Vector(geometry, attributes, style); │ │ │ │ + features.push(feature) │ │ │ │ } │ │ │ │ } │ │ │ │ - var selectionLayer = this.createSelectionLayer(layer); │ │ │ │ - this.events.triggerEvent("selected", { │ │ │ │ - layer: layer, │ │ │ │ - filters: filters │ │ │ │ - }); │ │ │ │ - var sld = this.createSLD(layer, filters, geometryAttributes); │ │ │ │ - selectionLayer.mergeNewParams({ │ │ │ │ - SLD_BODY: sld │ │ │ │ - }); │ │ │ │ - delete this._queue │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - this.applySelection() │ │ │ │ - }, │ │ │ │ - applySelection: function() { │ │ │ │ - var canApply = true; │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ - if (!this.wfsCache[this.layers[i].id]) { │ │ │ │ - canApply = false; │ │ │ │ - break │ │ │ │ } │ │ │ │ } │ │ │ │ - canApply && this._queue.call(this) │ │ │ │ + return features │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.SLDSelect" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Button = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - type: OpenLayers.Control.TYPE_BUTTON, │ │ │ │ - trigger: function() {}, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Button" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.Text" │ │ │ │ }); │ │ │ │ -OpenLayers.Control.NavigationHistory = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - type: OpenLayers.Control.TYPE_TOGGLE, │ │ │ │ - previous: null, │ │ │ │ - previousOptions: null, │ │ │ │ - next: null, │ │ │ │ - nextOptions: null, │ │ │ │ - limit: 50, │ │ │ │ - autoActivate: true, │ │ │ │ - clearOnDeactivate: false, │ │ │ │ - registry: null, │ │ │ │ - nextStack: null, │ │ │ │ - previousStack: null, │ │ │ │ - listeners: null, │ │ │ │ - restoring: false, │ │ │ │ +OpenLayers.Format.EncodedPolyline = OpenLayers.Class(OpenLayers.Format, { │ │ │ │ + geometryType: "linestring", │ │ │ │ initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.registry = OpenLayers.Util.extend({ │ │ │ │ - moveend: this.getState │ │ │ │ - }, this.registry); │ │ │ │ - var previousOptions = { │ │ │ │ - trigger: OpenLayers.Function.bind(this.previousTrigger, this), │ │ │ │ - displayClass: this.displayClass + " " + this.displayClass + "Previous" │ │ │ │ - }; │ │ │ │ - OpenLayers.Util.extend(previousOptions, this.previousOptions); │ │ │ │ - this.previous = new OpenLayers.Control.Button(previousOptions); │ │ │ │ - var nextOptions = { │ │ │ │ - trigger: OpenLayers.Function.bind(this.nextTrigger, this), │ │ │ │ - displayClass: this.displayClass + " " + this.displayClass + "Next" │ │ │ │ - }; │ │ │ │ - OpenLayers.Util.extend(nextOptions, this.nextOptions); │ │ │ │ - this.next = new OpenLayers.Control.Button(nextOptions); │ │ │ │ - this.clear() │ │ │ │ + OpenLayers.Format.prototype.initialize.apply(this, [options]) │ │ │ │ }, │ │ │ │ - onPreviousChange: function(state, length) { │ │ │ │ - if (state && !this.previous.active) { │ │ │ │ - this.previous.activate() │ │ │ │ - } else if (!state && this.previous.active) { │ │ │ │ - this.previous.deactivate() │ │ │ │ + read: function(encoded) { │ │ │ │ + var geomType; │ │ │ │ + if (this.geometryType == "linestring") geomType = OpenLayers.Geometry.LineString; │ │ │ │ + else if (this.geometryType == "linearring") geomType = OpenLayers.Geometry.LinearRing; │ │ │ │ + else if (this.geometryType == "multipoint") geomType = OpenLayers.Geometry.MultiPoint; │ │ │ │ + else if (this.geometryType != "point" && this.geometryType != "polygon") return null; │ │ │ │ + var flatPoints = this.decodeDeltas(encoded, 2); │ │ │ │ + var flatPointsLength = flatPoints.length; │ │ │ │ + var pointGeometries = []; │ │ │ │ + for (var i = 0; i + 1 < flatPointsLength;) { │ │ │ │ + var y = flatPoints[i++], │ │ │ │ + x = flatPoints[i++]; │ │ │ │ + pointGeometries.push(new OpenLayers.Geometry.Point(x, y)) │ │ │ │ } │ │ │ │ + if (this.geometryType == "point") return new OpenLayers.Feature.Vector(pointGeometries[0]); │ │ │ │ + if (this.geometryType == "polygon") return new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(pointGeometries)])); │ │ │ │ + return new OpenLayers.Feature.Vector(new geomType(pointGeometries)) │ │ │ │ }, │ │ │ │ - onNextChange: function(state, length) { │ │ │ │ - if (state && !this.next.active) { │ │ │ │ - this.next.activate() │ │ │ │ - } else if (!state && this.next.active) { │ │ │ │ - this.next.deactivate() │ │ │ │ + decode: function(encoded, dims, opt_factor) { │ │ │ │ + var factor = opt_factor || 1e5; │ │ │ │ + var flatPoints = this.decodeDeltas(encoded, dims, factor); │ │ │ │ + var flatPointsLength = flatPoints.length; │ │ │ │ + var points = []; │ │ │ │ + for (var i = 0; i + (dims - 1) < flatPointsLength;) { │ │ │ │ + var point = []; │ │ │ │ + for (var dim = 0; dim < dims; ++dim) { │ │ │ │ + point.push(flatPoints[i++]) │ │ │ │ + } │ │ │ │ + points.push(point) │ │ │ │ } │ │ │ │ + return points │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this); │ │ │ │ - this.previous.destroy(); │ │ │ │ - this.next.destroy(); │ │ │ │ - this.deactivate(); │ │ │ │ - for (var prop in this) { │ │ │ │ - this[prop] = null │ │ │ │ + write: function(features) { │ │ │ │ + var feature; │ │ │ │ + if (features.constructor == Array) feature = features[0]; │ │ │ │ + else feature = features; │ │ │ │ + var geometry = feature.geometry; │ │ │ │ + var type = geometry.CLASS_NAME.split(".")[2].toLowerCase(); │ │ │ │ + var pointGeometries; │ │ │ │ + if (type == "point") pointGeometries = new Array(geometry); │ │ │ │ + else if (type == "linestring" || type == "linearring" || type == "multipoint") pointGeometries = geometry.components; │ │ │ │ + else if (type == "polygon") pointGeometries = geometry.components[0].components; │ │ │ │ + else return null; │ │ │ │ + var flatPoints = []; │ │ │ │ + var pointGeometriesLength = pointGeometries.length; │ │ │ │ + for (var i = 0; i < pointGeometriesLength; ++i) { │ │ │ │ + var pointGeometry = pointGeometries[i]; │ │ │ │ + flatPoints.push(pointGeometry.y); │ │ │ │ + flatPoints.push(pointGeometry.x) │ │ │ │ } │ │ │ │ + return this.encodeDeltas(flatPoints, 2) │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - this.map = map; │ │ │ │ - this.next.setMap(map); │ │ │ │ - this.previous.setMap(map) │ │ │ │ - }, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - this.next.draw(); │ │ │ │ - this.previous.draw() │ │ │ │ - }, │ │ │ │ - previousTrigger: function() { │ │ │ │ - var current = this.previousStack.shift(); │ │ │ │ - var state = this.previousStack.shift(); │ │ │ │ - if (state != undefined) { │ │ │ │ - this.nextStack.unshift(current); │ │ │ │ - this.previousStack.unshift(state); │ │ │ │ - this.restoring = true; │ │ │ │ - this.restore(state); │ │ │ │ - this.restoring = false; │ │ │ │ - this.onNextChange(this.nextStack[0], this.nextStack.length); │ │ │ │ - this.onPreviousChange(this.previousStack[1], this.previousStack.length - 1) │ │ │ │ - } else { │ │ │ │ - this.previousStack.unshift(current) │ │ │ │ + encode: function(points, dims, opt_factor) { │ │ │ │ + var factor = opt_factor || 1e5; │ │ │ │ + var flatPoints = []; │ │ │ │ + var pointsLength = points.length; │ │ │ │ + for (var i = 0; i < pointsLength; ++i) { │ │ │ │ + var point = points[i]; │ │ │ │ + for (var dim = 0; dim < dims; ++dim) { │ │ │ │ + flatPoints.push(point[dim]) │ │ │ │ + } │ │ │ │ } │ │ │ │ - return state │ │ │ │ + return this.encodeDeltas(flatPoints, dims, factor) │ │ │ │ }, │ │ │ │ - nextTrigger: function() { │ │ │ │ - var state = this.nextStack.shift(); │ │ │ │ - if (state != undefined) { │ │ │ │ - this.previousStack.unshift(state); │ │ │ │ - this.restoring = true; │ │ │ │ - this.restore(state); │ │ │ │ - this.restoring = false; │ │ │ │ - this.onNextChange(this.nextStack[0], this.nextStack.length); │ │ │ │ - this.onPreviousChange(this.previousStack[1], this.previousStack.length - 1) │ │ │ │ + encodeDeltas: function(numbers, dimension, opt_factor) { │ │ │ │ + var factor = opt_factor || 1e5; │ │ │ │ + var d; │ │ │ │ + var lastNumbers = new Array(dimension); │ │ │ │ + for (d = 0; d < dimension; ++d) { │ │ │ │ + lastNumbers[d] = 0 │ │ │ │ } │ │ │ │ - return state │ │ │ │ - }, │ │ │ │ - clear: function() { │ │ │ │ - this.previousStack = []; │ │ │ │ - this.previous.deactivate(); │ │ │ │ - this.nextStack = []; │ │ │ │ - this.next.deactivate() │ │ │ │ - }, │ │ │ │ - getState: function() { │ │ │ │ - return { │ │ │ │ - center: this.map.getCenter(), │ │ │ │ - resolution: this.map.getResolution(), │ │ │ │ - projection: this.map.getProjectionObject(), │ │ │ │ - units: this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength;) { │ │ │ │ + for (d = 0; d < dimension; ++d, ++i) { │ │ │ │ + var num = numbers[i]; │ │ │ │ + var delta = num - lastNumbers[d]; │ │ │ │ + lastNumbers[d] = num; │ │ │ │ + numbers[i] = delta │ │ │ │ + } │ │ │ │ } │ │ │ │ + return this.encodeFloats(numbers, factor) │ │ │ │ }, │ │ │ │ - restore: function(state) { │ │ │ │ - var center, zoom; │ │ │ │ - if (this.map.getProjectionObject() == state.projection) { │ │ │ │ - zoom = this.map.getZoomForResolution(state.resolution); │ │ │ │ - center = state.center │ │ │ │ - } else { │ │ │ │ - center = state.center.clone(); │ │ │ │ - center.transform(state.projection, this.map.getProjectionObject()); │ │ │ │ - var sourceUnits = state.units; │ │ │ │ - var targetUnits = this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units; │ │ │ │ - var resolutionFactor = sourceUnits && targetUnits ? OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1; │ │ │ │ - zoom = this.map.getZoomForResolution(resolutionFactor * state.resolution) │ │ │ │ + decodeDeltas: function(encoded, dimension, opt_factor) { │ │ │ │ + var factor = opt_factor || 1e5; │ │ │ │ + var d; │ │ │ │ + var lastNumbers = new Array(dimension); │ │ │ │ + for (d = 0; d < dimension; ++d) { │ │ │ │ + lastNumbers[d] = 0 │ │ │ │ } │ │ │ │ - this.map.setCenter(center, zoom) │ │ │ │ - }, │ │ │ │ - setListeners: function() { │ │ │ │ - this.listeners = {}; │ │ │ │ - for (var type in this.registry) { │ │ │ │ - this.listeners[type] = OpenLayers.Function.bind(function() { │ │ │ │ - if (!this.restoring) { │ │ │ │ - var state = this.registry[type].apply(this, arguments); │ │ │ │ - this.previousStack.unshift(state); │ │ │ │ - if (this.previousStack.length > 1) { │ │ │ │ - this.onPreviousChange(this.previousStack[1], this.previousStack.length - 1) │ │ │ │ - } │ │ │ │ - if (this.previousStack.length > this.limit + 1) { │ │ │ │ - this.previousStack.pop() │ │ │ │ - } │ │ │ │ - if (this.nextStack.length > 0) { │ │ │ │ - this.nextStack = []; │ │ │ │ - this.onNextChange(null, 0) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - return true │ │ │ │ - }, this) │ │ │ │ + var numbers = this.decodeFloats(encoded, factor); │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength;) { │ │ │ │ + for (d = 0; d < dimension; ++d, ++i) { │ │ │ │ + lastNumbers[d] += numbers[i]; │ │ │ │ + numbers[i] = lastNumbers[d] │ │ │ │ + } │ │ │ │ } │ │ │ │ + return numbers │ │ │ │ }, │ │ │ │ - activate: function() { │ │ │ │ - var activated = false; │ │ │ │ - if (this.map) { │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this)) { │ │ │ │ - if (this.listeners == null) { │ │ │ │ - this.setListeners() │ │ │ │ - } │ │ │ │ - for (var type in this.listeners) { │ │ │ │ - this.map.events.register(type, this, this.listeners[type]) │ │ │ │ - } │ │ │ │ - activated = true; │ │ │ │ - if (this.previousStack.length == 0) { │ │ │ │ - this.initStack() │ │ │ │ - } │ │ │ │ - } │ │ │ │ + encodeFloats: function(numbers, opt_factor) { │ │ │ │ + var factor = opt_factor || 1e5; │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength; ++i) { │ │ │ │ + numbers[i] = Math.round(numbers[i] * factor) │ │ │ │ } │ │ │ │ - return activated │ │ │ │ + return this.encodeSignedIntegers(numbers) │ │ │ │ }, │ │ │ │ - initStack: function() { │ │ │ │ - if (this.map.getCenter()) { │ │ │ │ - this.listeners.moveend() │ │ │ │ + decodeFloats: function(encoded, opt_factor) { │ │ │ │ + var factor = opt_factor || 1e5; │ │ │ │ + var numbers = this.decodeSignedIntegers(encoded); │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength; ++i) { │ │ │ │ + numbers[i] /= factor │ │ │ │ } │ │ │ │ + return numbers │ │ │ │ }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (this.map) { │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this)) { │ │ │ │ - for (var type in this.listeners) { │ │ │ │ - this.map.events.unregister(type, this, this.listeners[type]) │ │ │ │ - } │ │ │ │ - if (this.clearOnDeactivate) { │ │ │ │ - this.clear() │ │ │ │ - } │ │ │ │ - deactivated = true │ │ │ │ + encodeSignedIntegers: function(numbers) { │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength; ++i) { │ │ │ │ + var num = numbers[i]; │ │ │ │ + var signedNum = num << 1; │ │ │ │ + if (num < 0) { │ │ │ │ + signedNum = ~signedNum │ │ │ │ } │ │ │ │ + numbers[i] = signedNum │ │ │ │ } │ │ │ │ - return deactivated │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.NavigationHistory" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Geolocate = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - geolocation: null, │ │ │ │ - available: "geolocation" in navigator, │ │ │ │ - bind: true, │ │ │ │ - watch: false, │ │ │ │ - geolocationOptions: null, │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ + return this.encodeUnsignedIntegers(numbers) │ │ │ │ }, │ │ │ │ - activate: function() { │ │ │ │ - if (this.available && !this.geolocation) { │ │ │ │ - this.geolocation = navigator.geolocation │ │ │ │ + decodeSignedIntegers: function(encoded) { │ │ │ │ + var numbers = this.decodeUnsignedIntegers(encoded); │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength; ++i) { │ │ │ │ + var num = numbers[i]; │ │ │ │ + numbers[i] = num & 1 ? ~(num >> 1) : num >> 1 │ │ │ │ } │ │ │ │ - if (!this.geolocation) { │ │ │ │ - this.events.triggerEvent("locationuncapable"); │ │ │ │ - return false │ │ │ │ + return numbers │ │ │ │ + }, │ │ │ │ + encodeUnsignedIntegers: function(numbers) { │ │ │ │ + var encoded = ""; │ │ │ │ + var numbersLength = numbers.length; │ │ │ │ + for (var i = 0; i < numbersLength; ++i) { │ │ │ │ + encoded += this.encodeUnsignedInteger(numbers[i]) │ │ │ │ } │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - if (this.watch) { │ │ │ │ - this.watchId = this.geolocation.watchPosition(OpenLayers.Function.bind(this.geolocate, this), OpenLayers.Function.bind(this.failure, this), this.geolocationOptions) │ │ │ │ + return encoded │ │ │ │ + }, │ │ │ │ + decodeUnsignedIntegers: function(encoded) { │ │ │ │ + var numbers = []; │ │ │ │ + var current = 0; │ │ │ │ + var shift = 0; │ │ │ │ + var encodedLength = encoded.length; │ │ │ │ + for (var i = 0; i < encodedLength; ++i) { │ │ │ │ + var b = encoded.charCodeAt(i) - 63; │ │ │ │ + current |= (b & 31) << shift; │ │ │ │ + if (b < 32) { │ │ │ │ + numbers.push(current); │ │ │ │ + current = 0; │ │ │ │ + shift = 0 │ │ │ │ } else { │ │ │ │ - this.getCurrentLocation() │ │ │ │ + shift += 5 │ │ │ │ } │ │ │ │ - return true │ │ │ │ } │ │ │ │ - return false │ │ │ │ + return numbers │ │ │ │ }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active && this.watchId !== null) { │ │ │ │ - this.geolocation.clearWatch(this.watchId) │ │ │ │ - } │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply(this, arguments) │ │ │ │ + encodeFloat: function(num, opt_factor) { │ │ │ │ + num = Math.round(num * (opt_factor || 1e5)); │ │ │ │ + return this.encodeSignedInteger(num) │ │ │ │ }, │ │ │ │ - geolocate: function(position) { │ │ │ │ - var center = new OpenLayers.LonLat(position.coords.longitude, position.coords.latitude).transform(new OpenLayers.Projection("EPSG:4326"), this.map.getProjectionObject()); │ │ │ │ - if (this.bind) { │ │ │ │ - this.map.setCenter(center) │ │ │ │ + decodeFloat: function(encoded, opt_factor) { │ │ │ │ + var result = this.decodeSignedInteger(encoded); │ │ │ │ + return result / (opt_factor || 1e5) │ │ │ │ + }, │ │ │ │ + encodeSignedInteger: function(num) { │ │ │ │ + var signedNum = num << 1; │ │ │ │ + if (num < 0) { │ │ │ │ + signedNum = ~signedNum │ │ │ │ } │ │ │ │ - this.events.triggerEvent("locationupdated", { │ │ │ │ - position: position, │ │ │ │ - point: new OpenLayers.Geometry.Point(center.lon, center.lat) │ │ │ │ - }) │ │ │ │ + return this.encodeUnsignedInteger(signedNum) │ │ │ │ }, │ │ │ │ - getCurrentLocation: function() { │ │ │ │ - if (!this.active || this.watch) { │ │ │ │ - return false │ │ │ │ + decodeSignedInteger: function(encoded) { │ │ │ │ + var result = this.decodeUnsignedInteger(encoded); │ │ │ │ + return result & 1 ? ~(result >> 1) : result >> 1 │ │ │ │ + }, │ │ │ │ + encodeUnsignedInteger: function(num) { │ │ │ │ + var value, encoded = ""; │ │ │ │ + while (num >= 32) { │ │ │ │ + value = (32 | num & 31) + 63; │ │ │ │ + encoded += String.fromCharCode(value); │ │ │ │ + num >>= 5 │ │ │ │ } │ │ │ │ - this.geolocation.getCurrentPosition(OpenLayers.Function.bind(this.geolocate, this), OpenLayers.Function.bind(this.failure, this), this.geolocationOptions); │ │ │ │ - return true │ │ │ │ + value = num + 63; │ │ │ │ + encoded += String.fromCharCode(value); │ │ │ │ + return encoded │ │ │ │ }, │ │ │ │ - failure: function(error) { │ │ │ │ - this.events.triggerEvent("locationfailed", { │ │ │ │ - error: error │ │ │ │ - }) │ │ │ │ + decodeUnsignedInteger: function(encoded) { │ │ │ │ + var result = 0; │ │ │ │ + var shift = 0; │ │ │ │ + var encodedLength = encoded.length; │ │ │ │ + for (var i = 0; i < encodedLength; ++i) { │ │ │ │ + var b = encoded.charCodeAt(i) - 63; │ │ │ │ + result |= (b & 31) << shift; │ │ │ │ + if (b < 32) break; │ │ │ │ + shift += 5 │ │ │ │ + } │ │ │ │ + return result │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Geolocate" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.EncodedPolyline" │ │ │ │ }); │ │ │ │ -OpenLayers.Control.PanZoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - slideFactor: 50, │ │ │ │ - slideRatio: null, │ │ │ │ - buttons: null, │ │ │ │ - position: null, │ │ │ │ +OpenLayers.Format.ArcXML = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + fontStyleKeys: ["antialiasing", "blockout", "font", "fontcolor", "fontsize", "fontstyle", "glowing", "interval", "outline", "printmode", "shadow", "transparency"], │ │ │ │ + request: null, │ │ │ │ + response: null, │ │ │ │ initialize: function(options) { │ │ │ │ - this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoom.X, OpenLayers.Control.PanZoom.Y); │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.unregister("buttonclick", this, this.onButtonClick) │ │ │ │ + this.request = new OpenLayers.Format.ArcXML.Request; │ │ │ │ + this.response = new OpenLayers.Format.ArcXML.Response; │ │ │ │ + if (options) { │ │ │ │ + if (options.requesttype == "feature") { │ │ │ │ + this.request.get_image = null; │ │ │ │ + var qry = this.request.get_feature.query; │ │ │ │ + this.addCoordSys(qry.featurecoordsys, options.featureCoordSys); │ │ │ │ + this.addCoordSys(qry.filtercoordsys, options.filterCoordSys); │ │ │ │ + if (options.polygon) { │ │ │ │ + qry.isspatial = true; │ │ │ │ + qry.spatialfilter.polygon = options.polygon │ │ │ │ + } else if (options.envelope) { │ │ │ │ + qry.isspatial = true; │ │ │ │ + qry.spatialfilter.envelope = { │ │ │ │ + minx: 0, │ │ │ │ + miny: 0, │ │ │ │ + maxx: 0, │ │ │ │ + maxy: 0 │ │ │ │ + }; │ │ │ │ + this.parseEnvelope(qry.spatialfilter.envelope, options.envelope) │ │ │ │ + } │ │ │ │ + } else if (options.requesttype == "image") { │ │ │ │ + this.request.get_feature = null; │ │ │ │ + var props = this.request.get_image.properties; │ │ │ │ + this.parseEnvelope(props.envelope, options.envelope); │ │ │ │ + this.addLayers(props.layerlist, options.layers); │ │ │ │ + this.addImageSize(props.imagesize, options.tileSize); │ │ │ │ + this.addCoordSys(props.featurecoordsys, options.featureCoordSys); │ │ │ │ + this.addCoordSys(props.filtercoordsys, options.filterCoordSys) │ │ │ │ + } else { │ │ │ │ + this.request = null │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.removeButtons(); │ │ │ │ - this.buttons = null; │ │ │ │ - this.position = null; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - this.map.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ - }, │ │ │ │ - draw: function(px) { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - px = this.position; │ │ │ │ - this.buttons = []; │ │ │ │ - var sz = { │ │ │ │ - w: 18, │ │ │ │ - h: 18 │ │ │ │ - }; │ │ │ │ - var centered = new OpenLayers.Pixel(px.x + sz.w / 2, px.y); │ │ │ │ - this._addButton("panup", "north-mini.png", centered, sz); │ │ │ │ - px.y = centered.y + sz.h; │ │ │ │ - this._addButton("panleft", "west-mini.png", px, sz); │ │ │ │ - this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz); │ │ │ │ - this._addButton("pandown", "south-mini.png", centered.add(0, sz.h * 2), sz); │ │ │ │ - this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h * 3 + 5), sz); │ │ │ │ - this._addButton("zoomworld", "zoom-world-mini.png", centered.add(0, sz.h * 4 + 5), sz); │ │ │ │ - this._addButton("zoomout", "zoom-minus-mini.png", centered.add(0, sz.h * 5 + 5), sz); │ │ │ │ - return this.div │ │ │ │ - }, │ │ │ │ - _addButton: function(id, img, xy, sz) { │ │ │ │ - var imgLocation = OpenLayers.Util.getImageLocation(img); │ │ │ │ - var btn = OpenLayers.Util.createAlphaImageDiv(this.id + "_" + id, xy, sz, imgLocation, "absolute"); │ │ │ │ - btn.style.cursor = "pointer"; │ │ │ │ - this.div.appendChild(btn); │ │ │ │ - btn.action = id; │ │ │ │ - btn.className = "olButton"; │ │ │ │ - this.buttons.push(btn); │ │ │ │ - return btn │ │ │ │ - }, │ │ │ │ - _removeButton: function(btn) { │ │ │ │ - this.div.removeChild(btn); │ │ │ │ - OpenLayers.Util.removeItem(this.buttons, btn) │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]) │ │ │ │ }, │ │ │ │ - removeButtons: function() { │ │ │ │ - for (var i = this.buttons.length - 1; i >= 0; --i) { │ │ │ │ - this._removeButton(this.buttons[i]) │ │ │ │ + parseEnvelope: function(env, arr) { │ │ │ │ + if (arr && arr.length == 4) { │ │ │ │ + env.minx = arr[0]; │ │ │ │ + env.miny = arr[1]; │ │ │ │ + env.maxx = arr[2]; │ │ │ │ + env.maxy = arr[3] │ │ │ │ } │ │ │ │ }, │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - var btn = evt.buttonElement; │ │ │ │ - switch (btn.action) { │ │ │ │ - case "panup": │ │ │ │ - this.map.pan(0, -this.getSlideFactor("h")); │ │ │ │ - break; │ │ │ │ - case "pandown": │ │ │ │ - this.map.pan(0, this.getSlideFactor("h")); │ │ │ │ - break; │ │ │ │ - case "panleft": │ │ │ │ - this.map.pan(-this.getSlideFactor("w"), 0); │ │ │ │ - break; │ │ │ │ - case "panright": │ │ │ │ - this.map.pan(this.getSlideFactor("w"), 0); │ │ │ │ - break; │ │ │ │ - case "zoomin": │ │ │ │ - this.map.zoomIn(); │ │ │ │ - break; │ │ │ │ - case "zoomout": │ │ │ │ - this.map.zoomOut(); │ │ │ │ - break; │ │ │ │ - case "zoomworld": │ │ │ │ - this.map.zoomToMaxExtent(); │ │ │ │ - break │ │ │ │ + addLayers: function(ll, lyrs) { │ │ │ │ + for (var lind = 0, len = lyrs.length; lind < len; lind++) { │ │ │ │ + ll.push(lyrs[lind]) │ │ │ │ } │ │ │ │ }, │ │ │ │ - getSlideFactor: function(dim) { │ │ │ │ - return this.slideRatio ? this.map.getSize()[dim] * this.slideRatio : this.slideFactor │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.PanZoom" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.PanZoom.X = 4; │ │ │ │ -OpenLayers.Control.PanZoom.Y = 4; │ │ │ │ -OpenLayers.Control.PanZoomBar = OpenLayers.Class(OpenLayers.Control.PanZoom, { │ │ │ │ - zoomStopWidth: 18, │ │ │ │ - zoomStopHeight: 11, │ │ │ │ - slider: null, │ │ │ │ - sliderEvents: null, │ │ │ │ - zoombarDiv: null, │ │ │ │ - zoomWorldIcon: false, │ │ │ │ - panIcons: true, │ │ │ │ - forceFixedZoomLevel: false, │ │ │ │ - mouseDragStart: null, │ │ │ │ - deltaY: null, │ │ │ │ - zoomStart: null, │ │ │ │ - destroy: function() { │ │ │ │ - this._removeZoomBar(); │ │ │ │ - this.map.events.un({ │ │ │ │ - changebaselayer: this.redraw, │ │ │ │ - updatesize: this.redraw, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments); │ │ │ │ - delete this.mouseDragStart; │ │ │ │ - delete this.zoomStart │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.PanZoom.prototype.setMap.apply(this, arguments); │ │ │ │ - this.map.events.on({ │ │ │ │ - changebaselayer: this.redraw, │ │ │ │ - updatesize: this.redraw, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - redraw: function() { │ │ │ │ - if (this.div != null) { │ │ │ │ - this.removeButtons(); │ │ │ │ - this._removeZoomBar() │ │ │ │ + addImageSize: function(imsize, olsize) { │ │ │ │ + if (olsize !== null) { │ │ │ │ + imsize.width = olsize.w; │ │ │ │ + imsize.height = olsize.h; │ │ │ │ + imsize.printwidth = olsize.w; │ │ │ │ + imsize.printheight = olsize.h │ │ │ │ } │ │ │ │ - this.draw() │ │ │ │ }, │ │ │ │ - draw: function(px) { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - px = this.position.clone(); │ │ │ │ - this.buttons = []; │ │ │ │ - var sz = { │ │ │ │ - w: 18, │ │ │ │ - h: 18 │ │ │ │ - }; │ │ │ │ - if (this.panIcons) { │ │ │ │ - var centered = new OpenLayers.Pixel(px.x + sz.w / 2, px.y); │ │ │ │ - var wposition = sz.w; │ │ │ │ - if (this.zoomWorldIcon) { │ │ │ │ - centered = new OpenLayers.Pixel(px.x + sz.w, px.y) │ │ │ │ - } │ │ │ │ - this._addButton("panup", "north-mini.png", centered, sz); │ │ │ │ - px.y = centered.y + sz.h; │ │ │ │ - this._addButton("panleft", "west-mini.png", px, sz); │ │ │ │ - if (this.zoomWorldIcon) { │ │ │ │ - this._addButton("zoomworld", "zoom-world-mini.png", px.add(sz.w, 0), sz); │ │ │ │ - wposition *= 2 │ │ │ │ - } │ │ │ │ - this._addButton("panright", "east-mini.png", px.add(wposition, 0), sz); │ │ │ │ - this._addButton("pandown", "south-mini.png", centered.add(0, sz.h * 2), sz); │ │ │ │ - this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h * 3 + 5), sz); │ │ │ │ - centered = this._addZoomBar(centered.add(0, sz.h * 4 + 5)); │ │ │ │ - this._addButton("zoomout", "zoom-minus-mini.png", centered, sz) │ │ │ │ + addCoordSys: function(featOrFilt, fsys) { │ │ │ │ + if (typeof fsys == "string") { │ │ │ │ + featOrFilt.id = parseInt(fsys); │ │ │ │ + featOrFilt.string = fsys │ │ │ │ + } else if (typeof fsys == "object" && fsys.proj !== null) { │ │ │ │ + featOrFilt.id = fsys.proj.srsProjNumber; │ │ │ │ + featOrFilt.string = fsys.proj.srsCode │ │ │ │ } else { │ │ │ │ - this._addButton("zoomin", "zoom-plus-mini.png", px, sz); │ │ │ │ - centered = this._addZoomBar(px.add(0, sz.h)); │ │ │ │ - this._addButton("zoomout", "zoom-minus-mini.png", centered, sz); │ │ │ │ - if (this.zoomWorldIcon) { │ │ │ │ - centered = centered.add(0, sz.h + 3); │ │ │ │ - this._addButton("zoomworld", "zoom-world-mini.png", centered, sz) │ │ │ │ - } │ │ │ │ + featOrFilt = fsys │ │ │ │ } │ │ │ │ - return this.div │ │ │ │ }, │ │ │ │ - _addZoomBar: function(centered) { │ │ │ │ - var imgLocation = OpenLayers.Util.getImageLocation("slider.png"); │ │ │ │ - var id = this.id + "_" + this.map.id; │ │ │ │ - var minZoom = this.map.getMinZoom(); │ │ │ │ - var zoomsToEnd = this.map.getNumZoomLevels() - 1 - this.map.getZoom(); │ │ │ │ - var slider = OpenLayers.Util.createAlphaImageDiv(id, centered.add(-1, zoomsToEnd * this.zoomStopHeight), { │ │ │ │ - w: 20, │ │ │ │ - h: 9 │ │ │ │ - }, imgLocation, "absolute"); │ │ │ │ - slider.style.cursor = "move"; │ │ │ │ - this.slider = slider; │ │ │ │ - this.sliderEvents = new OpenLayers.Events(this, slider, null, true, { │ │ │ │ - includeXY: true │ │ │ │ - }); │ │ │ │ - this.sliderEvents.on({ │ │ │ │ - touchstart: this.zoomBarDown, │ │ │ │ - touchmove: this.zoomBarDrag, │ │ │ │ - touchend: this.zoomBarUp, │ │ │ │ - mousedown: this.zoomBarDown, │ │ │ │ - mousemove: this.zoomBarDrag, │ │ │ │ - mouseup: this.zoomBarUp │ │ │ │ - }); │ │ │ │ - var sz = { │ │ │ │ - w: this.zoomStopWidth, │ │ │ │ - h: this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom) │ │ │ │ - }; │ │ │ │ - var imgLocation = OpenLayers.Util.getImageLocation("zoombar.png"); │ │ │ │ - var div = null; │ │ │ │ - if (OpenLayers.Util.alphaHack()) { │ │ │ │ - var id = this.id + "_" + this.map.id; │ │ │ │ - div = OpenLayers.Util.createAlphaImageDiv(id, centered, { │ │ │ │ - w: sz.w, │ │ │ │ - h: this.zoomStopHeight │ │ │ │ - }, imgLocation, "absolute", null, "crop"); │ │ │ │ - div.style.height = sz.h + "px" │ │ │ │ + iserror: function(data) { │ │ │ │ + var ret = null; │ │ │ │ + if (!data) { │ │ │ │ + ret = this.response.error !== "" │ │ │ │ } else { │ │ │ │ - div = OpenLayers.Util.createDiv("OpenLayers_Control_PanZoomBar_Zoombar" + this.map.id, centered, sz, imgLocation) │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]); │ │ │ │ + var errorNodes = data.documentElement.getElementsByTagName("ERROR"); │ │ │ │ + ret = errorNodes !== null && errorNodes.length > 0 │ │ │ │ } │ │ │ │ - div.style.cursor = "pointer"; │ │ │ │ - div.className = "olButton"; │ │ │ │ - this.zoombarDiv = div; │ │ │ │ - this.div.appendChild(div); │ │ │ │ - this.startTop = parseInt(div.style.top); │ │ │ │ - this.div.appendChild(slider); │ │ │ │ - this.map.events.register("zoomend", this, this.moveZoomBar); │ │ │ │ - centered = centered.add(0, this.zoomStopHeight * (this.map.getNumZoomLevels() - minZoom)); │ │ │ │ - return centered │ │ │ │ - }, │ │ │ │ - _removeZoomBar: function() { │ │ │ │ - this.sliderEvents.un({ │ │ │ │ - touchstart: this.zoomBarDown, │ │ │ │ - touchmove: this.zoomBarDrag, │ │ │ │ - touchend: this.zoomBarUp, │ │ │ │ - mousedown: this.zoomBarDown, │ │ │ │ - mousemove: this.zoomBarDrag, │ │ │ │ - mouseup: this.zoomBarUp │ │ │ │ - }); │ │ │ │ - this.sliderEvents.destroy(); │ │ │ │ - this.div.removeChild(this.zoombarDiv); │ │ │ │ - this.zoombarDiv = null; │ │ │ │ - this.div.removeChild(this.slider); │ │ │ │ - this.slider = null; │ │ │ │ - this.map.events.unregister("zoomend", this, this.moveZoomBar) │ │ │ │ + return ret │ │ │ │ }, │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - OpenLayers.Control.PanZoom.prototype.onButtonClick.apply(this, arguments); │ │ │ │ - if (evt.buttonElement === this.zoombarDiv) { │ │ │ │ - var levels = evt.buttonXY.y / this.zoomStopHeight; │ │ │ │ - if (this.forceFixedZoomLevel || !this.map.fractionalZoom) { │ │ │ │ - levels = Math.floor(levels) │ │ │ │ - } │ │ │ │ - var zoom = this.map.getNumZoomLevels() - 1 - levels; │ │ │ │ - zoom = Math.min(Math.max(zoom, 0), this.map.getNumZoomLevels() - 1); │ │ │ │ - this.map.zoomTo(zoom) │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - passEventToSlider: function(evt) { │ │ │ │ - this.sliderEvents.handleBrowserEvent(evt) │ │ │ │ - }, │ │ │ │ - zoomBarDown: function(evt) { │ │ │ │ - if (!OpenLayers.Event.isLeftClick(evt) && !OpenLayers.Event.isSingleTouch(evt)) { │ │ │ │ - return │ │ │ │ + var arcNode = null; │ │ │ │ + if (data && data.documentElement) { │ │ │ │ + if (data.documentElement.nodeName == "ARCXML") { │ │ │ │ + arcNode = data.documentElement │ │ │ │ + } else { │ │ │ │ + arcNode = data.documentElement.getElementsByTagName("ARCXML")[0] │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.map.events.on({ │ │ │ │ - touchmove: this.passEventToSlider, │ │ │ │ - mousemove: this.passEventToSlider, │ │ │ │ - mouseup: this.passEventToSlider, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.mouseDragStart = evt.xy.clone(); │ │ │ │ - this.zoomStart = evt.xy.clone(); │ │ │ │ - this.div.style.cursor = "move"; │ │ │ │ - this.zoombarDiv.offsets = null; │ │ │ │ - OpenLayers.Event.stop(evt) │ │ │ │ - }, │ │ │ │ - zoomBarDrag: function(evt) { │ │ │ │ - if (this.mouseDragStart != null) { │ │ │ │ - var deltaY = this.mouseDragStart.y - evt.xy.y; │ │ │ │ - var offsets = OpenLayers.Util.pagePosition(this.zoombarDiv); │ │ │ │ - if (evt.clientY - offsets[1] > 0 && evt.clientY - offsets[1] < parseInt(this.zoombarDiv.style.height) - 2) { │ │ │ │ - var newTop = parseInt(this.slider.style.top) - deltaY; │ │ │ │ - this.slider.style.top = newTop + "px"; │ │ │ │ - this.mouseDragStart = evt.xy.clone() │ │ │ │ + if (!arcNode || arcNode.firstChild.nodeName === "parsererror") { │ │ │ │ + var error, source; │ │ │ │ + try { │ │ │ │ + error = data.firstChild.nodeValue; │ │ │ │ + source = data.firstChild.childNodes[1].firstChild.nodeValue │ │ │ │ + } catch (err) {} │ │ │ │ + throw { │ │ │ │ + message: "Error parsing the ArcXML request", │ │ │ │ + error: error, │ │ │ │ + source: source │ │ │ │ } │ │ │ │ - this.deltaY = this.zoomStart.y - evt.xy.y; │ │ │ │ - OpenLayers.Event.stop(evt) │ │ │ │ } │ │ │ │ + var response = this.parseResponse(arcNode); │ │ │ │ + return response │ │ │ │ }, │ │ │ │ - zoomBarUp: function(evt) { │ │ │ │ - if (!OpenLayers.Event.isLeftClick(evt) && evt.type !== "touchend") { │ │ │ │ - return │ │ │ │ + write: function(request) { │ │ │ │ + if (!request) { │ │ │ │ + request = this.request │ │ │ │ } │ │ │ │ - if (this.mouseDragStart) { │ │ │ │ - this.div.style.cursor = ""; │ │ │ │ - this.map.events.un({ │ │ │ │ - touchmove: this.passEventToSlider, │ │ │ │ - mouseup: this.passEventToSlider, │ │ │ │ - mousemove: this.passEventToSlider, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - var zoomLevel = this.map.zoom; │ │ │ │ - if (!this.forceFixedZoomLevel && this.map.fractionalZoom) { │ │ │ │ - zoomLevel += this.deltaY / this.zoomStopHeight; │ │ │ │ - zoomLevel = Math.min(Math.max(zoomLevel, 0), this.map.getNumZoomLevels() - 1) │ │ │ │ + var root = this.createElementNS("", "ARCXML"); │ │ │ │ + root.setAttribute("version", "1.1"); │ │ │ │ + var reqElem = this.createElementNS("", "REQUEST"); │ │ │ │ + if (request.get_image != null) { │ │ │ │ + var getElem = this.createElementNS("", "GET_IMAGE"); │ │ │ │ + reqElem.appendChild(getElem); │ │ │ │ + var propElem = this.createElementNS("", "PROPERTIES"); │ │ │ │ + getElem.appendChild(propElem); │ │ │ │ + var props = request.get_image.properties; │ │ │ │ + if (props.featurecoordsys != null) { │ │ │ │ + var feat = this.createElementNS("", "FEATURECOORDSYS"); │ │ │ │ + propElem.appendChild(feat); │ │ │ │ + if (props.featurecoordsys.id === 0) { │ │ │ │ + feat.setAttribute("string", props.featurecoordsys["string"]) │ │ │ │ + } else { │ │ │ │ + feat.setAttribute("id", props.featurecoordsys.id) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (props.filtercoordsys != null) { │ │ │ │ + var filt = this.createElementNS("", "FILTERCOORDSYS"); │ │ │ │ + propElem.appendChild(filt); │ │ │ │ + if (props.filtercoordsys.id === 0) { │ │ │ │ + filt.setAttribute("string", props.filtercoordsys.string) │ │ │ │ + } else { │ │ │ │ + filt.setAttribute("id", props.filtercoordsys.id) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (props.envelope != null) { │ │ │ │ + var env = this.createElementNS("", "ENVELOPE"); │ │ │ │ + propElem.appendChild(env); │ │ │ │ + env.setAttribute("minx", props.envelope.minx); │ │ │ │ + env.setAttribute("miny", props.envelope.miny); │ │ │ │ + env.setAttribute("maxx", props.envelope.maxx); │ │ │ │ + env.setAttribute("maxy", props.envelope.maxy) │ │ │ │ + } │ │ │ │ + var imagesz = this.createElementNS("", "IMAGESIZE"); │ │ │ │ + propElem.appendChild(imagesz); │ │ │ │ + imagesz.setAttribute("height", props.imagesize.height); │ │ │ │ + imagesz.setAttribute("width", props.imagesize.width); │ │ │ │ + if (props.imagesize.height != props.imagesize.printheight || props.imagesize.width != props.imagesize.printwidth) { │ │ │ │ + imagesz.setAttribute("printheight", props.imagesize.printheight); │ │ │ │ + imagesz.setArrtibute("printwidth", props.imagesize.printwidth) │ │ │ │ + } │ │ │ │ + if (props.background != null) { │ │ │ │ + var backgrnd = this.createElementNS("", "BACKGROUND"); │ │ │ │ + propElem.appendChild(backgrnd); │ │ │ │ + backgrnd.setAttribute("color", props.background.color.r + "," + props.background.color.g + "," + props.background.color.b); │ │ │ │ + if (props.background.transcolor !== null) { │ │ │ │ + backgrnd.setAttribute("transcolor", props.background.transcolor.r + "," + props.background.transcolor.g + "," + props.background.transcolor.b) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (props.layerlist != null && props.layerlist.length > 0) { │ │ │ │ + var layerlst = this.createElementNS("", "LAYERLIST"); │ │ │ │ + propElem.appendChild(layerlst); │ │ │ │ + for (var ld = 0; ld < props.layerlist.length; ld++) { │ │ │ │ + var ldef = this.createElementNS("", "LAYERDEF"); │ │ │ │ + layerlst.appendChild(ldef); │ │ │ │ + ldef.setAttribute("id", props.layerlist[ld].id); │ │ │ │ + ldef.setAttribute("visible", props.layerlist[ld].visible); │ │ │ │ + if (typeof props.layerlist[ld].query == "object") { │ │ │ │ + var query = props.layerlist[ld].query; │ │ │ │ + if (query.where.length < 0) { │ │ │ │ + continue │ │ │ │ + } │ │ │ │ + var queryElem = null; │ │ │ │ + if (typeof query.spatialfilter == "boolean" && query.spatialfilter) { │ │ │ │ + queryElem = this.createElementNS("", "SPATIALQUERY") │ │ │ │ + } else { │ │ │ │ + queryElem = this.createElementNS("", "QUERY") │ │ │ │ + } │ │ │ │ + queryElem.setAttribute("where", query.where); │ │ │ │ + if (typeof query.accuracy == "number" && query.accuracy > 0) { │ │ │ │ + queryElem.setAttribute("accuracy", query.accuracy) │ │ │ │ + } │ │ │ │ + if (typeof query.featurelimit == "number" && query.featurelimit < 2e3) { │ │ │ │ + queryElem.setAttribute("featurelimit", query.featurelimit) │ │ │ │ + } │ │ │ │ + if (typeof query.subfields == "string" && query.subfields != "#ALL#") { │ │ │ │ + queryElem.setAttribute("subfields", query.subfields) │ │ │ │ + } │ │ │ │ + if (typeof query.joinexpression == "string" && query.joinexpression.length > 0) { │ │ │ │ + queryElem.setAttribute("joinexpression", query.joinexpression) │ │ │ │ + } │ │ │ │ + if (typeof query.jointables == "string" && query.jointables.length > 0) { │ │ │ │ + queryElem.setAttribute("jointables", query.jointables) │ │ │ │ + } │ │ │ │ + ldef.appendChild(queryElem) │ │ │ │ + } │ │ │ │ + if (typeof props.layerlist[ld].renderer == "object") { │ │ │ │ + this.addRenderer(ldef, props.layerlist[ld].renderer) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else if (request.get_feature != null) { │ │ │ │ + var getElem = this.createElementNS("", "GET_FEATURES"); │ │ │ │ + getElem.setAttribute("outputmode", "newxml"); │ │ │ │ + getElem.setAttribute("checkesc", "true"); │ │ │ │ + if (request.get_feature.geometry) { │ │ │ │ + getElem.setAttribute("geometry", request.get_feature.geometry) │ │ │ │ } else { │ │ │ │ - zoomLevel += this.deltaY / this.zoomStopHeight; │ │ │ │ - zoomLevel = Math.max(Math.round(zoomLevel), 0) │ │ │ │ + getElem.setAttribute("geometry", "false") │ │ │ │ + } │ │ │ │ + if (request.get_feature.compact) { │ │ │ │ + getElem.setAttribute("compact", request.get_feature.compact) │ │ │ │ + } │ │ │ │ + if (request.get_feature.featurelimit == "number") { │ │ │ │ + getElem.setAttribute("featurelimit", request.get_feature.featurelimit) │ │ │ │ + } │ │ │ │ + getElem.setAttribute("globalenvelope", "true"); │ │ │ │ + reqElem.appendChild(getElem); │ │ │ │ + if (request.get_feature.layer != null && request.get_feature.layer.length > 0) { │ │ │ │ + var lyrElem = this.createElementNS("", "LAYER"); │ │ │ │ + lyrElem.setAttribute("id", request.get_feature.layer); │ │ │ │ + getElem.appendChild(lyrElem) │ │ │ │ + } │ │ │ │ + var fquery = request.get_feature.query; │ │ │ │ + if (fquery != null) { │ │ │ │ + var qElem = null; │ │ │ │ + if (fquery.isspatial) { │ │ │ │ + qElem = this.createElementNS("", "SPATIALQUERY") │ │ │ │ + } else { │ │ │ │ + qElem = this.createElementNS("", "QUERY") │ │ │ │ + } │ │ │ │ + getElem.appendChild(qElem); │ │ │ │ + if (typeof fquery.accuracy == "number") { │ │ │ │ + qElem.setAttribute("accuracy", fquery.accuracy) │ │ │ │ + } │ │ │ │ + if (fquery.featurecoordsys != null) { │ │ │ │ + var fcsElem1 = this.createElementNS("", "FEATURECOORDSYS"); │ │ │ │ + if (fquery.featurecoordsys.id == 0) { │ │ │ │ + fcsElem1.setAttribute("string", fquery.featurecoordsys.string) │ │ │ │ + } else { │ │ │ │ + fcsElem1.setAttribute("id", fquery.featurecoordsys.id) │ │ │ │ + } │ │ │ │ + qElem.appendChild(fcsElem1) │ │ │ │ + } │ │ │ │ + if (fquery.filtercoordsys != null) { │ │ │ │ + var fcsElem2 = this.createElementNS("", "FILTERCOORDSYS"); │ │ │ │ + if (fquery.filtercoordsys.id === 0) { │ │ │ │ + fcsElem2.setAttribute("string", fquery.filtercoordsys.string) │ │ │ │ + } else { │ │ │ │ + fcsElem2.setAttribute("id", fquery.filtercoordsys.id) │ │ │ │ + } │ │ │ │ + qElem.appendChild(fcsElem2) │ │ │ │ + } │ │ │ │ + if (fquery.buffer > 0) { │ │ │ │ + var bufElem = this.createElementNS("", "BUFFER"); │ │ │ │ + bufElem.setAttribute("distance", fquery.buffer); │ │ │ │ + qElem.appendChild(bufElem) │ │ │ │ + } │ │ │ │ + if (fquery.isspatial) { │ │ │ │ + var spfElem = this.createElementNS("", "SPATIALFILTER"); │ │ │ │ + spfElem.setAttribute("relation", fquery.spatialfilter.relation); │ │ │ │ + qElem.appendChild(spfElem); │ │ │ │ + if (fquery.spatialfilter.envelope) { │ │ │ │ + var envElem = this.createElementNS("", "ENVELOPE"); │ │ │ │ + envElem.setAttribute("minx", fquery.spatialfilter.envelope.minx); │ │ │ │ + envElem.setAttribute("miny", fquery.spatialfilter.envelope.miny); │ │ │ │ + envElem.setAttribute("maxx", fquery.spatialfilter.envelope.maxx); │ │ │ │ + envElem.setAttribute("maxy", fquery.spatialfilter.envelope.maxy); │ │ │ │ + spfElem.appendChild(envElem) │ │ │ │ + } else if (typeof fquery.spatialfilter.polygon == "object") { │ │ │ │ + spfElem.appendChild(this.writePolygonGeometry(fquery.spatialfilter.polygon)) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (fquery.where != null && fquery.where.length > 0) { │ │ │ │ + qElem.setAttribute("where", fquery.where) │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.map.zoomTo(zoomLevel); │ │ │ │ - this.mouseDragStart = null; │ │ │ │ - this.zoomStart = null; │ │ │ │ - this.deltaY = 0; │ │ │ │ - OpenLayers.Event.stop(evt) │ │ │ │ } │ │ │ │ + root.appendChild(reqElem); │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [root]) │ │ │ │ }, │ │ │ │ - moveZoomBar: function() { │ │ │ │ - var newTop = (this.map.getNumZoomLevels() - 1 - this.map.getZoom()) * this.zoomStopHeight + this.startTop + 1; │ │ │ │ - this.slider.style.top = newTop + "px" │ │ │ │ + addGroupRenderer: function(ldef, toprenderer) { │ │ │ │ + var topRelem = this.createElementNS("", "GROUPRENDERER"); │ │ │ │ + ldef.appendChild(topRelem); │ │ │ │ + for (var rind = 0; rind < toprenderer.length; rind++) { │ │ │ │ + var renderer = toprenderer[rind]; │ │ │ │ + this.addRenderer(topRelem, renderer) │ │ │ │ + } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.PanZoomBar" │ │ │ │ -}); │ │ │ │ -OpenLayers.Handler.Pinch = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - started: false, │ │ │ │ - stopDown: false, │ │ │ │ - pinching: false, │ │ │ │ - last: null, │ │ │ │ - start: null, │ │ │ │ - touchstart: function(evt) { │ │ │ │ - var propagate = true; │ │ │ │ - this.pinching = false; │ │ │ │ - if (OpenLayers.Event.isMultiTouch(evt)) { │ │ │ │ - this.started = true; │ │ │ │ - this.last = this.start = { │ │ │ │ - distance: this.getDistance(evt.touches), │ │ │ │ - delta: 0, │ │ │ │ - scale: 1 │ │ │ │ - }; │ │ │ │ - this.callback("start", [evt, this.start]); │ │ │ │ - propagate = !this.stopDown │ │ │ │ - } else if (this.started) { │ │ │ │ - return false │ │ │ │ + addRenderer: function(topRelem, renderer) { │ │ │ │ + if (OpenLayers.Util.isArray(renderer)) { │ │ │ │ + this.addGroupRenderer(topRelem, renderer) │ │ │ │ } else { │ │ │ │ - this.started = false; │ │ │ │ - this.start = null; │ │ │ │ - this.last = null │ │ │ │ + var renderElem = this.createElementNS("", renderer.type.toUpperCase() + "RENDERER"); │ │ │ │ + topRelem.appendChild(renderElem); │ │ │ │ + if (renderElem.tagName == "VALUEMAPRENDERER") { │ │ │ │ + this.addValueMapRenderer(renderElem, renderer) │ │ │ │ + } else if (renderElem.tagName == "VALUEMAPLABELRENDERER") { │ │ │ │ + this.addValueMapLabelRenderer(renderElem, renderer) │ │ │ │ + } else if (renderElem.tagName == "SIMPLELABELRENDERER") { │ │ │ │ + this.addSimpleLabelRenderer(renderElem, renderer) │ │ │ │ + } else if (renderElem.tagName == "SCALEDEPENDENTRENDERER") { │ │ │ │ + this.addScaleDependentRenderer(renderElem, renderer) │ │ │ │ + } │ │ │ │ } │ │ │ │ - OpenLayers.Event.preventDefault(evt); │ │ │ │ - return propagate │ │ │ │ }, │ │ │ │ - touchmove: function(evt) { │ │ │ │ - if (this.started && OpenLayers.Event.isMultiTouch(evt)) { │ │ │ │ - this.pinching = true; │ │ │ │ - var current = this.getPinchData(evt); │ │ │ │ - this.callback("move", [evt, current]); │ │ │ │ - this.last = current; │ │ │ │ - OpenLayers.Event.stop(evt) │ │ │ │ - } else if (this.started) { │ │ │ │ - return false │ │ │ │ + addScaleDependentRenderer: function(renderElem, renderer) { │ │ │ │ + if (typeof renderer.lower == "string" || typeof renderer.lower == "number") { │ │ │ │ + renderElem.setAttribute("lower", renderer.lower) │ │ │ │ } │ │ │ │ - return true │ │ │ │ - }, │ │ │ │ - touchend: function(evt) { │ │ │ │ - if (this.started && !OpenLayers.Event.isMultiTouch(evt)) { │ │ │ │ - this.started = false; │ │ │ │ - this.pinching = false; │ │ │ │ - this.callback("done", [evt, this.start, this.last]); │ │ │ │ - this.start = null; │ │ │ │ - this.last = null; │ │ │ │ - return false │ │ │ │ + if (typeof renderer.upper == "string" || typeof renderer.upper == "number") { │ │ │ │ + renderElem.setAttribute("upper", renderer.upper) │ │ │ │ } │ │ │ │ - return true │ │ │ │ + this.addRenderer(renderElem, renderer.renderer) │ │ │ │ }, │ │ │ │ - activate: function() { │ │ │ │ - var activated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.pinching = false; │ │ │ │ - activated = true │ │ │ │ + addValueMapLabelRenderer: function(renderElem, renderer) { │ │ │ │ + renderElem.setAttribute("lookupfield", renderer.lookupfield); │ │ │ │ + renderElem.setAttribute("labelfield", renderer.labelfield); │ │ │ │ + if (typeof renderer.exacts == "object") { │ │ │ │ + for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) { │ │ │ │ + var exact = renderer.exacts[ext]; │ │ │ │ + var eelem = this.createElementNS("", "EXACT"); │ │ │ │ + if (typeof exact.value == "string") { │ │ │ │ + eelem.setAttribute("value", exact.value) │ │ │ │ + } │ │ │ │ + if (typeof exact.label == "string") { │ │ │ │ + eelem.setAttribute("label", exact.label) │ │ │ │ + } │ │ │ │ + if (typeof exact.method == "string") { │ │ │ │ + eelem.setAttribute("method", exact.method) │ │ │ │ + } │ │ │ │ + renderElem.appendChild(eelem); │ │ │ │ + if (typeof exact.symbol == "object") { │ │ │ │ + var selem = null; │ │ │ │ + if (exact.symbol.type == "text") { │ │ │ │ + selem = this.createElementNS("", "TEXTSYMBOL") │ │ │ │ + } │ │ │ │ + if (selem != null) { │ │ │ │ + var keys = this.fontStyleKeys; │ │ │ │ + for (var i = 0, len = keys.length; i < len; i++) { │ │ │ │ + var key = keys[i]; │ │ │ │ + if (exact.symbol[key]) { │ │ │ │ + selem.setAttribute(key, exact.symbol[key]) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + eelem.appendChild(selem) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - return activated │ │ │ │ }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.started = false; │ │ │ │ - this.pinching = false; │ │ │ │ - this.start = null; │ │ │ │ - this.last = null; │ │ │ │ - deactivated = true │ │ │ │ + addValueMapRenderer: function(renderElem, renderer) { │ │ │ │ + renderElem.setAttribute("lookupfield", renderer.lookupfield); │ │ │ │ + if (typeof renderer.ranges == "object") { │ │ │ │ + for (var rng = 0, rnglen = renderer.ranges.length; rng < rnglen; rng++) { │ │ │ │ + var range = renderer.ranges[rng]; │ │ │ │ + var relem = this.createElementNS("", "RANGE"); │ │ │ │ + relem.setAttribute("lower", range.lower); │ │ │ │ + relem.setAttribute("upper", range.upper); │ │ │ │ + renderElem.appendChild(relem); │ │ │ │ + if (typeof range.symbol == "object") { │ │ │ │ + var selem = null; │ │ │ │ + if (range.symbol.type == "simplepolygon") { │ │ │ │ + selem = this.createElementNS("", "SIMPLEPOLYGONSYMBOL") │ │ │ │ + } │ │ │ │ + if (selem != null) { │ │ │ │ + if (typeof range.symbol.boundarycolor == "string") { │ │ │ │ + selem.setAttribute("boundarycolor", range.symbol.boundarycolor) │ │ │ │ + } │ │ │ │ + if (typeof range.symbol.fillcolor == "string") { │ │ │ │ + selem.setAttribute("fillcolor", range.symbol.fillcolor) │ │ │ │ + } │ │ │ │ + if (typeof range.symbol.filltransparency == "number") { │ │ │ │ + selem.setAttribute("filltransparency", range.symbol.filltransparency) │ │ │ │ + } │ │ │ │ + relem.appendChild(selem) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else if (typeof renderer.exacts == "object") { │ │ │ │ + for (var ext = 0, extlen = renderer.exacts.length; ext < extlen; ext++) { │ │ │ │ + var exact = renderer.exacts[ext]; │ │ │ │ + var eelem = this.createElementNS("", "EXACT"); │ │ │ │ + if (typeof exact.value == "string") { │ │ │ │ + eelem.setAttribute("value", exact.value) │ │ │ │ + } │ │ │ │ + if (typeof exact.label == "string") { │ │ │ │ + eelem.setAttribute("label", exact.label) │ │ │ │ + } │ │ │ │ + if (typeof exact.method == "string") { │ │ │ │ + eelem.setAttribute("method", exact.method) │ │ │ │ + } │ │ │ │ + renderElem.appendChild(eelem); │ │ │ │ + if (typeof exact.symbol == "object") { │ │ │ │ + var selem = null; │ │ │ │ + if (exact.symbol.type == "simplemarker") { │ │ │ │ + selem = this.createElementNS("", "SIMPLEMARKERSYMBOL") │ │ │ │ + } │ │ │ │ + if (selem != null) { │ │ │ │ + if (typeof exact.symbol.antialiasing == "string") { │ │ │ │ + selem.setAttribute("antialiasing", exact.symbol.antialiasing) │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.color == "string") { │ │ │ │ + selem.setAttribute("color", exact.symbol.color) │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.outline == "string") { │ │ │ │ + selem.setAttribute("outline", exact.symbol.outline) │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.overlap == "string") { │ │ │ │ + selem.setAttribute("overlap", exact.symbol.overlap) │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.shadow == "string") { │ │ │ │ + selem.setAttribute("shadow", exact.symbol.shadow) │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.transparency == "number") { │ │ │ │ + selem.setAttribute("transparency", exact.symbol.transparency) │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.usecentroid == "string") { │ │ │ │ + selem.setAttribute("usecentroid", exact.symbol.usecentroid) │ │ │ │ + } │ │ │ │ + if (typeof exact.symbol.width == "number") { │ │ │ │ + selem.setAttribute("width", exact.symbol.width) │ │ │ │ + } │ │ │ │ + eelem.appendChild(selem) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - return deactivated │ │ │ │ - }, │ │ │ │ - getDistance: function(touches) { │ │ │ │ - var t0 = touches[0]; │ │ │ │ - var t1 = touches[1]; │ │ │ │ - return Math.sqrt(Math.pow(t0.olClientX - t1.olClientX, 2) + Math.pow(t0.olClientY - t1.olClientY, 2)) │ │ │ │ }, │ │ │ │ - getPinchData: function(evt) { │ │ │ │ - var distance = this.getDistance(evt.touches); │ │ │ │ - var scale = distance / this.start.distance; │ │ │ │ - return { │ │ │ │ - distance: distance, │ │ │ │ - delta: this.last.distance - distance, │ │ │ │ - scale: scale │ │ │ │ + addSimpleLabelRenderer: function(renderElem, renderer) { │ │ │ │ + renderElem.setAttribute("field", renderer.field); │ │ │ │ + var keys = ["featureweight", "howmanylabels", "labelbufferratio", "labelpriorities", "labelweight", "linelabelposition", "rotationalangles"]; │ │ │ │ + for (var i = 0, len = keys.length; i < len; i++) { │ │ │ │ + var key = keys[i]; │ │ │ │ + if (renderer[key]) { │ │ │ │ + renderElem.setAttribute(key, renderer[key]) │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Pinch" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.PinchZoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ - pinchOrigin: null, │ │ │ │ - currentCenter: null, │ │ │ │ - autoActivate: true, │ │ │ │ - preserveCenter: false, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ - this.handler = new OpenLayers.Handler.Pinch(this, { │ │ │ │ - start: this.pinchStart, │ │ │ │ - move: this.pinchMove, │ │ │ │ - done: this.pinchDone │ │ │ │ - }, this.handlerOptions) │ │ │ │ - }, │ │ │ │ - pinchStart: function(evt, pinchData) { │ │ │ │ - var xy = this.preserveCenter ? this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy; │ │ │ │ - this.pinchOrigin = xy; │ │ │ │ - this.currentCenter = xy │ │ │ │ - }, │ │ │ │ - pinchMove: function(evt, pinchData) { │ │ │ │ - var scale = pinchData.scale; │ │ │ │ - var containerOrigin = this.map.layerContainerOriginPx; │ │ │ │ - var pinchOrigin = this.pinchOrigin; │ │ │ │ - var current = this.preserveCenter ? this.map.getPixelFromLonLat(this.map.getCenter()) : evt.xy; │ │ │ │ - var dx = Math.round(containerOrigin.x + current.x - pinchOrigin.x + (scale - 1) * (containerOrigin.x - pinchOrigin.x)); │ │ │ │ - var dy = Math.round(containerOrigin.y + current.y - pinchOrigin.y + (scale - 1) * (containerOrigin.y - pinchOrigin.y)); │ │ │ │ - this.map.applyTransform(dx, dy, scale); │ │ │ │ - this.currentCenter = current │ │ │ │ - }, │ │ │ │ - pinchDone: function(evt, start, last) { │ │ │ │ - this.map.applyTransform(); │ │ │ │ - var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true); │ │ │ │ - if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) { │ │ │ │ - var resolution = this.map.getResolutionForZoom(zoom); │ │ │ │ - var location = this.map.getLonLatFromPixel(this.pinchOrigin); │ │ │ │ - var zoomPixel = this.currentCenter; │ │ │ │ - var size = this.map.getSize(); │ │ │ │ - location.lon += resolution * (size.w / 2 - zoomPixel.x); │ │ │ │ - location.lat -= resolution * (size.h / 2 - zoomPixel.y); │ │ │ │ - this.map.div.clientWidth = this.map.div.clientWidth; │ │ │ │ - this.map.setCenter(location, zoom) │ │ │ │ + if (renderer.symbol.type == "text") { │ │ │ │ + var symbol = renderer.symbol; │ │ │ │ + var selem = this.createElementNS("", "TEXTSYMBOL"); │ │ │ │ + renderElem.appendChild(selem); │ │ │ │ + var keys = this.fontStyleKeys; │ │ │ │ + for (var i = 0, len = keys.length; i < len; i++) { │ │ │ │ + var key = keys[i]; │ │ │ │ + if (symbol[key]) { │ │ │ │ + selem.setAttribute(key, renderer[key]) │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.PinchZoom" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.ZoomIn = OpenLayers.Class(OpenLayers.Control.Button, { │ │ │ │ - trigger: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.zoomIn() │ │ │ │ + writePolygonGeometry: function(polygon) { │ │ │ │ + if (!(polygon instanceof OpenLayers.Geometry.Polygon)) { │ │ │ │ + throw { │ │ │ │ + message: "Cannot write polygon geometry to ArcXML with an " + polygon.CLASS_NAME + " object.", │ │ │ │ + geometry: polygon │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var polyElem = this.createElementNS("", "POLYGON"); │ │ │ │ + for (var ln = 0, lnlen = polygon.components.length; ln < lnlen; ln++) { │ │ │ │ + var ring = polygon.components[ln]; │ │ │ │ + var ringElem = this.createElementNS("", "RING"); │ │ │ │ + for (var rn = 0, rnlen = ring.components.length; rn < rnlen; rn++) { │ │ │ │ + var point = ring.components[rn]; │ │ │ │ + var pointElem = this.createElementNS("", "POINT"); │ │ │ │ + pointElem.setAttribute("x", point.x); │ │ │ │ + pointElem.setAttribute("y", point.y); │ │ │ │ + ringElem.appendChild(pointElem) │ │ │ │ + } │ │ │ │ + polyElem.appendChild(ringElem) │ │ │ │ } │ │ │ │ + return polyElem │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ZoomIn" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.ScaleLine = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - maxWidth: 100, │ │ │ │ - topOutUnits: "km", │ │ │ │ - topInUnits: "m", │ │ │ │ - bottomOutUnits: "mi", │ │ │ │ - bottomInUnits: "ft", │ │ │ │ - eTop: null, │ │ │ │ - eBottom: null, │ │ │ │ - geodesic: false, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (!this.eTop) { │ │ │ │ - this.eTop = document.createElement("div"); │ │ │ │ - this.eTop.className = this.displayClass + "Top"; │ │ │ │ - var theLen = this.topInUnits.length; │ │ │ │ - this.div.appendChild(this.eTop); │ │ │ │ - if (this.topOutUnits == "" || this.topInUnits == "") { │ │ │ │ - this.eTop.style.visibility = "hidden" │ │ │ │ - } else { │ │ │ │ - this.eTop.style.visibility = "visible" │ │ │ │ + parseResponse: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + var newData = new OpenLayers.Format.XML; │ │ │ │ + data = newData.read(data) │ │ │ │ + } │ │ │ │ + var response = new OpenLayers.Format.ArcXML.Response; │ │ │ │ + var errorNode = data.getElementsByTagName("ERROR"); │ │ │ │ + if (errorNode != null && errorNode.length > 0) { │ │ │ │ + response.error = this.getChildValue(errorNode, "Unknown error.") │ │ │ │ + } else { │ │ │ │ + var responseNode = data.getElementsByTagName("RESPONSE"); │ │ │ │ + if (responseNode == null || responseNode.length == 0) { │ │ │ │ + response.error = "No RESPONSE tag found in ArcXML response."; │ │ │ │ + return response │ │ │ │ } │ │ │ │ - this.eBottom = document.createElement("div"); │ │ │ │ - this.eBottom.className = this.displayClass + "Bottom"; │ │ │ │ - this.div.appendChild(this.eBottom); │ │ │ │ - if (this.bottomOutUnits == "" || this.bottomInUnits == "") { │ │ │ │ - this.eBottom.style.visibility = "hidden" │ │ │ │ + var rtype = responseNode[0].firstChild.nodeName; │ │ │ │ + if (rtype == "#text") { │ │ │ │ + rtype = responseNode[0].firstChild.nextSibling.nodeName │ │ │ │ + } │ │ │ │ + if (rtype == "IMAGE") { │ │ │ │ + var envelopeNode = data.getElementsByTagName("ENVELOPE"); │ │ │ │ + var outputNode = data.getElementsByTagName("OUTPUT"); │ │ │ │ + if (envelopeNode == null || envelopeNode.length == 0) { │ │ │ │ + response.error = "No ENVELOPE tag found in ArcXML response." │ │ │ │ + } else if (outputNode == null || outputNode.length == 0) { │ │ │ │ + response.error = "No OUTPUT tag found in ArcXML response." │ │ │ │ + } else { │ │ │ │ + var envAttr = this.parseAttributes(envelopeNode[0]); │ │ │ │ + var outputAttr = this.parseAttributes(outputNode[0]); │ │ │ │ + if (typeof outputAttr.type == "string") { │ │ │ │ + response.image = { │ │ │ │ + envelope: envAttr, │ │ │ │ + output: { │ │ │ │ + type: outputAttr.type, │ │ │ │ + data: this.getChildValue(outputNode[0]) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + response.image = { │ │ │ │ + envelope: envAttr, │ │ │ │ + output: outputAttr │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else if (rtype == "FEATURES") { │ │ │ │ + var features = responseNode[0].getElementsByTagName("FEATURES"); │ │ │ │ + var featureCount = features[0].getElementsByTagName("FEATURECOUNT"); │ │ │ │ + response.features.featurecount = featureCount[0].getAttribute("count"); │ │ │ │ + if (response.features.featurecount > 0) { │ │ │ │ + var envelope = features[0].getElementsByTagName("ENVELOPE"); │ │ │ │ + response.features.envelope = this.parseAttributes(envelope[0], typeof 0); │ │ │ │ + var featureList = features[0].getElementsByTagName("FEATURE"); │ │ │ │ + for (var fn = 0; fn < featureList.length; fn++) { │ │ │ │ + var feature = new OpenLayers.Feature.Vector; │ │ │ │ + var fields = featureList[fn].getElementsByTagName("FIELD"); │ │ │ │ + for (var fdn = 0; fdn < fields.length; fdn++) { │ │ │ │ + var fieldName = fields[fdn].getAttribute("name"); │ │ │ │ + var fieldValue = fields[fdn].getAttribute("value"); │ │ │ │ + feature.attributes[fieldName] = fieldValue │ │ │ │ + } │ │ │ │ + var geom = featureList[fn].getElementsByTagName("POLYGON"); │ │ │ │ + if (geom.length > 0) { │ │ │ │ + var ring = geom[0].getElementsByTagName("RING"); │ │ │ │ + var polys = []; │ │ │ │ + for (var rn = 0; rn < ring.length; rn++) { │ │ │ │ + var linearRings = []; │ │ │ │ + linearRings.push(this.parsePointGeometry(ring[rn])); │ │ │ │ + var holes = ring[rn].getElementsByTagName("HOLE"); │ │ │ │ + for (var hn = 0; hn < holes.length; hn++) { │ │ │ │ + linearRings.push(this.parsePointGeometry(holes[hn])) │ │ │ │ + } │ │ │ │ + holes = null; │ │ │ │ + polys.push(new OpenLayers.Geometry.Polygon(linearRings)); │ │ │ │ + linearRings = null │ │ │ │ + } │ │ │ │ + ring = null; │ │ │ │ + if (polys.length == 1) { │ │ │ │ + feature.geometry = polys[0] │ │ │ │ + } else { │ │ │ │ + feature.geometry = new OpenLayers.Geometry.MultiPolygon(polys) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + response.features.feature.push(feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ } else { │ │ │ │ - this.eBottom.style.visibility = "visible" │ │ │ │ + response.error = "Unidentified response type." │ │ │ │ } │ │ │ │ } │ │ │ │ - this.map.events.register("moveend", this, this.update); │ │ │ │ - this.update(); │ │ │ │ - return this.div │ │ │ │ + return response │ │ │ │ }, │ │ │ │ - getBarLen: function(maxLen) { │ │ │ │ - var digits = parseInt(Math.log(maxLen) / Math.log(10)); │ │ │ │ - var pow10 = Math.pow(10, digits); │ │ │ │ - var firstChar = parseInt(maxLen / pow10); │ │ │ │ - var barLen; │ │ │ │ - if (firstChar > 5) { │ │ │ │ - barLen = 5 │ │ │ │ - } else if (firstChar > 2) { │ │ │ │ - barLen = 2 │ │ │ │ - } else { │ │ │ │ - barLen = 1 │ │ │ │ + parseAttributes: function(node, type) { │ │ │ │ + var attributes = {}; │ │ │ │ + for (var attr = 0; attr < node.attributes.length; attr++) { │ │ │ │ + if (type == "number") { │ │ │ │ + attributes[node.attributes[attr].nodeName] = parseFloat(node.attributes[attr].nodeValue) │ │ │ │ + } else { │ │ │ │ + attributes[node.attributes[attr].nodeName] = node.attributes[attr].nodeValue │ │ │ │ + } │ │ │ │ } │ │ │ │ - return barLen * pow10 │ │ │ │ + return attributes │ │ │ │ }, │ │ │ │ - update: function() { │ │ │ │ - var res = this.map.getResolution(); │ │ │ │ - if (!res) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var curMapUnits = this.map.getUnits(); │ │ │ │ - var inches = OpenLayers.INCHES_PER_UNIT; │ │ │ │ - var maxSizeData = this.maxWidth * res * inches[curMapUnits]; │ │ │ │ - var geodesicRatio = 1; │ │ │ │ - if (this.geodesic === true) { │ │ │ │ - var maxSizeGeodesic = (this.map.getGeodesicPixelSize().w || 1e-6) * this.maxWidth; │ │ │ │ - var maxSizeKilometers = maxSizeData / inches["km"]; │ │ │ │ - geodesicRatio = maxSizeGeodesic / maxSizeKilometers; │ │ │ │ - maxSizeData *= geodesicRatio │ │ │ │ - } │ │ │ │ - var topUnits; │ │ │ │ - var bottomUnits; │ │ │ │ - if (maxSizeData > 1e5) { │ │ │ │ - topUnits = this.topOutUnits; │ │ │ │ - bottomUnits = this.bottomOutUnits │ │ │ │ + parsePointGeometry: function(node) { │ │ │ │ + var ringPoints = []; │ │ │ │ + var coords = node.getElementsByTagName("COORDS"); │ │ │ │ + if (coords.length > 0) { │ │ │ │ + var coordArr = this.getChildValue(coords[0]); │ │ │ │ + coordArr = coordArr.split(/;/); │ │ │ │ + for (var cn = 0; cn < coordArr.length; cn++) { │ │ │ │ + var coordItems = coordArr[cn].split(/ /); │ │ │ │ + ringPoints.push(new OpenLayers.Geometry.Point(coordItems[0], coordItems[1])) │ │ │ │ + } │ │ │ │ + coords = null │ │ │ │ } else { │ │ │ │ - topUnits = this.topInUnits; │ │ │ │ - bottomUnits = this.bottomInUnits │ │ │ │ - } │ │ │ │ - var topMax = maxSizeData / inches[topUnits]; │ │ │ │ - var bottomMax = maxSizeData / inches[bottomUnits]; │ │ │ │ - var topRounded = this.getBarLen(topMax); │ │ │ │ - var bottomRounded = this.getBarLen(bottomMax); │ │ │ │ - topMax = topRounded / inches[curMapUnits] * inches[topUnits]; │ │ │ │ - bottomMax = bottomRounded / inches[curMapUnits] * inches[bottomUnits]; │ │ │ │ - var topPx = topMax / res / geodesicRatio; │ │ │ │ - var bottomPx = bottomMax / res / geodesicRatio; │ │ │ │ - if (this.eBottom.style.visibility == "visible") { │ │ │ │ - this.eBottom.style.width = Math.round(bottomPx) + "px"; │ │ │ │ - this.eBottom.innerHTML = bottomRounded + " " + bottomUnits │ │ │ │ - } │ │ │ │ - if (this.eTop.style.visibility == "visible") { │ │ │ │ - this.eTop.style.width = Math.round(topPx) + "px"; │ │ │ │ - this.eTop.innerHTML = topRounded + " " + topUnits │ │ │ │ + var point = node.getElementsByTagName("POINT"); │ │ │ │ + if (point.length > 0) { │ │ │ │ + for (var pn = 0; pn < point.length; pn++) { │ │ │ │ + ringPoints.push(new OpenLayers.Geometry.Point(parseFloat(point[pn].getAttribute("x")), parseFloat(point[pn].getAttribute("y")))) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + point = null │ │ │ │ } │ │ │ │ + return new OpenLayers.Geometry.LinearRing(ringPoints) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ScaleLine" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.ArcXML" │ │ │ │ }); │ │ │ │ -OpenLayers.Handler.Hover = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - delay: 500, │ │ │ │ - pixelTolerance: null, │ │ │ │ - stopMove: false, │ │ │ │ - px: null, │ │ │ │ - timerId: null, │ │ │ │ - mousemove: function(evt) { │ │ │ │ - if (this.passesTolerance(evt.xy)) { │ │ │ │ - this.clearTimer(); │ │ │ │ - this.callback("move", [evt]); │ │ │ │ - this.px = evt.xy; │ │ │ │ - evt = OpenLayers.Util.extend({}, evt); │ │ │ │ - this.timerId = window.setTimeout(OpenLayers.Function.bind(this.delayedCall, this, evt), this.delay) │ │ │ │ - } │ │ │ │ - return !this.stopMove │ │ │ │ +OpenLayers.Format.ArcXML.Request = OpenLayers.Class({ │ │ │ │ + initialize: function(params) { │ │ │ │ + var defaults = { │ │ │ │ + get_image: { │ │ │ │ + properties: { │ │ │ │ + background: null, │ │ │ │ + draw: true, │ │ │ │ + envelope: { │ │ │ │ + minx: 0, │ │ │ │ + miny: 0, │ │ │ │ + maxx: 0, │ │ │ │ + maxy: 0 │ │ │ │ + }, │ │ │ │ + featurecoordsys: { │ │ │ │ + id: 0, │ │ │ │ + string: "", │ │ │ │ + datumtransformid: 0, │ │ │ │ + datumtransformstring: "" │ │ │ │ + }, │ │ │ │ + filtercoordsys: { │ │ │ │ + id: 0, │ │ │ │ + string: "", │ │ │ │ + datumtransformid: 0, │ │ │ │ + datumtransformstring: "" │ │ │ │ + }, │ │ │ │ + imagesize: { │ │ │ │ + height: 0, │ │ │ │ + width: 0, │ │ │ │ + dpi: 96, │ │ │ │ + printheight: 0, │ │ │ │ + printwidth: 0, │ │ │ │ + scalesymbols: false │ │ │ │ + }, │ │ │ │ + layerlist: [], │ │ │ │ + output: { │ │ │ │ + baseurl: "", │ │ │ │ + legendbaseurl: "", │ │ │ │ + legendname: "", │ │ │ │ + legendpath: "", │ │ │ │ + legendurl: "", │ │ │ │ + name: "", │ │ │ │ + path: "", │ │ │ │ + type: "jpg", │ │ │ │ + url: "" │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + get_feature: { │ │ │ │ + layer: "", │ │ │ │ + query: { │ │ │ │ + isspatial: false, │ │ │ │ + featurecoordsys: { │ │ │ │ + id: 0, │ │ │ │ + string: "", │ │ │ │ + datumtransformid: 0, │ │ │ │ + datumtransformstring: "" │ │ │ │ + }, │ │ │ │ + filtercoordsys: { │ │ │ │ + id: 0, │ │ │ │ + string: "", │ │ │ │ + datumtransformid: 0, │ │ │ │ + datumtransformstring: "" │ │ │ │ + }, │ │ │ │ + buffer: 0, │ │ │ │ + where: "", │ │ │ │ + spatialfilter: { │ │ │ │ + relation: "envelope_intersection", │ │ │ │ + envelope: null │ │ │ │ + } │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + environment: { │ │ │ │ + separators: { │ │ │ │ + cs: " ", │ │ │ │ + ts: ";" │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + layer: [], │ │ │ │ + workspaces: [] │ │ │ │ + }; │ │ │ │ + return OpenLayers.Util.extend(this, defaults) │ │ │ │ }, │ │ │ │ - mouseout: function(evt) { │ │ │ │ - if (OpenLayers.Util.mouseLeft(evt, this.map.viewPortDiv)) { │ │ │ │ - this.clearTimer(); │ │ │ │ - this.callback("move", [evt]) │ │ │ │ + CLASS_NAME: "OpenLayers.Format.ArcXML.Request" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.ArcXML.Response = OpenLayers.Class({ │ │ │ │ + initialize: function(params) { │ │ │ │ + var defaults = { │ │ │ │ + image: { │ │ │ │ + envelope: null, │ │ │ │ + output: "" │ │ │ │ + }, │ │ │ │ + features: { │ │ │ │ + featurecount: 0, │ │ │ │ + envelope: null, │ │ │ │ + feature: [] │ │ │ │ + }, │ │ │ │ + error: "" │ │ │ │ + }; │ │ │ │ + return OpenLayers.Util.extend(this, defaults) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.ArcXML.Response" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.CSWGetDomain = function(options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, OpenLayers.Format.CSWGetDomain.DEFAULTS); │ │ │ │ + var cls = OpenLayers.Format.CSWGetDomain["v" + options.version.replace(/\./g, "_")]; │ │ │ │ + if (!cls) { │ │ │ │ + throw "Unsupported CSWGetDomain version: " + options.version │ │ │ │ + } │ │ │ │ + return new cls(options) │ │ │ │ +}; │ │ │ │ +OpenLayers.Format.CSWGetDomain.DEFAULTS = { │ │ │ │ + version: "2.0.2" │ │ │ │ +}; │ │ │ │ +OpenLayers.Format.WFS = OpenLayers.Class(OpenLayers.Format.GML, { │ │ │ │ + layer: null, │ │ │ │ + wfsns: "http://www.opengis.net/wfs", │ │ │ │ + ogcns: "http://www.opengis.net/ogc", │ │ │ │ + initialize: function(options, layer) { │ │ │ │ + OpenLayers.Format.GML.prototype.initialize.apply(this, [options]); │ │ │ │ + this.layer = layer; │ │ │ │ + if (this.layer.featureNS) { │ │ │ │ + this.featureNS = this.layer.featureNS │ │ │ │ + } │ │ │ │ + if (this.layer.options.geometry_column) { │ │ │ │ + this.geometryName = this.layer.options.geometry_column │ │ │ │ + } │ │ │ │ + if (this.layer.options.typename) { │ │ │ │ + this.featureName = this.layer.options.typename │ │ │ │ } │ │ │ │ - return true │ │ │ │ }, │ │ │ │ - passesTolerance: function(px) { │ │ │ │ - var passes = true; │ │ │ │ - if (this.pixelTolerance && this.px) { │ │ │ │ - var dpx = Math.sqrt(Math.pow(this.px.x - px.x, 2) + Math.pow(this.px.y - px.y, 2)); │ │ │ │ - if (dpx < this.pixelTolerance) { │ │ │ │ - passes = false │ │ │ │ + write: function(features) { │ │ │ │ + var transaction = this.createElementNS(this.wfsns, "wfs:Transaction"); │ │ │ │ + transaction.setAttribute("version", "1.0.0"); │ │ │ │ + transaction.setAttribute("service", "WFS"); │ │ │ │ + for (var i = 0; i < features.length; i++) { │ │ │ │ + switch (features[i].state) { │ │ │ │ + case OpenLayers.State.INSERT: │ │ │ │ + transaction.appendChild(this.insert(features[i])); │ │ │ │ + break; │ │ │ │ + case OpenLayers.State.UPDATE: │ │ │ │ + transaction.appendChild(this.update(features[i])); │ │ │ │ + break; │ │ │ │ + case OpenLayers.State.DELETE: │ │ │ │ + transaction.appendChild(this.remove(features[i])); │ │ │ │ + break │ │ │ │ } │ │ │ │ } │ │ │ │ - return passes │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [transaction]) │ │ │ │ }, │ │ │ │ - clearTimer: function() { │ │ │ │ - if (this.timerId != null) { │ │ │ │ - window.clearTimeout(this.timerId); │ │ │ │ - this.timerId = null │ │ │ │ + createFeatureXML: function(feature) { │ │ │ │ + var geometryNode = this.buildGeometryNode(feature.geometry); │ │ │ │ + var geomContainer = this.createElementNS(this.featureNS, "feature:" + this.geometryName); │ │ │ │ + geomContainer.appendChild(geometryNode); │ │ │ │ + var featureContainer = this.createElementNS(this.featureNS, "feature:" + this.featureName); │ │ │ │ + featureContainer.appendChild(geomContainer); │ │ │ │ + for (var attr in feature.attributes) { │ │ │ │ + var attrText = this.createTextNode(feature.attributes[attr]); │ │ │ │ + var nodename = attr; │ │ │ │ + if (attr.search(":") != -1) { │ │ │ │ + nodename = attr.split(":")[1] │ │ │ │ + } │ │ │ │ + var attrContainer = this.createElementNS(this.featureNS, "feature:" + nodename); │ │ │ │ + attrContainer.appendChild(attrText); │ │ │ │ + featureContainer.appendChild(attrContainer) │ │ │ │ } │ │ │ │ + return featureContainer │ │ │ │ }, │ │ │ │ - delayedCall: function(evt) { │ │ │ │ - this.callback("pause", [evt]) │ │ │ │ + insert: function(feature) { │ │ │ │ + var insertNode = this.createElementNS(this.wfsns, "wfs:Insert"); │ │ │ │ + insertNode.appendChild(this.createFeatureXML(feature)); │ │ │ │ + return insertNode │ │ │ │ }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.clearTimer(); │ │ │ │ - deactivated = true │ │ │ │ + update: function(feature) { │ │ │ │ + if (!feature.fid) { │ │ │ │ + OpenLayers.Console.userError(OpenLayers.i18n("noFID")) │ │ │ │ } │ │ │ │ - return deactivated │ │ │ │ + var updateNode = this.createElementNS(this.wfsns, "wfs:Update"); │ │ │ │ + updateNode.setAttribute("typeName", this.featurePrefix + ":" + this.featureName); │ │ │ │ + updateNode.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); │ │ │ │ + var propertyNode = this.createElementNS(this.wfsns, "wfs:Property"); │ │ │ │ + var nameNode = this.createElementNS(this.wfsns, "wfs:Name"); │ │ │ │ + var txtNode = this.createTextNode(this.geometryName); │ │ │ │ + nameNode.appendChild(txtNode); │ │ │ │ + propertyNode.appendChild(nameNode); │ │ │ │ + var valueNode = this.createElementNS(this.wfsns, "wfs:Value"); │ │ │ │ + var geometryNode = this.buildGeometryNode(feature.geometry); │ │ │ │ + if (feature.layer) { │ │ │ │ + geometryNode.setAttribute("srsName", feature.layer.projection.getCode()) │ │ │ │ + } │ │ │ │ + valueNode.appendChild(geometryNode); │ │ │ │ + propertyNode.appendChild(valueNode); │ │ │ │ + updateNode.appendChild(propertyNode); │ │ │ │ + for (var propName in feature.attributes) { │ │ │ │ + propertyNode = this.createElementNS(this.wfsns, "wfs:Property"); │ │ │ │ + nameNode = this.createElementNS(this.wfsns, "wfs:Name"); │ │ │ │ + nameNode.appendChild(this.createTextNode(propName)); │ │ │ │ + propertyNode.appendChild(nameNode); │ │ │ │ + valueNode = this.createElementNS(this.wfsns, "wfs:Value"); │ │ │ │ + valueNode.appendChild(this.createTextNode(feature.attributes[propName])); │ │ │ │ + propertyNode.appendChild(valueNode); │ │ │ │ + updateNode.appendChild(propertyNode) │ │ │ │ + } │ │ │ │ + var filterNode = this.createElementNS(this.ogcns, "ogc:Filter"); │ │ │ │ + var filterIdNode = this.createElementNS(this.ogcns, "ogc:FeatureId"); │ │ │ │ + filterIdNode.setAttribute("fid", feature.fid); │ │ │ │ + filterNode.appendChild(filterIdNode); │ │ │ │ + updateNode.appendChild(filterNode); │ │ │ │ + return updateNode │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Hover" │ │ │ │ + remove: function(feature) { │ │ │ │ + if (!feature.fid) { │ │ │ │ + OpenLayers.Console.userError(OpenLayers.i18n("noFID")); │ │ │ │ + return false │ │ │ │ + } │ │ │ │ + var deleteNode = this.createElementNS(this.wfsns, "wfs:Delete"); │ │ │ │ + deleteNode.setAttribute("typeName", this.featurePrefix + ":" + this.featureName); │ │ │ │ + deleteNode.setAttribute("xmlns:" + this.featurePrefix, this.featureNS); │ │ │ │ + var filterNode = this.createElementNS(this.ogcns, "ogc:Filter"); │ │ │ │ + var filterIdNode = this.createElementNS(this.ogcns, "ogc:FeatureId"); │ │ │ │ + filterIdNode.setAttribute("fid", feature.fid); │ │ │ │ + filterNode.appendChild(filterIdNode); │ │ │ │ + deleteNode.appendChild(filterNode); │ │ │ │ + return deleteNode │ │ │ │ + }, │ │ │ │ + destroy: function() { │ │ │ │ + this.layer = null │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFS" │ │ │ │ }); │ │ │ │ -OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ - layerIdentifier: "_layer", │ │ │ │ - featureIdentifier: "_feature", │ │ │ │ - regExes: { │ │ │ │ - trimSpace: /^\s*|\s*$/g, │ │ │ │ - removeSpace: /\s*/g, │ │ │ │ - splitSpace: /\s+/, │ │ │ │ - trimComma: /\s*,\s*/g │ │ │ │ +OpenLayers.Format.Atom = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + namespaces: { │ │ │ │ + atom: "http://www.w3.org/2005/Atom", │ │ │ │ + georss: "http://www.georss.org/georss" │ │ │ │ }, │ │ │ │ - gmlFormat: null, │ │ │ │ - read: function(data) { │ │ │ │ - var result; │ │ │ │ - if (typeof data == "string") { │ │ │ │ - data = OpenLayers.Format.XML.prototype.read.apply(this, [data]) │ │ │ │ + feedTitle: "untitled", │ │ │ │ + defaultEntryTitle: "untitled", │ │ │ │ + gmlParser: null, │ │ │ │ + xy: false, │ │ │ │ + read: function(doc) { │ │ │ │ + if (typeof doc == "string") { │ │ │ │ + doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]) │ │ │ │ } │ │ │ │ - var root = data.documentElement; │ │ │ │ - if (root) { │ │ │ │ - var scope = this; │ │ │ │ - var read = this["read_" + root.nodeName]; │ │ │ │ - if (read) { │ │ │ │ - result = read.call(this, root) │ │ │ │ - } else { │ │ │ │ - result = new OpenLayers.Format.GML(this.options ? this.options : {}).read(data) │ │ │ │ + return this.parseFeatures(doc) │ │ │ │ + }, │ │ │ │ + write: function(features) { │ │ │ │ + var doc; │ │ │ │ + if (OpenLayers.Util.isArray(features)) { │ │ │ │ + doc = this.createElementNSPlus("atom:feed"); │ │ │ │ + doc.appendChild(this.createElementNSPlus("atom:title", { │ │ │ │ + value: this.feedTitle │ │ │ │ + })); │ │ │ │ + for (var i = 0, ii = features.length; i < ii; i++) { │ │ │ │ + doc.appendChild(this.buildEntryNode(features[i])) │ │ │ │ } │ │ │ │ } else { │ │ │ │ - result = data │ │ │ │ + doc = this.buildEntryNode(features) │ │ │ │ } │ │ │ │ - return result │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [doc]) │ │ │ │ }, │ │ │ │ - read_msGMLOutput: function(data) { │ │ │ │ - var response = []; │ │ │ │ - var layerNodes = this.getSiblingNodesByTagCriteria(data, this.layerIdentifier); │ │ │ │ - if (layerNodes) { │ │ │ │ - for (var i = 0, len = layerNodes.length; i < len; ++i) { │ │ │ │ - var node = layerNodes[i]; │ │ │ │ - var layerName = node.nodeName; │ │ │ │ - if (node.prefix) { │ │ │ │ - layerName = layerName.split(":")[1] │ │ │ │ - } │ │ │ │ - var layerName = layerName.replace(this.layerIdentifier, ""); │ │ │ │ - var featureNodes = this.getSiblingNodesByTagCriteria(node, this.featureIdentifier); │ │ │ │ - if (featureNodes) { │ │ │ │ - for (var j = 0; j < featureNodes.length; j++) { │ │ │ │ - var featureNode = featureNodes[j]; │ │ │ │ - var geomInfo = this.parseGeometry(featureNode); │ │ │ │ - var attributes = this.parseAttributes(featureNode); │ │ │ │ - var feature = new OpenLayers.Feature.Vector(geomInfo.geometry, attributes, null); │ │ │ │ - feature.bounds = geomInfo.bounds; │ │ │ │ - feature.type = layerName; │ │ │ │ - response.push(feature) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + buildContentNode: function(content) { │ │ │ │ + var node = this.createElementNSPlus("atom:content", { │ │ │ │ + attributes: { │ │ │ │ + type: content.type || null │ │ │ │ } │ │ │ │ - } │ │ │ │ - return response │ │ │ │ - }, │ │ │ │ - read_FeatureInfoResponse: function(data) { │ │ │ │ - var response = []; │ │ │ │ - var featureNodes = this.getElementsByTagNameNS(data, "*", "FIELDS"); │ │ │ │ - for (var i = 0, len = featureNodes.length; i < len; i++) { │ │ │ │ - var featureNode = featureNodes[i]; │ │ │ │ - var geom = null; │ │ │ │ - var attributes = {}; │ │ │ │ - var j; │ │ │ │ - var jlen = featureNode.attributes.length; │ │ │ │ - if (jlen > 0) { │ │ │ │ - for (j = 0; j < jlen; j++) { │ │ │ │ - var attribute = featureNode.attributes[j]; │ │ │ │ - attributes[attribute.nodeName] = attribute.nodeValue │ │ │ │ + }); │ │ │ │ + if (content.src) { │ │ │ │ + node.setAttribute("src", content.src) │ │ │ │ + } else { │ │ │ │ + if (content.type == "text" || content.type == null) { │ │ │ │ + node.appendChild(this.createTextNode(content.value)) │ │ │ │ + } else if (content.type == "html") { │ │ │ │ + if (typeof content.value != "string") { │ │ │ │ + throw "HTML content must be in form of an escaped string" │ │ │ │ } │ │ │ │ + node.appendChild(this.createTextNode(content.value)) │ │ │ │ + } else if (content.type == "xhtml") { │ │ │ │ + node.appendChild(content.value) │ │ │ │ + } else if (content.type == "xhtml" || content.type.match(/(\+|\/)xml$/)) { │ │ │ │ + node.appendChild(content.value) │ │ │ │ } else { │ │ │ │ - var nodes = featureNode.childNodes; │ │ │ │ - for (j = 0, jlen = nodes.length; j < jlen; ++j) { │ │ │ │ - var node = nodes[j]; │ │ │ │ - if (node.nodeType != 3) { │ │ │ │ - attributes[node.getAttribute("name")] = node.getAttribute("value") │ │ │ │ - } │ │ │ │ - } │ │ │ │ + node.appendChild(this.createTextNode(content.value)) │ │ │ │ } │ │ │ │ - response.push(new OpenLayers.Feature.Vector(geom, attributes, null)) │ │ │ │ } │ │ │ │ - return response │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - getSiblingNodesByTagCriteria: function(node, criteria) { │ │ │ │ - var nodes = []; │ │ │ │ - var children, tagName, n, matchNodes, child; │ │ │ │ - if (node && node.hasChildNodes()) { │ │ │ │ - children = node.childNodes; │ │ │ │ - n = children.length; │ │ │ │ - for (var k = 0; k < n; k++) { │ │ │ │ - child = children[k]; │ │ │ │ - while (child && child.nodeType != 1) { │ │ │ │ - child = child.nextSibling; │ │ │ │ - k++ │ │ │ │ - } │ │ │ │ - tagName = child ? child.nodeName : ""; │ │ │ │ - if (tagName.length > 0 && tagName.indexOf(criteria) > -1) { │ │ │ │ - nodes.push(child) │ │ │ │ - } else { │ │ │ │ - matchNodes = this.getSiblingNodesByTagCriteria(child, criteria); │ │ │ │ - if (matchNodes.length > 0) { │ │ │ │ - nodes.length == 0 ? nodes = matchNodes : nodes.push(matchNodes) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + buildEntryNode: function(feature) { │ │ │ │ + var attrib = feature.attributes; │ │ │ │ + var atomAttrib = attrib.atom || {}; │ │ │ │ + var entryNode = this.createElementNSPlus("atom:entry"); │ │ │ │ + if (atomAttrib.authors) { │ │ │ │ + var authors = OpenLayers.Util.isArray(atomAttrib.authors) ? atomAttrib.authors : [atomAttrib.authors]; │ │ │ │ + for (var i = 0, ii = authors.length; i < ii; i++) { │ │ │ │ + entryNode.appendChild(this.buildPersonConstructNode("author", authors[i])) │ │ │ │ } │ │ │ │ } │ │ │ │ - return nodes │ │ │ │ - }, │ │ │ │ - parseAttributes: function(node) { │ │ │ │ - var attributes = {}; │ │ │ │ - if (node.nodeType == 1) { │ │ │ │ - var children = node.childNodes; │ │ │ │ - var n = children.length; │ │ │ │ - for (var i = 0; i < n; ++i) { │ │ │ │ - var child = children[i]; │ │ │ │ - if (child.nodeType == 1) { │ │ │ │ - var grandchildren = child.childNodes; │ │ │ │ - var name = child.prefix ? child.nodeName.split(":")[1] : child.nodeName; │ │ │ │ - if (grandchildren.length == 0) { │ │ │ │ - attributes[name] = null │ │ │ │ - } else if (grandchildren.length == 1) { │ │ │ │ - var grandchild = grandchildren[0]; │ │ │ │ - if (grandchild.nodeType == 3 || grandchild.nodeType == 4) { │ │ │ │ - var value = grandchild.nodeValue.replace(this.regExes.trimSpace, ""); │ │ │ │ - attributes[name] = value │ │ │ │ - } │ │ │ │ + if (atomAttrib.categories) { │ │ │ │ + var categories = OpenLayers.Util.isArray(atomAttrib.categories) ? atomAttrib.categories : [atomAttrib.categories]; │ │ │ │ + var category; │ │ │ │ + for (var i = 0, ii = categories.length; i < ii; i++) { │ │ │ │ + category = categories[i]; │ │ │ │ + entryNode.appendChild(this.createElementNSPlus("atom:category", { │ │ │ │ + attributes: { │ │ │ │ + term: category.term, │ │ │ │ + scheme: category.scheme || null, │ │ │ │ + label: category.label || null │ │ │ │ } │ │ │ │ - } │ │ │ │ + })) │ │ │ │ } │ │ │ │ } │ │ │ │ - return attributes │ │ │ │ - }, │ │ │ │ - parseGeometry: function(node) { │ │ │ │ - if (!this.gmlFormat) { │ │ │ │ - this.gmlFormat = new OpenLayers.Format.GML │ │ │ │ + if (atomAttrib.content) { │ │ │ │ + entryNode.appendChild(this.buildContentNode(atomAttrib.content)) │ │ │ │ } │ │ │ │ - var feature = this.gmlFormat.parseFeature(node); │ │ │ │ - var geometry, bounds = null; │ │ │ │ - if (feature) { │ │ │ │ - geometry = feature.geometry && feature.geometry.clone(); │ │ │ │ - bounds = feature.bounds && feature.bounds.clone(); │ │ │ │ - feature.destroy() │ │ │ │ + if (atomAttrib.contributors) { │ │ │ │ + var contributors = OpenLayers.Util.isArray(atomAttrib.contributors) ? atomAttrib.contributors : [atomAttrib.contributors]; │ │ │ │ + for (var i = 0, ii = contributors.length; i < ii; i++) { │ │ │ │ + entryNode.appendChild(this.buildPersonConstructNode("contributor", contributors[i])) │ │ │ │ + } │ │ │ │ } │ │ │ │ - return { │ │ │ │ - geometry: geometry, │ │ │ │ - bounds: bounds │ │ │ │ + if (feature.fid) { │ │ │ │ + entryNode.appendChild(this.createElementNSPlus("atom:id", { │ │ │ │ + value: feature.fid │ │ │ │ + })) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Format.WMSGetFeatureInfo" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - hover: false, │ │ │ │ - drillDown: false, │ │ │ │ - maxFeatures: 10, │ │ │ │ - clickCallback: "click", │ │ │ │ - output: "features", │ │ │ │ - layers: null, │ │ │ │ - queryVisible: false, │ │ │ │ - url: null, │ │ │ │ - layerUrls: null, │ │ │ │ - infoFormat: "text/html", │ │ │ │ - vendorParams: {}, │ │ │ │ - format: null, │ │ │ │ - formatOptions: null, │ │ │ │ - handler: null, │ │ │ │ - hoverRequest: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - options.handlerOptions = options.handlerOptions || {}; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - if (!this.format) { │ │ │ │ - this.format = new OpenLayers.Format.WMSGetFeatureInfo(options.formatOptions) │ │ │ │ + if (atomAttrib.links) { │ │ │ │ + var links = OpenLayers.Util.isArray(atomAttrib.links) ? atomAttrib.links : [atomAttrib.links]; │ │ │ │ + var link; │ │ │ │ + for (var i = 0, ii = links.length; i < ii; i++) { │ │ │ │ + link = links[i]; │ │ │ │ + entryNode.appendChild(this.createElementNSPlus("atom:link", { │ │ │ │ + attributes: { │ │ │ │ + href: link.href, │ │ │ │ + rel: link.rel || null, │ │ │ │ + type: link.type || null, │ │ │ │ + hreflang: link.hreflang || null, │ │ │ │ + title: link.title || null, │ │ │ │ + length: link.length || null │ │ │ │ + } │ │ │ │ + })) │ │ │ │ + } │ │ │ │ } │ │ │ │ - if (this.drillDown === true) { │ │ │ │ - this.hover = false │ │ │ │ + if (atomAttrib.published) { │ │ │ │ + entryNode.appendChild(this.createElementNSPlus("atom:published", { │ │ │ │ + value: atomAttrib.published │ │ │ │ + })) │ │ │ │ } │ │ │ │ - if (this.hover) { │ │ │ │ - this.handler = new OpenLayers.Handler.Hover(this, { │ │ │ │ - move: this.cancelHover, │ │ │ │ - pause: this.getInfoForHover │ │ │ │ - }, OpenLayers.Util.extend(this.handlerOptions.hover || {}, { │ │ │ │ - delay: 250 │ │ │ │ + if (atomAttrib.rights) { │ │ │ │ + entryNode.appendChild(this.createElementNSPlus("atom:rights", { │ │ │ │ + value: atomAttrib.rights │ │ │ │ })) │ │ │ │ - } else { │ │ │ │ - var callbacks = {}; │ │ │ │ - callbacks[this.clickCallback] = this.getInfoForClick; │ │ │ │ - this.handler = new OpenLayers.Handler.Click(this, callbacks, this.handlerOptions.click || {}) │ │ │ │ } │ │ │ │ + if (atomAttrib.summary || attrib.description) { │ │ │ │ + entryNode.appendChild(this.createElementNSPlus("atom:summary", { │ │ │ │ + value: atomAttrib.summary || attrib.description │ │ │ │ + })) │ │ │ │ + } │ │ │ │ + entryNode.appendChild(this.createElementNSPlus("atom:title", { │ │ │ │ + value: atomAttrib.title || attrib.title || this.defaultEntryTitle │ │ │ │ + })); │ │ │ │ + if (atomAttrib.updated) { │ │ │ │ + entryNode.appendChild(this.createElementNSPlus("atom:updated", { │ │ │ │ + value: atomAttrib.updated │ │ │ │ + })) │ │ │ │ + } │ │ │ │ + if (feature.geometry) { │ │ │ │ + var whereNode = this.createElementNSPlus("georss:where"); │ │ │ │ + whereNode.appendChild(this.buildGeometryNode(feature.geometry)); │ │ │ │ + entryNode.appendChild(whereNode) │ │ │ │ + } │ │ │ │ + return entryNode │ │ │ │ }, │ │ │ │ - getInfoForClick: function(evt) { │ │ │ │ - this.events.triggerEvent("beforegetfeatureinfo", { │ │ │ │ - xy: evt.xy │ │ │ │ - }); │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ - this.request(evt.xy, {}) │ │ │ │ - }, │ │ │ │ - getInfoForHover: function(evt) { │ │ │ │ - this.events.triggerEvent("beforegetfeatureinfo", { │ │ │ │ - xy: evt.xy │ │ │ │ - }); │ │ │ │ - this.request(evt.xy, { │ │ │ │ - hover: true │ │ │ │ + initGmlParser: function() { │ │ │ │ + this.gmlParser = new OpenLayers.Format.GML.v3({ │ │ │ │ + xy: this.xy, │ │ │ │ + featureNS: "http://example.com#feature", │ │ │ │ + internalProjection: this.internalProjection, │ │ │ │ + externalProjection: this.externalProjection │ │ │ │ }) │ │ │ │ }, │ │ │ │ - cancelHover: function() { │ │ │ │ - if (this.hoverRequest) { │ │ │ │ - this.hoverRequest.abort(); │ │ │ │ - this.hoverRequest = null │ │ │ │ + buildGeometryNode: function(geometry) { │ │ │ │ + if (!this.gmlParser) { │ │ │ │ + this.initGmlParser() │ │ │ │ } │ │ │ │ + var node = this.gmlParser.writeNode("feature:_geometry", geometry); │ │ │ │ + return node.firstChild │ │ │ │ }, │ │ │ │ - findLayers: function() { │ │ │ │ - var candidates = this.layers || this.map.layers; │ │ │ │ - var layers = []; │ │ │ │ - var layer, url; │ │ │ │ - for (var i = candidates.length - 1; i >= 0; --i) { │ │ │ │ - layer = candidates[i]; │ │ │ │ - if (layer instanceof OpenLayers.Layer.WMS && (!this.queryVisible || layer.getVisibility())) { │ │ │ │ - url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url; │ │ │ │ - if (this.drillDown === false && !this.url) { │ │ │ │ - this.url = url │ │ │ │ - } │ │ │ │ - if (this.drillDown === true || this.urlMatches(url)) { │ │ │ │ - layers.push(layer) │ │ │ │ - } │ │ │ │ + buildPersonConstructNode: function(name, value) { │ │ │ │ + var oNames = ["uri", "email"]; │ │ │ │ + var personNode = this.createElementNSPlus("atom:" + name); │ │ │ │ + personNode.appendChild(this.createElementNSPlus("atom:name", { │ │ │ │ + value: value.name │ │ │ │ + })); │ │ │ │ + for (var i = 0, ii = oNames.length; i < ii; i++) { │ │ │ │ + if (value[oNames[i]]) { │ │ │ │ + personNode.appendChild(this.createElementNSPlus("atom:" + oNames[i], { │ │ │ │ + value: value[oNames[i]] │ │ │ │ + })) │ │ │ │ } │ │ │ │ } │ │ │ │ - return layers │ │ │ │ + return personNode │ │ │ │ }, │ │ │ │ - urlMatches: function(url) { │ │ │ │ - var matches = OpenLayers.Util.isEquivalentUrl(this.url, url); │ │ │ │ - if (!matches && this.layerUrls) { │ │ │ │ - for (var i = 0, len = this.layerUrls.length; i < len; ++i) { │ │ │ │ - if (OpenLayers.Util.isEquivalentUrl(this.layerUrls[i], url)) { │ │ │ │ - matches = true; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ + getFirstChildValue: function(node, nsuri, name, def) { │ │ │ │ + var value; │ │ │ │ + var nodes = this.getElementsByTagNameNS(node, nsuri, name); │ │ │ │ + if (nodes && nodes.length > 0) { │ │ │ │ + value = this.getChildValue(nodes[0], def) │ │ │ │ + } else { │ │ │ │ + value = def │ │ │ │ } │ │ │ │ - return matches │ │ │ │ + return value │ │ │ │ }, │ │ │ │ - buildWMSOptions: function(url, layers, clickPosition, format) { │ │ │ │ - var layerNames = [], │ │ │ │ - styleNames = []; │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - if (layers[i].params.LAYERS != null) { │ │ │ │ - layerNames = layerNames.concat(layers[i].params.LAYERS); │ │ │ │ - styleNames = styleNames.concat(this.getStyleNames(layers[i])) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var firstLayer = layers[0]; │ │ │ │ - var projection = this.map.getProjection(); │ │ │ │ - var layerProj = firstLayer.projection; │ │ │ │ - if (layerProj && layerProj.equals(this.map.getProjectionObject())) { │ │ │ │ - projection = layerProj.getCode() │ │ │ │ - } │ │ │ │ - var params = OpenLayers.Util.extend({ │ │ │ │ - service: "WMS", │ │ │ │ - version: firstLayer.params.VERSION, │ │ │ │ - request: "GetFeatureInfo", │ │ │ │ - exceptions: firstLayer.params.EXCEPTIONS, │ │ │ │ - bbox: this.map.getExtent().toBBOX(null, firstLayer.reverseAxisOrder()), │ │ │ │ - feature_count: this.maxFeatures, │ │ │ │ - height: this.map.getSize().h, │ │ │ │ - width: this.map.getSize().w, │ │ │ │ - format: format, │ │ │ │ - info_format: firstLayer.params.INFO_FORMAT || this.infoFormat │ │ │ │ - }, parseFloat(firstLayer.params.VERSION) >= 1.3 ? { │ │ │ │ - crs: projection, │ │ │ │ - i: parseInt(clickPosition.x), │ │ │ │ - j: parseInt(clickPosition.y) │ │ │ │ - } : { │ │ │ │ - srs: projection, │ │ │ │ - x: parseInt(clickPosition.x), │ │ │ │ - y: parseInt(clickPosition.y) │ │ │ │ - }); │ │ │ │ - if (layerNames.length != 0) { │ │ │ │ - params = OpenLayers.Util.extend({ │ │ │ │ - layers: layerNames, │ │ │ │ - query_layers: layerNames, │ │ │ │ - styles: styleNames │ │ │ │ - }, params) │ │ │ │ - } │ │ │ │ - OpenLayers.Util.applyDefaults(params, this.vendorParams); │ │ │ │ - return { │ │ │ │ - url: url, │ │ │ │ - params: OpenLayers.Util.upperCaseObject(params), │ │ │ │ - callback: function(request) { │ │ │ │ - this.handleResponse(clickPosition, request, url) │ │ │ │ - }, │ │ │ │ - scope: this │ │ │ │ + parseFeature: function(node) { │ │ │ │ + var atomAttrib = {}; │ │ │ │ + var value = null; │ │ │ │ + var nodes = null; │ │ │ │ + var attval = null; │ │ │ │ + var atomns = this.namespaces.atom; │ │ │ │ + this.parsePersonConstructs(node, "author", atomAttrib); │ │ │ │ + nodes = this.getElementsByTagNameNS(node, atomns, "category"); │ │ │ │ + if (nodes.length > 0) { │ │ │ │ + atomAttrib.categories = [] │ │ │ │ } │ │ │ │ - }, │ │ │ │ - getStyleNames: function(layer) { │ │ │ │ - var styleNames; │ │ │ │ - if (layer.params.STYLES) { │ │ │ │ - styleNames = layer.params.STYLES │ │ │ │ - } else { │ │ │ │ - if (OpenLayers.Util.isArray(layer.params.LAYERS)) { │ │ │ │ - styleNames = new Array(layer.params.LAYERS.length) │ │ │ │ - } else { │ │ │ │ - styleNames = layer.params.LAYERS.replace(/[^,]/g, "") │ │ │ │ + for (var i = 0, ii = nodes.length; i < ii; i++) { │ │ │ │ + value = {}; │ │ │ │ + value.term = nodes[i].getAttribute("term"); │ │ │ │ + attval = nodes[i].getAttribute("scheme"); │ │ │ │ + if (attval) { │ │ │ │ + value.scheme = attval │ │ │ │ } │ │ │ │ + attval = nodes[i].getAttribute("label"); │ │ │ │ + if (attval) { │ │ │ │ + value.label = attval │ │ │ │ + } │ │ │ │ + atomAttrib.categories.push(value) │ │ │ │ } │ │ │ │ - return styleNames │ │ │ │ - }, │ │ │ │ - request: function(clickPosition, options) { │ │ │ │ - var layers = this.findLayers(); │ │ │ │ - if (layers.length == 0) { │ │ │ │ - this.events.triggerEvent("nogetfeatureinfo"); │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ - return │ │ │ │ - } │ │ │ │ - options = options || {}; │ │ │ │ - if (this.drillDown === false) { │ │ │ │ - var wmsOptions = this.buildWMSOptions(this.url, layers, clickPosition, layers[0].params.FORMAT); │ │ │ │ - var request = OpenLayers.Request.GET(wmsOptions); │ │ │ │ - if (options.hover === true) { │ │ │ │ - this.hoverRequest = request │ │ │ │ + nodes = this.getElementsByTagNameNS(node, atomns, "content"); │ │ │ │ + if (nodes.length > 0) { │ │ │ │ + value = {}; │ │ │ │ + attval = nodes[0].getAttribute("type"); │ │ │ │ + if (attval) { │ │ │ │ + value.type = attval │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - this._requestCount = 0; │ │ │ │ - this._numRequests = 0; │ │ │ │ - this.features = []; │ │ │ │ - var services = {}, │ │ │ │ - url; │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - var layer = layers[i]; │ │ │ │ - var service, found = false; │ │ │ │ - url = OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url; │ │ │ │ - if (url in services) { │ │ │ │ - services[url].push(layer) │ │ │ │ + attval = nodes[0].getAttribute("src"); │ │ │ │ + if (attval) { │ │ │ │ + value.src = attval │ │ │ │ + } else { │ │ │ │ + if (value.type == "text" || value.type == "html" || value.type == null) { │ │ │ │ + value.value = this.getFirstChildValue(node, atomns, "content", null) │ │ │ │ + } else if (value.type == "xhtml" || value.type.match(/(\+|\/)xml$/)) { │ │ │ │ + value.value = this.getChildEl(nodes[0]) │ │ │ │ } else { │ │ │ │ - this._numRequests++; │ │ │ │ - services[url] = [layer] │ │ │ │ + value.value = this.getFirstChildValue(node, atomns, "content", null) │ │ │ │ } │ │ │ │ - } │ │ │ │ - var layers; │ │ │ │ - for (var url in services) { │ │ │ │ - layers = services[url]; │ │ │ │ - var wmsOptions = this.buildWMSOptions(url, layers, clickPosition, layers[0].params.FORMAT); │ │ │ │ - OpenLayers.Request.GET(wmsOptions) │ │ │ │ + atomAttrib.content = value │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - triggerGetFeatureInfo: function(request, xy, features) { │ │ │ │ - this.events.triggerEvent("getfeatureinfo", { │ │ │ │ - text: request.responseText, │ │ │ │ - features: features, │ │ │ │ - request: request, │ │ │ │ - xy: xy │ │ │ │ - }); │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait") │ │ │ │ - }, │ │ │ │ - handleResponse: function(xy, request, url) { │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText │ │ │ │ + this.parsePersonConstructs(node, "contributor", atomAttrib); │ │ │ │ + atomAttrib.id = this.getFirstChildValue(node, atomns, "id", null); │ │ │ │ + nodes = this.getElementsByTagNameNS(node, atomns, "link"); │ │ │ │ + if (nodes.length > 0) { │ │ │ │ + atomAttrib.links = new Array(nodes.length) │ │ │ │ } │ │ │ │ - var features = this.format.read(doc); │ │ │ │ - if (this.drillDown === false) { │ │ │ │ - this.triggerGetFeatureInfo(request, xy, features) │ │ │ │ - } else { │ │ │ │ - this._requestCount++; │ │ │ │ - if (this.output === "object") { │ │ │ │ - this._features = (this._features || []).concat({ │ │ │ │ - url: url, │ │ │ │ - features: features │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - this._features = (this._features || []).concat(features) │ │ │ │ - } │ │ │ │ - if (this._requestCount === this._numRequests) { │ │ │ │ - this.triggerGetFeatureInfo(request, xy, this._features.concat()); │ │ │ │ - delete this._features; │ │ │ │ - delete this._requestCount; │ │ │ │ - delete this._numRequests │ │ │ │ + var oAtts = ["rel", "type", "hreflang", "title", "length"]; │ │ │ │ + for (var i = 0, ii = nodes.length; i < ii; i++) { │ │ │ │ + value = {}; │ │ │ │ + value.href = nodes[i].getAttribute("href"); │ │ │ │ + for (var j = 0, jj = oAtts.length; j < jj; j++) { │ │ │ │ + attval = nodes[i].getAttribute(oAtts[j]); │ │ │ │ + if (attval) { │ │ │ │ + value[oAtts[j]] = attval │ │ │ │ + } │ │ │ │ } │ │ │ │ + atomAttrib.links[i] = value │ │ │ │ } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.WMSGetFeatureInfo" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.OverviewMap = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - element: null, │ │ │ │ - ovmap: null, │ │ │ │ - size: { │ │ │ │ - w: 180, │ │ │ │ - h: 90 │ │ │ │ - }, │ │ │ │ - layers: null, │ │ │ │ - minRectSize: 15, │ │ │ │ - minRectDisplayClass: "RectReplacement", │ │ │ │ - minRatio: 8, │ │ │ │ - maxRatio: 32, │ │ │ │ - mapOptions: null, │ │ │ │ - autoPan: false, │ │ │ │ - handlers: null, │ │ │ │ - resolutionFactor: 1, │ │ │ │ - maximized: false, │ │ │ │ - maximizeTitle: "", │ │ │ │ - minimizeTitle: "", │ │ │ │ - initialize: function(options) { │ │ │ │ - this.layers = []; │ │ │ │ - this.handlers = {}; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - if (!this.mapDiv) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - if (this.handlers.click) { │ │ │ │ - this.handlers.click.destroy() │ │ │ │ - } │ │ │ │ - if (this.handlers.drag) { │ │ │ │ - this.handlers.drag.destroy() │ │ │ │ + value = this.getFirstChildValue(node, atomns, "published", null); │ │ │ │ + if (value) { │ │ │ │ + atomAttrib.published = value │ │ │ │ } │ │ │ │ - this.ovmap && this.ovmap.viewPortDiv.removeChild(this.extentRectangle); │ │ │ │ - this.extentRectangle = null; │ │ │ │ - if (this.rectEvents) { │ │ │ │ - this.rectEvents.destroy(); │ │ │ │ - this.rectEvents = null │ │ │ │ + value = this.getFirstChildValue(node, atomns, "rights", null); │ │ │ │ + if (value) { │ │ │ │ + atomAttrib.rights = value │ │ │ │ } │ │ │ │ - if (this.ovmap) { │ │ │ │ - this.ovmap.destroy(); │ │ │ │ - this.ovmap = null │ │ │ │ + value = this.getFirstChildValue(node, atomns, "summary", null); │ │ │ │ + if (value) { │ │ │ │ + atomAttrib.summary = value │ │ │ │ } │ │ │ │ - this.element.removeChild(this.mapDiv); │ │ │ │ - this.mapDiv = null; │ │ │ │ - this.div.removeChild(this.element); │ │ │ │ - this.element = null; │ │ │ │ - if (this.maximizeDiv) { │ │ │ │ - this.div.removeChild(this.maximizeDiv); │ │ │ │ - this.maximizeDiv = null │ │ │ │ + atomAttrib.title = this.getFirstChildValue(node, atomns, "title", null); │ │ │ │ + atomAttrib.updated = this.getFirstChildValue(node, atomns, "updated", null); │ │ │ │ + var featureAttrib = { │ │ │ │ + title: atomAttrib.title, │ │ │ │ + description: atomAttrib.summary, │ │ │ │ + atom: atomAttrib │ │ │ │ + }; │ │ │ │ + var geometry = this.parseLocations(node)[0]; │ │ │ │ + var feature = new OpenLayers.Feature.Vector(geometry, featureAttrib); │ │ │ │ + feature.fid = atomAttrib.id; │ │ │ │ + return feature │ │ │ │ + }, │ │ │ │ + parseFeatures: function(node) { │ │ │ │ + var features = []; │ │ │ │ + var entries = this.getElementsByTagNameNS(node, this.namespaces.atom, "entry"); │ │ │ │ + if (entries.length == 0) { │ │ │ │ + entries = [node] │ │ │ │ } │ │ │ │ - if (this.minimizeDiv) { │ │ │ │ - this.div.removeChild(this.minimizeDiv); │ │ │ │ - this.minimizeDiv = null │ │ │ │ + for (var i = 0, ii = entries.length; i < ii; i++) { │ │ │ │ + features.push(this.parseFeature(entries[i])) │ │ │ │ } │ │ │ │ - this.map.events.un({ │ │ │ │ - buttonclick: this.onButtonClick, │ │ │ │ - moveend: this.update, │ │ │ │ - changebaselayer: this.baseLayerDraw, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ + return features │ │ │ │ }, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (this.layers.length === 0) { │ │ │ │ - if (this.map.baseLayer) { │ │ │ │ - var layer = this.map.baseLayer.clone(); │ │ │ │ - this.layers = [layer] │ │ │ │ - } else { │ │ │ │ - this.map.events.register("changebaselayer", this, this.baseLayerDraw); │ │ │ │ - return this.div │ │ │ │ + parseLocations: function(node) { │ │ │ │ + var georssns = this.namespaces.georss; │ │ │ │ + var locations = { │ │ │ │ + components: [] │ │ │ │ + }; │ │ │ │ + var where = this.getElementsByTagNameNS(node, georssns, "where"); │ │ │ │ + if (where && where.length > 0) { │ │ │ │ + if (!this.gmlParser) { │ │ │ │ + this.initGmlParser() │ │ │ │ + } │ │ │ │ + for (var i = 0, ii = where.length; i < ii; i++) { │ │ │ │ + this.gmlParser.readChildNodes(where[i], locations) │ │ │ │ } │ │ │ │ } │ │ │ │ - this.element = document.createElement("div"); │ │ │ │ - this.element.className = this.displayClass + "Element"; │ │ │ │ - this.element.style.display = "none"; │ │ │ │ - this.mapDiv = document.createElement("div"); │ │ │ │ - this.mapDiv.style.width = this.size.w + "px"; │ │ │ │ - this.mapDiv.style.height = this.size.h + "px"; │ │ │ │ - this.mapDiv.style.position = "relative"; │ │ │ │ - this.mapDiv.style.overflow = "hidden"; │ │ │ │ - this.mapDiv.id = OpenLayers.Util.createUniqueID("overviewMap"); │ │ │ │ - this.extentRectangle = document.createElement("div"); │ │ │ │ - this.extentRectangle.style.position = "absolute"; │ │ │ │ - this.extentRectangle.style.zIndex = 1e3; │ │ │ │ - this.extentRectangle.className = this.displayClass + "ExtentRectangle"; │ │ │ │ - this.element.appendChild(this.mapDiv); │ │ │ │ - this.div.appendChild(this.element); │ │ │ │ - if (!this.outsideViewport) { │ │ │ │ - this.div.className += " " + this.displayClass + "Container"; │ │ │ │ - var img = OpenLayers.Util.getImageLocation("layer-switcher-maximize.png"); │ │ │ │ - this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(this.displayClass + "MaximizeButton", null, null, img, "absolute"); │ │ │ │ - this.maximizeDiv.style.display = "none"; │ │ │ │ - this.maximizeDiv.className = this.displayClass + "MaximizeButton olButton"; │ │ │ │ - if (this.maximizeTitle) { │ │ │ │ - this.maximizeDiv.title = this.maximizeTitle │ │ │ │ + var components = locations.components; │ │ │ │ + var point = this.getElementsByTagNameNS(node, georssns, "point"); │ │ │ │ + if (point && point.length > 0) { │ │ │ │ + for (var i = 0, ii = point.length; i < ii; i++) { │ │ │ │ + var xy = OpenLayers.String.trim(point[i].firstChild.nodeValue).split(/\s+/); │ │ │ │ + if (xy.length != 2) { │ │ │ │ + xy = OpenLayers.String.trim(point[i].firstChild.nodeValue).split(/\s*,\s*/) │ │ │ │ + } │ │ │ │ + components.push(new OpenLayers.Geometry.Point(xy[1], xy[0])) │ │ │ │ } │ │ │ │ - this.div.appendChild(this.maximizeDiv); │ │ │ │ - var img = OpenLayers.Util.getImageLocation("layer-switcher-minimize.png"); │ │ │ │ - this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_minimizeDiv", null, null, img, "absolute"); │ │ │ │ - this.minimizeDiv.style.display = "none"; │ │ │ │ - this.minimizeDiv.className = this.displayClass + "MinimizeButton olButton"; │ │ │ │ - if (this.minimizeTitle) { │ │ │ │ - this.minimizeDiv.title = this.minimizeTitle │ │ │ │ + } │ │ │ │ + var line = this.getElementsByTagNameNS(node, georssns, "line"); │ │ │ │ + if (line && line.length > 0) { │ │ │ │ + var coords; │ │ │ │ + var p; │ │ │ │ + var points; │ │ │ │ + for (var i = 0, ii = line.length; i < ii; i++) { │ │ │ │ + coords = OpenLayers.String.trim(line[i].firstChild.nodeValue).split(/\s+/); │ │ │ │ + points = []; │ │ │ │ + for (var j = 0, jj = coords.length; j < jj; j += 2) { │ │ │ │ + p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]); │ │ │ │ + points.push(p) │ │ │ │ + } │ │ │ │ + components.push(new OpenLayers.Geometry.LineString(points)) │ │ │ │ } │ │ │ │ - this.div.appendChild(this.minimizeDiv); │ │ │ │ - this.minimizeControl() │ │ │ │ - } else { │ │ │ │ - this.element.style.display = "" │ │ │ │ } │ │ │ │ - if (this.map.getExtent()) { │ │ │ │ - this.update() │ │ │ │ + var polygon = this.getElementsByTagNameNS(node, georssns, "polygon"); │ │ │ │ + if (polygon && polygon.length > 0) { │ │ │ │ + var coords; │ │ │ │ + var p; │ │ │ │ + var points; │ │ │ │ + for (var i = 0, ii = polygon.length; i < ii; i++) { │ │ │ │ + coords = OpenLayers.String.trim(polygon[i].firstChild.nodeValue).split(/\s+/); │ │ │ │ + points = []; │ │ │ │ + for (var j = 0, jj = coords.length; j < jj; j += 2) { │ │ │ │ + p = new OpenLayers.Geometry.Point(coords[j + 1], coords[j]); │ │ │ │ + points.push(p) │ │ │ │ + } │ │ │ │ + components.push(new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(points)])) │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.map.events.on({ │ │ │ │ - buttonclick: this.onButtonClick, │ │ │ │ - moveend: this.update, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - if (this.maximized) { │ │ │ │ - this.maximizeControl() │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + for (var i = 0, ii = components.length; i < ii; i++) { │ │ │ │ + if (components[i]) { │ │ │ │ + components[i].transform(this.externalProjection, this.internalProjection) │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - return this.div │ │ │ │ - }, │ │ │ │ - baseLayerDraw: function() { │ │ │ │ - this.draw(); │ │ │ │ - this.map.events.unregister("changebaselayer", this, this.baseLayerDraw) │ │ │ │ + return components │ │ │ │ }, │ │ │ │ - rectDrag: function(px) { │ │ │ │ - var deltaX = this.handlers.drag.last.x - px.x; │ │ │ │ - var deltaY = this.handlers.drag.last.y - px.y; │ │ │ │ - if (deltaX != 0 || deltaY != 0) { │ │ │ │ - var rectTop = this.rectPxBounds.top; │ │ │ │ - var rectLeft = this.rectPxBounds.left; │ │ │ │ - var rectHeight = Math.abs(this.rectPxBounds.getHeight()); │ │ │ │ - var rectWidth = this.rectPxBounds.getWidth(); │ │ │ │ - var newTop = Math.max(0, rectTop - deltaY); │ │ │ │ - newTop = Math.min(newTop, this.ovmap.size.h - this.hComp - rectHeight); │ │ │ │ - var newLeft = Math.max(0, rectLeft - deltaX); │ │ │ │ - newLeft = Math.min(newLeft, this.ovmap.size.w - this.wComp - rectWidth); │ │ │ │ - this.setRectPxBounds(new OpenLayers.Bounds(newLeft, newTop + rectHeight, newLeft + rectWidth, newTop)) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - mapDivClick: function(evt) { │ │ │ │ - var pxCenter = this.rectPxBounds.getCenterPixel(); │ │ │ │ - var deltaX = evt.xy.x - pxCenter.x; │ │ │ │ - var deltaY = evt.xy.y - pxCenter.y; │ │ │ │ - var top = this.rectPxBounds.top; │ │ │ │ - var left = this.rectPxBounds.left; │ │ │ │ - var height = Math.abs(this.rectPxBounds.getHeight()); │ │ │ │ - var width = this.rectPxBounds.getWidth(); │ │ │ │ - var newTop = Math.max(0, top + deltaY); │ │ │ │ - newTop = Math.min(newTop, this.ovmap.size.h - height); │ │ │ │ - var newLeft = Math.max(0, left + deltaX); │ │ │ │ - newLeft = Math.min(newLeft, this.ovmap.size.w - width); │ │ │ │ - this.setRectPxBounds(new OpenLayers.Bounds(newLeft, newTop + height, newLeft + width, newTop)); │ │ │ │ - this.updateMapToRect() │ │ │ │ - }, │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - if (evt.buttonElement === this.minimizeDiv) { │ │ │ │ - this.minimizeControl() │ │ │ │ - } else if (evt.buttonElement === this.maximizeDiv) { │ │ │ │ - this.maximizeControl() │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - maximizeControl: function(e) { │ │ │ │ - this.element.style.display = ""; │ │ │ │ - this.showToggle(false); │ │ │ │ - if (e != null) { │ │ │ │ - OpenLayers.Event.stop(e) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - minimizeControl: function(e) { │ │ │ │ - this.element.style.display = "none"; │ │ │ │ - this.showToggle(true); │ │ │ │ - if (e != null) { │ │ │ │ - OpenLayers.Event.stop(e) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - showToggle: function(minimize) { │ │ │ │ - if (this.maximizeDiv) { │ │ │ │ - this.maximizeDiv.style.display = minimize ? "" : "none" │ │ │ │ - } │ │ │ │ - if (this.minimizeDiv) { │ │ │ │ - this.minimizeDiv.style.display = minimize ? "none" : "" │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - update: function() { │ │ │ │ - if (this.ovmap == null) { │ │ │ │ - this.createMap() │ │ │ │ - } │ │ │ │ - if (this.autoPan || !this.isSuitableOverview()) { │ │ │ │ - this.updateOverview() │ │ │ │ - } │ │ │ │ - this.updateRectToMap() │ │ │ │ - }, │ │ │ │ - isSuitableOverview: function() { │ │ │ │ - var mapExtent = this.map.getExtent(); │ │ │ │ - var maxExtent = this.map.getMaxExtent(); │ │ │ │ - var testExtent = new OpenLayers.Bounds(Math.max(mapExtent.left, maxExtent.left), Math.max(mapExtent.bottom, maxExtent.bottom), Math.min(mapExtent.right, maxExtent.right), Math.min(mapExtent.top, maxExtent.top)); │ │ │ │ - if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ - testExtent = testExtent.transform(this.map.getProjectionObject(), this.ovmap.getProjectionObject()) │ │ │ │ - } │ │ │ │ - var resRatio = this.ovmap.getResolution() / this.map.getResolution(); │ │ │ │ - return resRatio > this.minRatio && resRatio <= this.maxRatio && this.ovmap.getExtent().containsBounds(testExtent) │ │ │ │ - }, │ │ │ │ - updateOverview: function() { │ │ │ │ - var mapRes = this.map.getResolution(); │ │ │ │ - var targetRes = this.ovmap.getResolution(); │ │ │ │ - var resRatio = targetRes / mapRes; │ │ │ │ - if (resRatio > this.maxRatio) { │ │ │ │ - targetRes = this.minRatio * mapRes │ │ │ │ - } else if (resRatio <= this.minRatio) { │ │ │ │ - targetRes = this.maxRatio * mapRes │ │ │ │ + parsePersonConstructs: function(node, name, data) { │ │ │ │ + var persons = []; │ │ │ │ + var atomns = this.namespaces.atom; │ │ │ │ + var nodes = this.getElementsByTagNameNS(node, atomns, name); │ │ │ │ + var oAtts = ["uri", "email"]; │ │ │ │ + for (var i = 0, ii = nodes.length; i < ii; i++) { │ │ │ │ + var value = {}; │ │ │ │ + value.name = this.getFirstChildValue(nodes[i], atomns, "name", null); │ │ │ │ + for (var j = 0, jj = oAtts.length; j < jj; j++) { │ │ │ │ + var attval = this.getFirstChildValue(nodes[i], atomns, oAtts[j], null); │ │ │ │ + if (attval) { │ │ │ │ + value[oAtts[j]] = attval │ │ │ │ + } │ │ │ │ + } │ │ │ │ + persons.push(value) │ │ │ │ } │ │ │ │ - var center; │ │ │ │ - if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ - center = this.map.center.clone(); │ │ │ │ - center.transform(this.map.getProjectionObject(), this.ovmap.getProjectionObject()) │ │ │ │ - } else { │ │ │ │ - center = this.map.center │ │ │ │ + if (persons.length > 0) { │ │ │ │ + data[name + "s"] = persons │ │ │ │ } │ │ │ │ - this.ovmap.setCenter(center, this.ovmap.getZoomForResolution(targetRes * this.resolutionFactor)); │ │ │ │ - this.updateRectToMap() │ │ │ │ }, │ │ │ │ - createMap: function() { │ │ │ │ - var options = OpenLayers.Util.extend({ │ │ │ │ - controls: [], │ │ │ │ - maxResolution: "auto", │ │ │ │ - fallThrough: false │ │ │ │ - }, this.mapOptions); │ │ │ │ - this.ovmap = new OpenLayers.Map(this.mapDiv, options); │ │ │ │ - this.ovmap.viewPortDiv.appendChild(this.extentRectangle); │ │ │ │ - OpenLayers.Event.stopObserving(window, "unload", this.ovmap.unloadDestroy); │ │ │ │ - this.ovmap.addLayers(this.layers); │ │ │ │ - this.ovmap.zoomToMaxExtent(); │ │ │ │ - this.wComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle, "border-left-width")) + parseInt(OpenLayers.Element.getStyle(this.extentRectangle, "border-right-width")); │ │ │ │ - this.wComp = this.wComp ? this.wComp : 2; │ │ │ │ - this.hComp = parseInt(OpenLayers.Element.getStyle(this.extentRectangle, "border-top-width")) + parseInt(OpenLayers.Element.getStyle(this.extentRectangle, "border-bottom-width")); │ │ │ │ - this.hComp = this.hComp ? this.hComp : 2; │ │ │ │ - this.handlers.drag = new OpenLayers.Handler.Drag(this, { │ │ │ │ - move: this.rectDrag, │ │ │ │ - done: this.updateMapToRect │ │ │ │ - }, { │ │ │ │ - map: this.ovmap │ │ │ │ - }); │ │ │ │ - this.handlers.click = new OpenLayers.Handler.Click(this, { │ │ │ │ - click: this.mapDivClick │ │ │ │ - }, { │ │ │ │ - single: true, │ │ │ │ - double: false, │ │ │ │ - stopSingle: true, │ │ │ │ - stopDouble: true, │ │ │ │ - pixelTolerance: 1, │ │ │ │ - map: this.ovmap │ │ │ │ - }); │ │ │ │ - this.handlers.click.activate(); │ │ │ │ - this.rectEvents = new OpenLayers.Events(this, this.extentRectangle, null, true); │ │ │ │ - this.rectEvents.register("mouseover", this, function(e) { │ │ │ │ - if (!this.handlers.drag.active && !this.map.dragging) { │ │ │ │ - this.handlers.drag.activate() │ │ │ │ - } │ │ │ │ - }); │ │ │ │ - this.rectEvents.register("mouseout", this, function(e) { │ │ │ │ - if (!this.handlers.drag.dragging) { │ │ │ │ - this.handlers.drag.deactivate() │ │ │ │ + CLASS_NAME: "OpenLayers.Format.Atom" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.WPSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + defaultVersion: "1.0.0", │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WPSCapabilities" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.Context = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + layerOptions: null, │ │ │ │ + layerParams: null, │ │ │ │ + read: function(data, options) { │ │ │ │ + var context = OpenLayers.Format.XML.VersionedOGC.prototype.read.apply(this, arguments); │ │ │ │ + var map; │ │ │ │ + if (options && options.map) { │ │ │ │ + this.context = context; │ │ │ │ + if (options.map instanceof OpenLayers.Map) { │ │ │ │ + map = this.mergeContextToMap(context, options.map) │ │ │ │ + } else { │ │ │ │ + var mapOptions = options.map; │ │ │ │ + if (OpenLayers.Util.isElement(mapOptions) || typeof mapOptions == "string") { │ │ │ │ + mapOptions = { │ │ │ │ + div: mapOptions │ │ │ │ + } │ │ │ │ + } │ │ │ │ + map = this.contextToMap(context, mapOptions) │ │ │ │ } │ │ │ │ - }); │ │ │ │ - if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ - var sourceUnits = this.map.getProjectionObject().getUnits() || this.map.units || this.map.baseLayer.units; │ │ │ │ - var targetUnits = this.ovmap.getProjectionObject().getUnits() || this.ovmap.units || this.ovmap.baseLayer.units; │ │ │ │ - this.resolutionFactor = sourceUnits && targetUnits ? OpenLayers.INCHES_PER_UNIT[sourceUnits] / OpenLayers.INCHES_PER_UNIT[targetUnits] : 1 │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - updateRectToMap: function() { │ │ │ │ - var bounds; │ │ │ │ - if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ - bounds = this.map.getExtent().transform(this.map.getProjectionObject(), this.ovmap.getProjectionObject()) │ │ │ │ } else { │ │ │ │ - bounds = this.map.getExtent() │ │ │ │ - } │ │ │ │ - var pxBounds = this.getRectBoundsFromMapBounds(bounds); │ │ │ │ - if (pxBounds) { │ │ │ │ - this.setRectPxBounds(pxBounds) │ │ │ │ + map = context │ │ │ │ } │ │ │ │ + return map │ │ │ │ }, │ │ │ │ - updateMapToRect: function() { │ │ │ │ - var lonLatBounds = this.getMapBoundsFromRectBounds(this.rectPxBounds); │ │ │ │ - if (this.ovmap.getProjection() != this.map.getProjection()) { │ │ │ │ - lonLatBounds = lonLatBounds.transform(this.ovmap.getProjectionObject(), this.map.getProjectionObject()) │ │ │ │ + getLayerFromContext: function(layerContext) { │ │ │ │ + var i, len; │ │ │ │ + var options = { │ │ │ │ + queryable: layerContext.queryable, │ │ │ │ + visibility: layerContext.visibility, │ │ │ │ + maxExtent: layerContext.maxExtent, │ │ │ │ + metadata: OpenLayers.Util.applyDefaults(layerContext.metadata, { │ │ │ │ + styles: layerContext.styles, │ │ │ │ + formats: layerContext.formats, │ │ │ │ + abstract: layerContext["abstract"], │ │ │ │ + dataURL: layerContext.dataURL │ │ │ │ + }), │ │ │ │ + numZoomLevels: layerContext.numZoomLevels, │ │ │ │ + units: layerContext.units, │ │ │ │ + isBaseLayer: layerContext.isBaseLayer, │ │ │ │ + opacity: layerContext.opacity, │ │ │ │ + displayInLayerSwitcher: layerContext.displayInLayerSwitcher, │ │ │ │ + singleTile: layerContext.singleTile, │ │ │ │ + tileSize: layerContext.tileSize ? new OpenLayers.Size(layerContext.tileSize.width, layerContext.tileSize.height) : undefined, │ │ │ │ + minScale: layerContext.minScale || layerContext.maxScaleDenominator, │ │ │ │ + maxScale: layerContext.maxScale || layerContext.minScaleDenominator, │ │ │ │ + srs: layerContext.srs, │ │ │ │ + dimensions: layerContext.dimensions, │ │ │ │ + metadataURL: layerContext.metadataURL │ │ │ │ + }; │ │ │ │ + if (this.layerOptions) { │ │ │ │ + OpenLayers.Util.applyDefaults(options, this.layerOptions) │ │ │ │ } │ │ │ │ - this.map.panTo(lonLatBounds.getCenterLonLat()) │ │ │ │ - }, │ │ │ │ - setRectPxBounds: function(pxBounds) { │ │ │ │ - var top = Math.max(pxBounds.top, 0); │ │ │ │ - var left = Math.max(pxBounds.left, 0); │ │ │ │ - var bottom = Math.min(pxBounds.top + Math.abs(pxBounds.getHeight()), this.ovmap.size.h - this.hComp); │ │ │ │ - var right = Math.min(pxBounds.left + pxBounds.getWidth(), this.ovmap.size.w - this.wComp); │ │ │ │ - var width = Math.max(right - left, 0); │ │ │ │ - var height = Math.max(bottom - top, 0); │ │ │ │ - if (width < this.minRectSize || height < this.minRectSize) { │ │ │ │ - this.extentRectangle.className = this.displayClass + this.minRectDisplayClass; │ │ │ │ - var rLeft = left + width / 2 - this.minRectSize / 2; │ │ │ │ - var rTop = top + height / 2 - this.minRectSize / 2; │ │ │ │ - this.extentRectangle.style.top = Math.round(rTop) + "px"; │ │ │ │ - this.extentRectangle.style.left = Math.round(rLeft) + "px"; │ │ │ │ - this.extentRectangle.style.height = this.minRectSize + "px"; │ │ │ │ - this.extentRectangle.style.width = this.minRectSize + "px" │ │ │ │ - } else { │ │ │ │ - this.extentRectangle.className = this.displayClass + "ExtentRectangle"; │ │ │ │ - this.extentRectangle.style.top = Math.round(top) + "px"; │ │ │ │ - this.extentRectangle.style.left = Math.round(left) + "px"; │ │ │ │ - this.extentRectangle.style.height = Math.round(height) + "px"; │ │ │ │ - this.extentRectangle.style.width = Math.round(width) + "px" │ │ │ │ + var params = { │ │ │ │ + layers: layerContext.name, │ │ │ │ + transparent: layerContext.transparent, │ │ │ │ + version: layerContext.version │ │ │ │ + }; │ │ │ │ + if (layerContext.formats && layerContext.formats.length > 0) { │ │ │ │ + params.format = layerContext.formats[0].value; │ │ │ │ + for (i = 0, len = layerContext.formats.length; i < len; i++) { │ │ │ │ + var format = layerContext.formats[i]; │ │ │ │ + if (format.current == true) { │ │ │ │ + params.format = format.value; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.rectPxBounds = new OpenLayers.Bounds(Math.round(left), Math.round(bottom), Math.round(right), Math.round(top)) │ │ │ │ - }, │ │ │ │ - getRectBoundsFromMapBounds: function(lonLatBounds) { │ │ │ │ - var leftBottomPx = this.getOverviewPxFromLonLat({ │ │ │ │ - lon: lonLatBounds.left, │ │ │ │ - lat: lonLatBounds.bottom │ │ │ │ - }); │ │ │ │ - var rightTopPx = this.getOverviewPxFromLonLat({ │ │ │ │ - lon: lonLatBounds.right, │ │ │ │ - lat: lonLatBounds.top │ │ │ │ - }); │ │ │ │ - var bounds = null; │ │ │ │ - if (leftBottomPx && rightTopPx) { │ │ │ │ - bounds = new OpenLayers.Bounds(leftBottomPx.x, leftBottomPx.y, rightTopPx.x, rightTopPx.y) │ │ │ │ + if (layerContext.styles && layerContext.styles.length > 0) { │ │ │ │ + for (i = 0, len = layerContext.styles.length; i < len; i++) { │ │ │ │ + var style = layerContext.styles[i]; │ │ │ │ + if (style.current == true) { │ │ │ │ + if (style.href) { │ │ │ │ + params.sld = style.href │ │ │ │ + } else if (style.body) { │ │ │ │ + params.sld_body = style.body │ │ │ │ + } else { │ │ │ │ + params.styles = style.name │ │ │ │ + } │ │ │ │ + break │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - return bounds │ │ │ │ - }, │ │ │ │ - getMapBoundsFromRectBounds: function(pxBounds) { │ │ │ │ - var leftBottomLonLat = this.getLonLatFromOverviewPx({ │ │ │ │ - x: pxBounds.left, │ │ │ │ - y: pxBounds.bottom │ │ │ │ - }); │ │ │ │ - var rightTopLonLat = this.getLonLatFromOverviewPx({ │ │ │ │ - x: pxBounds.right, │ │ │ │ - y: pxBounds.top │ │ │ │ - }); │ │ │ │ - return new OpenLayers.Bounds(leftBottomLonLat.lon, leftBottomLonLat.lat, rightTopLonLat.lon, rightTopLonLat.lat) │ │ │ │ - }, │ │ │ │ - getLonLatFromOverviewPx: function(overviewMapPx) { │ │ │ │ - var size = this.ovmap.size; │ │ │ │ - var res = this.ovmap.getResolution(); │ │ │ │ - var center = this.ovmap.getExtent().getCenterLonLat(); │ │ │ │ - var deltaX = overviewMapPx.x - size.w / 2; │ │ │ │ - var deltaY = overviewMapPx.y - size.h / 2; │ │ │ │ - return { │ │ │ │ - lon: center.lon + deltaX * res, │ │ │ │ - lat: center.lat - deltaY * res │ │ │ │ + if (this.layerParams) { │ │ │ │ + OpenLayers.Util.applyDefaults(params, this.layerParams) │ │ │ │ + } │ │ │ │ + var layer = null; │ │ │ │ + var service = layerContext.service; │ │ │ │ + if (service == OpenLayers.Format.Context.serviceTypes.WFS) { │ │ │ │ + options.strategies = [new OpenLayers.Strategy.BBOX]; │ │ │ │ + options.protocol = new OpenLayers.Protocol.WFS({ │ │ │ │ + url: layerContext.url, │ │ │ │ + featurePrefix: layerContext.name.split(":")[0], │ │ │ │ + featureType: layerContext.name.split(":").pop() │ │ │ │ + }); │ │ │ │ + layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options) │ │ │ │ + } else if (service == OpenLayers.Format.Context.serviceTypes.KML) { │ │ │ │ + options.strategies = [new OpenLayers.Strategy.Fixed]; │ │ │ │ + options.protocol = new OpenLayers.Protocol.HTTP({ │ │ │ │ + url: layerContext.url, │ │ │ │ + format: new OpenLayers.Format.KML │ │ │ │ + }); │ │ │ │ + layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options) │ │ │ │ + } else if (service == OpenLayers.Format.Context.serviceTypes.GML) { │ │ │ │ + options.strategies = [new OpenLayers.Strategy.Fixed]; │ │ │ │ + options.protocol = new OpenLayers.Protocol.HTTP({ │ │ │ │ + url: layerContext.url, │ │ │ │ + format: new OpenLayers.Format.GML │ │ │ │ + }); │ │ │ │ + layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options) │ │ │ │ + } else if (layerContext.features) { │ │ │ │ + layer = new OpenLayers.Layer.Vector(layerContext.title || layerContext.name, options); │ │ │ │ + layer.addFeatures(layerContext.features) │ │ │ │ + } else if (layerContext.categoryLayer !== true) { │ │ │ │ + layer = new OpenLayers.Layer.WMS(layerContext.title || layerContext.name, layerContext.url, params, options) │ │ │ │ } │ │ │ │ + return layer │ │ │ │ }, │ │ │ │ - getOverviewPxFromLonLat: function(lonlat) { │ │ │ │ - var res = this.ovmap.getResolution(); │ │ │ │ - var extent = this.ovmap.getExtent(); │ │ │ │ - if (extent) { │ │ │ │ - return { │ │ │ │ - x: Math.round(1 / res * (lonlat.lon - extent.left)), │ │ │ │ - y: Math.round(1 / res * (extent.top - lonlat.lat)) │ │ │ │ + getLayersFromContext: function(layersContext) { │ │ │ │ + var layers = []; │ │ │ │ + for (var i = 0, len = layersContext.length; i < len; i++) { │ │ │ │ + var layer = this.getLayerFromContext(layersContext[i]); │ │ │ │ + if (layer !== null) { │ │ │ │ + layers.push(layer) │ │ │ │ } │ │ │ │ } │ │ │ │ + return layers │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.OverviewMap" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Scale = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - element: null, │ │ │ │ - geodesic: false, │ │ │ │ - initialize: function(element, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.element = OpenLayers.Util.getElement(element) │ │ │ │ - }, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (!this.element) { │ │ │ │ - this.element = document.createElement("div"); │ │ │ │ - this.div.appendChild(this.element) │ │ │ │ + contextToMap: function(context, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults({ │ │ │ │ + maxExtent: context.maxExtent, │ │ │ │ + projection: context.projection, │ │ │ │ + units: context.units │ │ │ │ + }, options); │ │ │ │ + if (options.maxExtent) { │ │ │ │ + options.maxResolution = options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH │ │ │ │ } │ │ │ │ - this.map.events.register("moveend", this, this.updateScale); │ │ │ │ - this.updateScale(); │ │ │ │ - return this.div │ │ │ │ + var metadata = { │ │ │ │ + contactInformation: context.contactInformation, │ │ │ │ + abstract: context["abstract"], │ │ │ │ + keywords: context.keywords, │ │ │ │ + logo: context.logo, │ │ │ │ + descriptionURL: context.descriptionURL │ │ │ │ + }; │ │ │ │ + options.metadata = metadata; │ │ │ │ + var map = new OpenLayers.Map(options); │ │ │ │ + map.addLayers(this.getLayersFromContext(context.layersContext)); │ │ │ │ + map.setCenter(context.bounds.getCenterLonLat(), map.getZoomForExtent(context.bounds, true)); │ │ │ │ + return map │ │ │ │ }, │ │ │ │ - updateScale: function() { │ │ │ │ - var scale; │ │ │ │ - if (this.geodesic === true) { │ │ │ │ - var units = this.map.getUnits(); │ │ │ │ - if (!units) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var inches = OpenLayers.INCHES_PER_UNIT; │ │ │ │ - scale = (this.map.getGeodesicPixelSize().w || 1e-6) * inches["km"] * OpenLayers.DOTS_PER_INCH │ │ │ │ - } else { │ │ │ │ - scale = this.map.getScale() │ │ │ │ - } │ │ │ │ - if (!scale) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - if (scale >= 9500 && scale <= 95e4) { │ │ │ │ - scale = Math.round(scale / 1e3) + "K" │ │ │ │ - } else if (scale >= 95e4) { │ │ │ │ - scale = Math.round(scale / 1e6) + "M" │ │ │ │ - } else { │ │ │ │ - scale = Math.round(scale) │ │ │ │ - } │ │ │ │ - this.element.innerHTML = OpenLayers.i18n("Scale = 1 : ${scaleDenom}", { │ │ │ │ - scaleDenom: scale │ │ │ │ - }) │ │ │ │ + mergeContextToMap: function(context, map) { │ │ │ │ + map.addLayers(this.getLayersFromContext(context.layersContext)); │ │ │ │ + return map │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Scale" │ │ │ │ + write: function(obj, options) { │ │ │ │ + obj = this.toContext(obj); │ │ │ │ + return OpenLayers.Format.XML.VersionedOGC.prototype.write.apply(this, arguments) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.Context" │ │ │ │ }); │ │ │ │ -OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - autoActivate: true, │ │ │ │ - intervals: [45, 30, 20, 10, 5, 2, 1, .5, .2, .1, .05, .01, .005, .002, .001], │ │ │ │ - displayInLayerSwitcher: true, │ │ │ │ - visible: true, │ │ │ │ - numPoints: 50, │ │ │ │ - targetSize: 200, │ │ │ │ - layerName: null, │ │ │ │ - labelled: true, │ │ │ │ - labelFormat: "dm", │ │ │ │ - lineSymbolizer: { │ │ │ │ - strokeColor: "#333", │ │ │ │ - strokeWidth: 1, │ │ │ │ - strokeOpacity: .5 │ │ │ │ +OpenLayers.Format.Context.serviceTypes = { │ │ │ │ + WMS: "urn:ogc:serviceType:WMS", │ │ │ │ + WFS: "urn:ogc:serviceType:WFS", │ │ │ │ + WCS: "urn:ogc:serviceType:WCS", │ │ │ │ + GML: "urn:ogc:serviceType:GML", │ │ │ │ + SLD: "urn:ogc:serviceType:SLD", │ │ │ │ + FES: "urn:ogc:serviceType:FES", │ │ │ │ + KML: "urn:ogc:serviceType:KML" │ │ │ │ +}; │ │ │ │ +OpenLayers.Format.KML = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + namespaces: { │ │ │ │ + kml: "http://www.opengis.net/kml/2.2", │ │ │ │ + gx: "http://www.google.com/kml/ext/2.2" │ │ │ │ }, │ │ │ │ - labelSymbolizer: {}, │ │ │ │ - gratLayer: null, │ │ │ │ + kmlns: "http://earth.google.com/kml/2.0", │ │ │ │ + placemarksDesc: "No description available", │ │ │ │ + foldersName: "OpenLayers export", │ │ │ │ + foldersDesc: "Exported on " + new Date, │ │ │ │ + extractAttributes: true, │ │ │ │ + kvpAttributes: false, │ │ │ │ + extractStyles: false, │ │ │ │ + extractTracks: false, │ │ │ │ + trackAttributes: null, │ │ │ │ + internalns: null, │ │ │ │ + features: null, │ │ │ │ + styles: null, │ │ │ │ + styleBaseUrl: "", │ │ │ │ + fetched: null, │ │ │ │ + maxDepth: 0, │ │ │ │ initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - options.layerName = options.layerName || OpenLayers.i18n("Graticule"); │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.labelSymbolizer.stroke = false; │ │ │ │ - this.labelSymbolizer.fill = false; │ │ │ │ - this.labelSymbolizer.label = "${label}"; │ │ │ │ - this.labelSymbolizer.labelAlign = "${labelAlign}"; │ │ │ │ - this.labelSymbolizer.labelXOffset = "${xOffset}"; │ │ │ │ - this.labelSymbolizer.labelYOffset = "${yOffset}" │ │ │ │ + this.regExes = { │ │ │ │ + trimSpace: /^\s*|\s*$/g, │ │ │ │ + removeSpace: /\s*/g, │ │ │ │ + splitSpace: /\s+/, │ │ │ │ + trimComma: /\s*,\s*/g, │ │ │ │ + kmlColor: /(\w{2})(\w{2})(\w{2})(\w{2})/, │ │ │ │ + kmlIconPalette: /root:\/\/icons\/palette-(\d+)(\.\w+)/, │ │ │ │ + straightBracket: /\$\[(.*?)\]/g │ │ │ │ + }; │ │ │ │ + this.externalProjection = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]) │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - if (this.gratLayer) { │ │ │ │ - this.gratLayer.destroy(); │ │ │ │ - this.gratLayer = null │ │ │ │ - } │ │ │ │ + read: function(data) { │ │ │ │ + this.features = []; │ │ │ │ + this.styles = {}; │ │ │ │ + this.fetched = {}; │ │ │ │ + var options = { │ │ │ │ + depth: 0, │ │ │ │ + styleBaseUrl: this.styleBaseUrl │ │ │ │ + }; │ │ │ │ + return this.parseData(data, options) │ │ │ │ }, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (!this.gratLayer) { │ │ │ │ - var gratStyle = new OpenLayers.Style({}, { │ │ │ │ - rules: [new OpenLayers.Rule({ │ │ │ │ - symbolizer: { │ │ │ │ - Point: this.labelSymbolizer, │ │ │ │ - Line: this.lineSymbolizer │ │ │ │ + parseData: function(data, options) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]) │ │ │ │ + } │ │ │ │ + var types = ["Link", "NetworkLink", "Style", "StyleMap", "Placemark"]; │ │ │ │ + for (var i = 0, len = types.length; i < len; ++i) { │ │ │ │ + var type = types[i]; │ │ │ │ + var nodes = this.getElementsByTagNameNS(data, "*", type); │ │ │ │ + if (nodes.length == 0) { │ │ │ │ + continue │ │ │ │ + } │ │ │ │ + switch (type.toLowerCase()) { │ │ │ │ + case "link": │ │ │ │ + case "networklink": │ │ │ │ + this.parseLinks(nodes, options); │ │ │ │ + break; │ │ │ │ + case "style": │ │ │ │ + if (this.extractStyles) { │ │ │ │ + this.parseStyles(nodes, options) │ │ │ │ } │ │ │ │ - })] │ │ │ │ - }); │ │ │ │ - this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, { │ │ │ │ - styleMap: new OpenLayers.StyleMap({ │ │ │ │ - default: gratStyle │ │ │ │ - }), │ │ │ │ - visibility: this.visible, │ │ │ │ - displayInLayerSwitcher: this.displayInLayerSwitcher │ │ │ │ - }) │ │ │ │ + break; │ │ │ │ + case "stylemap": │ │ │ │ + if (this.extractStyles) { │ │ │ │ + this.parseStyleMaps(nodes, options) │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case "placemark": │ │ │ │ + this.parseFeatures(nodes, options); │ │ │ │ + break │ │ │ │ + } │ │ │ │ } │ │ │ │ - return this.div │ │ │ │ + return this.features │ │ │ │ }, │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.map.addLayer(this.gratLayer); │ │ │ │ - this.map.events.register("moveend", this, this.update); │ │ │ │ - this.update(); │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ + parseLinks: function(nodes, options) { │ │ │ │ + if (options.depth >= this.maxDepth) { │ │ │ │ return false │ │ │ │ } │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.map.events.unregister("moveend", this, this.update); │ │ │ │ - this.map.removeLayer(this.gratLayer); │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ + var newOptions = OpenLayers.Util.extend({}, options); │ │ │ │ + newOptions.depth++; │ │ │ │ + for (var i = 0, len = nodes.length; i < len; i++) { │ │ │ │ + var href = this.parseProperty(nodes[i], "*", "href"); │ │ │ │ + if (href && !this.fetched[href]) { │ │ │ │ + this.fetched[href] = true; │ │ │ │ + var data = this.fetchLink(href); │ │ │ │ + if (data) { │ │ │ │ + this.parseData(data, newOptions) │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ - update: function() { │ │ │ │ - var mapBounds = this.map.getExtent(); │ │ │ │ - if (!mapBounds) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - this.gratLayer.destroyFeatures(); │ │ │ │ - var llProj = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ - var mapProj = this.map.getProjectionObject(); │ │ │ │ - var mapRes = this.map.getResolution(); │ │ │ │ - if (mapProj.proj && mapProj.proj.projName == "longlat") { │ │ │ │ - this.numPoints = 1 │ │ │ │ + fetchLink: function(href) { │ │ │ │ + var request = OpenLayers.Request.GET({ │ │ │ │ + url: href, │ │ │ │ + async: false │ │ │ │ + }); │ │ │ │ + if (request) { │ │ │ │ + return request.responseText │ │ │ │ } │ │ │ │ - var mapCenter = this.map.getCenter(); │ │ │ │ - var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat); │ │ │ │ - OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj); │ │ │ │ - var testSq = this.targetSize * mapRes; │ │ │ │ - testSq *= testSq; │ │ │ │ - var llInterval; │ │ │ │ - for (var i = 0; i < this.intervals.length; ++i) { │ │ │ │ - llInterval = this.intervals[i]; │ │ │ │ - var delta = llInterval / 2; │ │ │ │ - var p1 = mapCenterLL.offset({ │ │ │ │ - x: -delta, │ │ │ │ - y: -delta │ │ │ │ - }); │ │ │ │ - var p2 = mapCenterLL.offset({ │ │ │ │ - x: delta, │ │ │ │ - y: delta │ │ │ │ - }); │ │ │ │ - OpenLayers.Projection.transform(p1, llProj, mapProj); │ │ │ │ - OpenLayers.Projection.transform(p2, llProj, mapProj); │ │ │ │ - var distSq = (p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y); │ │ │ │ - if (distSq <= testSq) { │ │ │ │ - break │ │ │ │ + }, │ │ │ │ + parseStyles: function(nodes, options) { │ │ │ │ + for (var i = 0, len = nodes.length; i < len; i++) { │ │ │ │ + var style = this.parseStyle(nodes[i]); │ │ │ │ + if (style) { │ │ │ │ + var styleName = (options.styleBaseUrl || "") + "#" + style.id; │ │ │ │ + this.styles[styleName] = style │ │ │ │ } │ │ │ │ } │ │ │ │ - mapCenterLL.x = Math.floor(mapCenterLL.x / llInterval) * llInterval; │ │ │ │ - mapCenterLL.y = Math.floor(mapCenterLL.y / llInterval) * llInterval; │ │ │ │ - var iter = 0; │ │ │ │ - var centerLonPoints = [mapCenterLL.clone()]; │ │ │ │ - var newPoint = mapCenterLL.clone(); │ │ │ │ - var mapXY; │ │ │ │ - do { │ │ │ │ - newPoint = newPoint.offset({ │ │ │ │ - x: 0, │ │ │ │ - y: llInterval │ │ │ │ - }); │ │ │ │ - mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); │ │ │ │ - centerLonPoints.unshift(newPoint) │ │ │ │ - } while (mapBounds.containsPixel(mapXY) && ++iter < 1e3); │ │ │ │ - newPoint = mapCenterLL.clone(); │ │ │ │ - do { │ │ │ │ - newPoint = newPoint.offset({ │ │ │ │ - x: 0, │ │ │ │ - y: -llInterval │ │ │ │ - }); │ │ │ │ - mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); │ │ │ │ - centerLonPoints.push(newPoint) │ │ │ │ - } while (mapBounds.containsPixel(mapXY) && ++iter < 1e3); │ │ │ │ - iter = 0; │ │ │ │ - var centerLatPoints = [mapCenterLL.clone()]; │ │ │ │ - newPoint = mapCenterLL.clone(); │ │ │ │ - do { │ │ │ │ - newPoint = newPoint.offset({ │ │ │ │ - x: -llInterval, │ │ │ │ - y: 0 │ │ │ │ - }); │ │ │ │ - mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); │ │ │ │ - centerLatPoints.unshift(newPoint) │ │ │ │ - } while (mapBounds.containsPixel(mapXY) && ++iter < 1e3); │ │ │ │ - newPoint = mapCenterLL.clone(); │ │ │ │ - do { │ │ │ │ - newPoint = newPoint.offset({ │ │ │ │ - x: llInterval, │ │ │ │ - y: 0 │ │ │ │ - }); │ │ │ │ - mapXY = OpenLayers.Projection.transform(newPoint.clone(), llProj, mapProj); │ │ │ │ - centerLatPoints.push(newPoint) │ │ │ │ - } while (mapBounds.containsPixel(mapXY) && ++iter < 1e3); │ │ │ │ - var lines = []; │ │ │ │ - for (var i = 0; i < centerLatPoints.length; ++i) { │ │ │ │ - var lon = centerLatPoints[i].x; │ │ │ │ - var pointList = []; │ │ │ │ - var labelPoint = null; │ │ │ │ - var latEnd = Math.min(centerLonPoints[0].y, 90); │ │ │ │ - var latStart = Math.max(centerLonPoints[centerLonPoints.length - 1].y, -90); │ │ │ │ - var latDelta = (latEnd - latStart) / this.numPoints; │ │ │ │ - var lat = latStart; │ │ │ │ - for (var j = 0; j <= this.numPoints; ++j) { │ │ │ │ - var gridPoint = new OpenLayers.Geometry.Point(lon, lat); │ │ │ │ - gridPoint.transform(llProj, mapProj); │ │ │ │ - pointList.push(gridPoint); │ │ │ │ - lat += latDelta; │ │ │ │ - if (gridPoint.y >= mapBounds.bottom && !labelPoint) { │ │ │ │ - labelPoint = gridPoint │ │ │ │ + }, │ │ │ │ + parseKmlColor: function(kmlColor) { │ │ │ │ + var color = null; │ │ │ │ + if (kmlColor) { │ │ │ │ + var matches = kmlColor.match(this.regExes.kmlColor); │ │ │ │ + if (matches) { │ │ │ │ + color = { │ │ │ │ + color: "#" + matches[4] + matches[3] + matches[2], │ │ │ │ + opacity: parseInt(matches[1], 16) / 255 │ │ │ │ } │ │ │ │ } │ │ │ │ - if (this.labelled) { │ │ │ │ - var labelPos = new OpenLayers.Geometry.Point(labelPoint.x, mapBounds.bottom); │ │ │ │ - var labelAttrs = { │ │ │ │ - value: lon, │ │ │ │ - label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lon, "lon", this.labelFormat) : "", │ │ │ │ - labelAlign: "cb", │ │ │ │ - xOffset: 0, │ │ │ │ - yOffset: 2 │ │ │ │ - }; │ │ │ │ - this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs)) │ │ │ │ - } │ │ │ │ - var geom = new OpenLayers.Geometry.LineString(pointList); │ │ │ │ - lines.push(new OpenLayers.Feature.Vector(geom)) │ │ │ │ } │ │ │ │ - for (var j = 0; j < centerLonPoints.length; ++j) { │ │ │ │ - lat = centerLonPoints[j].y; │ │ │ │ - if (lat < -90 || lat > 90) { │ │ │ │ + return color │ │ │ │ + }, │ │ │ │ + parseStyle: function(node) { │ │ │ │ + var style = {}; │ │ │ │ + var types = ["LineStyle", "PolyStyle", "IconStyle", "BalloonStyle", "LabelStyle"]; │ │ │ │ + var type, styleTypeNode, nodeList, geometry, parser; │ │ │ │ + for (var i = 0, len = types.length; i < len; ++i) { │ │ │ │ + type = types[i]; │ │ │ │ + styleTypeNode = this.getElementsByTagNameNS(node, "*", type)[0]; │ │ │ │ + if (!styleTypeNode) { │ │ │ │ continue │ │ │ │ } │ │ │ │ - var pointList = []; │ │ │ │ - var lonStart = centerLatPoints[0].x; │ │ │ │ - var lonEnd = centerLatPoints[centerLatPoints.length - 1].x; │ │ │ │ - var lonDelta = (lonEnd - lonStart) / this.numPoints; │ │ │ │ - var lon = lonStart; │ │ │ │ - var labelPoint = null; │ │ │ │ - for (var i = 0; i <= this.numPoints; ++i) { │ │ │ │ - var gridPoint = new OpenLayers.Geometry.Point(lon, lat); │ │ │ │ - gridPoint.transform(llProj, mapProj); │ │ │ │ - pointList.push(gridPoint); │ │ │ │ - lon += lonDelta; │ │ │ │ - if (gridPoint.x < mapBounds.right) { │ │ │ │ - labelPoint = gridPoint │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.labelled) { │ │ │ │ - var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y); │ │ │ │ - var labelAttrs = { │ │ │ │ - value: lat, │ │ │ │ - label: this.labelled ? OpenLayers.Util.getFormattedLonLat(lat, "lat", this.labelFormat) : "", │ │ │ │ - labelAlign: "rb", │ │ │ │ - xOffset: -2, │ │ │ │ - yOffset: 2 │ │ │ │ - }; │ │ │ │ - this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos, labelAttrs)) │ │ │ │ + switch (type.toLowerCase()) { │ │ │ │ + case "linestyle": │ │ │ │ + var kmlColor = this.parseProperty(styleTypeNode, "*", "color"); │ │ │ │ + var color = this.parseKmlColor(kmlColor); │ │ │ │ + if (color) { │ │ │ │ + style["strokeColor"] = color.color; │ │ │ │ + style["strokeOpacity"] = color.opacity │ │ │ │ + } │ │ │ │ + var width = this.parseProperty(styleTypeNode, "*", "width"); │ │ │ │ + if (width) { │ │ │ │ + style["strokeWidth"] = width │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case "polystyle": │ │ │ │ + var kmlColor = this.parseProperty(styleTypeNode, "*", "color"); │ │ │ │ + var color = this.parseKmlColor(kmlColor); │ │ │ │ + if (color) { │ │ │ │ + style["fillOpacity"] = color.opacity; │ │ │ │ + style["fillColor"] = color.color │ │ │ │ + } │ │ │ │ + var fill = this.parseProperty(styleTypeNode, "*", "fill"); │ │ │ │ + if (fill == "0") { │ │ │ │ + style["fillColor"] = "none" │ │ │ │ + } │ │ │ │ + var outline = this.parseProperty(styleTypeNode, "*", "outline"); │ │ │ │ + if (outline == "0") { │ │ │ │ + style["strokeWidth"] = "0" │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case "iconstyle": │ │ │ │ + var scale = parseFloat(this.parseProperty(styleTypeNode, "*", "scale") || 1); │ │ │ │ + var width = 32 * scale; │ │ │ │ + var height = 32 * scale; │ │ │ │ + var iconNode = this.getElementsByTagNameNS(styleTypeNode, "*", "Icon")[0]; │ │ │ │ + if (iconNode) { │ │ │ │ + var href = this.parseProperty(iconNode, "*", "href"); │ │ │ │ + if (href) { │ │ │ │ + var w = this.parseProperty(iconNode, "*", "w"); │ │ │ │ + var h = this.parseProperty(iconNode, "*", "h"); │ │ │ │ + var google = "http://maps.google.com/mapfiles/kml"; │ │ │ │ + if (OpenLayers.String.startsWith(href, google) && !w && !h) { │ │ │ │ + w = 64; │ │ │ │ + h = 64; │ │ │ │ + scale = scale / 2 │ │ │ │ + } │ │ │ │ + w = w || h; │ │ │ │ + h = h || w; │ │ │ │ + if (w) { │ │ │ │ + width = parseInt(w) * scale │ │ │ │ + } │ │ │ │ + if (h) { │ │ │ │ + height = parseInt(h) * scale │ │ │ │ + } │ │ │ │ + var matches = href.match(this.regExes.kmlIconPalette); │ │ │ │ + if (matches) { │ │ │ │ + var palette = matches[1]; │ │ │ │ + var file_extension = matches[2]; │ │ │ │ + var x = this.parseProperty(iconNode, "*", "x"); │ │ │ │ + var y = this.parseProperty(iconNode, "*", "y"); │ │ │ │ + var posX = x ? x / 32 : 0; │ │ │ │ + var posY = y ? 7 - y / 32 : 7; │ │ │ │ + var pos = posY * 8 + posX; │ │ │ │ + href = "http://maps.google.com/mapfiles/kml/pal" + palette + "/icon" + pos + file_extension │ │ │ │ + } │ │ │ │ + style["graphicOpacity"] = 1; │ │ │ │ + style["externalGraphic"] = href │ │ │ │ + } │ │ │ │ + } │ │ │ │ + var hotSpotNode = this.getElementsByTagNameNS(styleTypeNode, "*", "hotSpot")[0]; │ │ │ │ + if (hotSpotNode) { │ │ │ │ + var x = parseFloat(hotSpotNode.getAttribute("x")); │ │ │ │ + var y = parseFloat(hotSpotNode.getAttribute("y")); │ │ │ │ + var xUnits = hotSpotNode.getAttribute("xunits"); │ │ │ │ + if (xUnits == "pixels") { │ │ │ │ + style["graphicXOffset"] = -x * scale │ │ │ │ + } else if (xUnits == "insetPixels") { │ │ │ │ + style["graphicXOffset"] = -width + x * scale │ │ │ │ + } else if (xUnits == "fraction") { │ │ │ │ + style["graphicXOffset"] = -width * x │ │ │ │ + } │ │ │ │ + var yUnits = hotSpotNode.getAttribute("yunits"); │ │ │ │ + if (yUnits == "pixels") { │ │ │ │ + style["graphicYOffset"] = -height + y * scale + 1 │ │ │ │ + } else if (yUnits == "insetPixels") { │ │ │ │ + style["graphicYOffset"] = -(y * scale) + 1 │ │ │ │ + } else if (yUnits == "fraction") { │ │ │ │ + style["graphicYOffset"] = -height * (1 - y) + 1 │ │ │ │ + } │ │ │ │ + } │ │ │ │ + style["graphicWidth"] = width; │ │ │ │ + style["graphicHeight"] = height; │ │ │ │ + break; │ │ │ │ + case "balloonstyle": │ │ │ │ + var balloonStyle = OpenLayers.Util.getXmlNodeValue(styleTypeNode); │ │ │ │ + if (balloonStyle) { │ │ │ │ + style["balloonStyle"] = balloonStyle.replace(this.regExes.straightBracket, "${$1}") │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case "labelstyle": │ │ │ │ + var kmlColor = this.parseProperty(styleTypeNode, "*", "color"); │ │ │ │ + var color = this.parseKmlColor(kmlColor); │ │ │ │ + if (color) { │ │ │ │ + style["fontColor"] = color.color; │ │ │ │ + style["fontOpacity"] = color.opacity │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ } │ │ │ │ - var geom = new OpenLayers.Geometry.LineString(pointList); │ │ │ │ - lines.push(new OpenLayers.Feature.Vector(geom)) │ │ │ │ } │ │ │ │ - this.gratLayer.addFeatures(lines) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Graticule" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.ArgParser = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - center: null, │ │ │ │ - zoom: null, │ │ │ │ - layers: null, │ │ │ │ - displayProjection: null, │ │ │ │ - getParameters: function(url) { │ │ │ │ - url = url || window.location.href; │ │ │ │ - var parameters = OpenLayers.Util.getParameters(url); │ │ │ │ - var index = url.indexOf("#"); │ │ │ │ - if (index > 0) { │ │ │ │ - url = "?" + url.substring(index + 1, url.length); │ │ │ │ - OpenLayers.Util.extend(parameters, OpenLayers.Util.getParameters(url)) │ │ │ │ + if (!style["strokeColor"] && style["fillColor"]) { │ │ │ │ + style["strokeColor"] = style["fillColor"] │ │ │ │ } │ │ │ │ - return parameters │ │ │ │ + var id = node.getAttribute("id"); │ │ │ │ + if (id && style) { │ │ │ │ + style.id = id │ │ │ │ + } │ │ │ │ + return style │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - for (var i = 0, len = this.map.controls.length; i < len; i++) { │ │ │ │ - var control = this.map.controls[i]; │ │ │ │ - if (control != this && control.CLASS_NAME == "OpenLayers.Control.ArgParser") { │ │ │ │ - if (control.displayProjection != this.displayProjection) { │ │ │ │ - this.displayProjection = control.displayProjection │ │ │ │ + parseStyleMaps: function(nodes, options) { │ │ │ │ + for (var i = 0, len = nodes.length; i < len; i++) { │ │ │ │ + var node = nodes[i]; │ │ │ │ + var pairs = this.getElementsByTagNameNS(node, "*", "Pair"); │ │ │ │ + var id = node.getAttribute("id"); │ │ │ │ + for (var j = 0, jlen = pairs.length; j < jlen; j++) { │ │ │ │ + var pair = pairs[j]; │ │ │ │ + var key = this.parseProperty(pair, "*", "key"); │ │ │ │ + var styleUrl = this.parseProperty(pair, "*", "styleUrl"); │ │ │ │ + if (styleUrl && key == "normal") { │ │ │ │ + this.styles[(options.styleBaseUrl || "") + "#" + id] = this.styles[(options.styleBaseUrl || "") + styleUrl] │ │ │ │ } │ │ │ │ - break │ │ │ │ } │ │ │ │ } │ │ │ │ - if (i == this.map.controls.length) { │ │ │ │ - var args = this.getParameters(); │ │ │ │ - if (args.layers) { │ │ │ │ - this.layers = args.layers; │ │ │ │ - this.map.events.register("addlayer", this, this.configureLayers); │ │ │ │ - this.configureLayers() │ │ │ │ - } │ │ │ │ - if (args.lat && args.lon) { │ │ │ │ - this.center = new OpenLayers.LonLat(parseFloat(args.lon), parseFloat(args.lat)); │ │ │ │ - if (args.zoom) { │ │ │ │ - this.zoom = parseFloat(args.zoom) │ │ │ │ + }, │ │ │ │ + parseFeatures: function(nodes, options) { │ │ │ │ + var features = []; │ │ │ │ + for (var i = 0, len = nodes.length; i < len; i++) { │ │ │ │ + var featureNode = nodes[i]; │ │ │ │ + var feature = this.parseFeature.apply(this, [featureNode]); │ │ │ │ + if (feature) { │ │ │ │ + if (this.extractStyles && feature.attributes && feature.attributes.styleUrl) { │ │ │ │ + feature.style = this.getStyle(feature.attributes.styleUrl, options) │ │ │ │ } │ │ │ │ - this.map.events.register("changebaselayer", this, this.setCenter); │ │ │ │ - this.setCenter() │ │ │ │ + if (this.extractStyles) { │ │ │ │ + var inlineStyleNode = this.getElementsByTagNameNS(featureNode, "*", "Style")[0]; │ │ │ │ + if (inlineStyleNode) { │ │ │ │ + var inlineStyle = this.parseStyle(inlineStyleNode); │ │ │ │ + if (inlineStyle) { │ │ │ │ + feature.style = OpenLayers.Util.extend(feature.style, inlineStyle) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (this.extractTracks) { │ │ │ │ + var tracks = this.getElementsByTagNameNS(featureNode, this.namespaces.gx, "Track"); │ │ │ │ + if (tracks && tracks.length > 0) { │ │ │ │ + var track = tracks[0]; │ │ │ │ + var container = { │ │ │ │ + features: [], │ │ │ │ + feature: feature │ │ │ │ + }; │ │ │ │ + this.readNode(track, container); │ │ │ │ + if (container.features.length > 0) { │ │ │ │ + features.push.apply(features, container.features) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + features.push(feature) │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + throw "Bad Placemark: " + i │ │ │ │ } │ │ │ │ } │ │ │ │ + this.features = this.features.concat(features) │ │ │ │ }, │ │ │ │ - setCenter: function() { │ │ │ │ - if (this.map.baseLayer) { │ │ │ │ - this.map.events.unregister("changebaselayer", this, this.setCenter); │ │ │ │ - if (this.displayProjection) { │ │ │ │ - this.center.transform(this.displayProjection, this.map.getProjectionObject()) │ │ │ │ + readers: { │ │ │ │ + kml: { │ │ │ │ + when: function(node, container) { │ │ │ │ + container.whens.push(OpenLayers.Date.parse(this.getChildValue(node))) │ │ │ │ + }, │ │ │ │ + _trackPointAttribute: function(node, container) { │ │ │ │ + var name = node.nodeName.split(":").pop(); │ │ │ │ + container.attributes[name].push(this.getChildValue(node)) │ │ │ │ } │ │ │ │ - this.map.setCenter(this.center, this.zoom) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - configureLayers: function() { │ │ │ │ - if (this.layers.length == this.map.layers.length) { │ │ │ │ - this.map.events.unregister("addlayer", this, this.configureLayers); │ │ │ │ - for (var i = 0, len = this.layers.length; i < len; i++) { │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - var c = this.layers.charAt(i); │ │ │ │ - if (c == "B") { │ │ │ │ - this.map.setBaseLayer(layer) │ │ │ │ - } else if (c == "T" || c == "F") { │ │ │ │ - layer.setVisibility(c == "T") │ │ │ │ + }, │ │ │ │ + gx: { │ │ │ │ + Track: function(node, container) { │ │ │ │ + var obj = { │ │ │ │ + whens: [], │ │ │ │ + points: [], │ │ │ │ + angles: [] │ │ │ │ + }; │ │ │ │ + if (this.trackAttributes) { │ │ │ │ + var name; │ │ │ │ + obj.attributes = {}; │ │ │ │ + for (var i = 0, ii = this.trackAttributes.length; i < ii; ++i) { │ │ │ │ + name = this.trackAttributes[i]; │ │ │ │ + obj.attributes[name] = []; │ │ │ │ + if (!(name in this.readers.kml)) { │ │ │ │ + this.readers.kml[name] = this.readers.kml._trackPointAttribute │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + this.readChildNodes(node, obj); │ │ │ │ + if (obj.whens.length !== obj.points.length) { │ │ │ │ + throw new Error("gx:Track with unequal number of when (" + obj.whens.length + ") and gx:coord (" + obj.points.length + ") elements.") │ │ │ │ + } │ │ │ │ + var hasAngles = obj.angles.length > 0; │ │ │ │ + if (hasAngles && obj.whens.length !== obj.angles.length) { │ │ │ │ + throw new Error("gx:Track with unequal number of when (" + obj.whens.length + ") and gx:angles (" + obj.angles.length + ") elements.") │ │ │ │ + } │ │ │ │ + var feature, point, angles; │ │ │ │ + for (var i = 0, ii = obj.whens.length; i < ii; ++i) { │ │ │ │ + feature = container.feature.clone(); │ │ │ │ + feature.fid = container.feature.fid || container.feature.id; │ │ │ │ + point = obj.points[i]; │ │ │ │ + feature.geometry = point; │ │ │ │ + if ("z" in point) { │ │ │ │ + feature.attributes.altitude = point.z │ │ │ │ + } │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + feature.geometry.transform(this.externalProjection, this.internalProjection) │ │ │ │ + } │ │ │ │ + if (this.trackAttributes) { │ │ │ │ + for (var j = 0, jj = this.trackAttributes.length; j < jj; ++j) { │ │ │ │ + var name = this.trackAttributes[j]; │ │ │ │ + feature.attributes[name] = obj.attributes[name][i] │ │ │ │ + } │ │ │ │ + } │ │ │ │ + feature.attributes.when = obj.whens[i]; │ │ │ │ + feature.attributes.trackId = container.feature.id; │ │ │ │ + if (hasAngles) { │ │ │ │ + angles = obj.angles[i]; │ │ │ │ + feature.attributes.heading = parseFloat(angles[0]); │ │ │ │ + feature.attributes.tilt = parseFloat(angles[1]); │ │ │ │ + feature.attributes.roll = parseFloat(angles[2]) │ │ │ │ + } │ │ │ │ + container.features.push(feature) │ │ │ │ + } │ │ │ │ + }, │ │ │ │ + coord: function(node, container) { │ │ │ │ + var str = this.getChildValue(node); │ │ │ │ + var coords = str.replace(this.regExes.trimSpace, "").split(/\s+/); │ │ │ │ + var point = new OpenLayers.Geometry.Point(coords[0], coords[1]); │ │ │ │ + if (coords.length > 2) { │ │ │ │ + point.z = parseFloat(coords[2]) │ │ │ │ } │ │ │ │ + container.points.push(point) │ │ │ │ + }, │ │ │ │ + angles: function(node, container) { │ │ │ │ + var str = this.getChildValue(node); │ │ │ │ + var parts = str.replace(this.regExes.trimSpace, "").split(/\s+/); │ │ │ │ + container.angles.push(parts) │ │ │ │ } │ │ │ │ } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ArgParser" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Permalink = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - argParserClass: OpenLayers.Control.ArgParser, │ │ │ │ - element: null, │ │ │ │ - anchor: false, │ │ │ │ - base: "", │ │ │ │ - displayProjection: null, │ │ │ │ - initialize: function(element, base, options) { │ │ │ │ - if (element !== null && typeof element == "object" && !OpenLayers.Util.isElement(element)) { │ │ │ │ - options = element; │ │ │ │ - this.base = document.location.href; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - if (this.element != null) { │ │ │ │ - this.element = OpenLayers.Util.getElement(this.element) │ │ │ │ + parseFeature: function(node) { │ │ │ │ + var order = ["MultiGeometry", "Polygon", "LineString", "Point"]; │ │ │ │ + var type, nodeList, geometry, parser; │ │ │ │ + for (var i = 0, len = order.length; i < len; ++i) { │ │ │ │ + type = order[i]; │ │ │ │ + this.internalns = node.namespaceURI ? node.namespaceURI : this.kmlns; │ │ │ │ + nodeList = this.getElementsByTagNameNS(node, this.internalns, type); │ │ │ │ + if (nodeList.length > 0) { │ │ │ │ + var parser = this.parseGeometry[type.toLowerCase()]; │ │ │ │ + if (parser) { │ │ │ │ + geometry = parser.apply(this, [nodeList[0]]); │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry.transform(this.externalProjection, this.internalProjection) │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + throw new TypeError("Unsupported geometry type: " + type) │ │ │ │ + } │ │ │ │ + break │ │ │ │ } │ │ │ │ - } else { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.element = OpenLayers.Util.getElement(element); │ │ │ │ - this.base = base || document.location.href │ │ │ │ } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.element && this.element.parentNode == this.div) { │ │ │ │ - this.div.removeChild(this.element); │ │ │ │ - this.element = null │ │ │ │ + var attributes; │ │ │ │ + if (this.extractAttributes) { │ │ │ │ + attributes = this.parseAttributes(node) │ │ │ │ } │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.unregister("moveend", this, this.updateLink) │ │ │ │ + var feature = new OpenLayers.Feature.Vector(geometry, attributes); │ │ │ │ + var fid = node.getAttribute("id") || node.getAttribute("name"); │ │ │ │ + if (fid != null) { │ │ │ │ + feature.fid = fid │ │ │ │ } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ + return feature │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - for (var i = 0, len = this.map.controls.length; i < len; i++) { │ │ │ │ - var control = this.map.controls[i]; │ │ │ │ - if (control.CLASS_NAME == this.argParserClass.CLASS_NAME) { │ │ │ │ - if (control.displayProjection != this.displayProjection) { │ │ │ │ - this.displayProjection = control.displayProjection │ │ │ │ - } │ │ │ │ - break │ │ │ │ + getStyle: function(styleUrl, options) { │ │ │ │ + var styleBaseUrl = OpenLayers.Util.removeTail(styleUrl); │ │ │ │ + var newOptions = OpenLayers.Util.extend({}, options); │ │ │ │ + newOptions.depth++; │ │ │ │ + newOptions.styleBaseUrl = styleBaseUrl; │ │ │ │ + if (!this.styles[styleUrl] && !OpenLayers.String.startsWith(styleUrl, "#") && newOptions.depth <= this.maxDepth && !this.fetched[styleBaseUrl]) { │ │ │ │ + var data = this.fetchLink(styleBaseUrl); │ │ │ │ + if (data) { │ │ │ │ + this.parseData(data, newOptions) │ │ │ │ } │ │ │ │ } │ │ │ │ - if (i == this.map.controls.length) { │ │ │ │ - this.map.addControl(new this.argParserClass({ │ │ │ │ - displayProjection: this.displayProjection │ │ │ │ - })) │ │ │ │ - } │ │ │ │ + var style = OpenLayers.Util.extend({}, this.styles[styleUrl]); │ │ │ │ + return style │ │ │ │ }, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (!this.element && !this.anchor) { │ │ │ │ - this.element = document.createElement("a"); │ │ │ │ - this.element.innerHTML = OpenLayers.i18n("Permalink"); │ │ │ │ - this.element.href = ""; │ │ │ │ - this.div.appendChild(this.element) │ │ │ │ + parseGeometry: { │ │ │ │ + point: function(node) { │ │ │ │ + var nodeList = this.getElementsByTagNameNS(node, this.internalns, "coordinates"); │ │ │ │ + var coords = []; │ │ │ │ + if (nodeList.length > 0) { │ │ │ │ + var coordString = nodeList[0].firstChild.nodeValue; │ │ │ │ + coordString = coordString.replace(this.regExes.removeSpace, ""); │ │ │ │ + coords = coordString.split(",") │ │ │ │ + } │ │ │ │ + var point = null; │ │ │ │ + if (coords.length > 1) { │ │ │ │ + if (coords.length == 2) { │ │ │ │ + coords[2] = null │ │ │ │ + } │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]) │ │ │ │ + } else { │ │ │ │ + throw "Bad coordinate string: " + coordString │ │ │ │ + } │ │ │ │ + return point │ │ │ │ + }, │ │ │ │ + linestring: function(node, ring) { │ │ │ │ + var nodeList = this.getElementsByTagNameNS(node, this.internalns, "coordinates"); │ │ │ │ + var line = null; │ │ │ │ + if (nodeList.length > 0) { │ │ │ │ + var coordString = this.getChildValue(nodeList[0]); │ │ │ │ + coordString = coordString.replace(this.regExes.trimSpace, ""); │ │ │ │ + coordString = coordString.replace(this.regExes.trimComma, ","); │ │ │ │ + var pointList = coordString.split(this.regExes.splitSpace); │ │ │ │ + var numPoints = pointList.length; │ │ │ │ + var points = new Array(numPoints); │ │ │ │ + var coords, numCoords; │ │ │ │ + for (var i = 0; i < numPoints; ++i) { │ │ │ │ + coords = pointList[i].split(","); │ │ │ │ + numCoords = coords.length; │ │ │ │ + if (numCoords > 1) { │ │ │ │ + if (coords.length == 2) { │ │ │ │ + coords[2] = null │ │ │ │ + } │ │ │ │ + points[i] = new OpenLayers.Geometry.Point(coords[0], coords[1], coords[2]) │ │ │ │ + } else { │ │ │ │ + throw "Bad LineString point coordinates: " + pointList[i] │ │ │ │ + } │ │ │ │ + } │ │ │ │ + if (numPoints) { │ │ │ │ + if (ring) { │ │ │ │ + line = new OpenLayers.Geometry.LinearRing(points) │ │ │ │ + } else { │ │ │ │ + line = new OpenLayers.Geometry.LineString(points) │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + throw "Bad LineString coordinates: " + coordString │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return line │ │ │ │ + }, │ │ │ │ + polygon: function(node) { │ │ │ │ + var nodeList = this.getElementsByTagNameNS(node, this.internalns, "LinearRing"); │ │ │ │ + var numRings = nodeList.length; │ │ │ │ + var components = new Array(numRings); │ │ │ │ + if (numRings > 0) { │ │ │ │ + var ring; │ │ │ │ + for (var i = 0, len = nodeList.length; i < len; ++i) { │ │ │ │ + ring = this.parseGeometry.linestring.apply(this, [nodeList[i], true]); │ │ │ │ + if (ring) { │ │ │ │ + components[i] = ring │ │ │ │ + } else { │ │ │ │ + throw "Bad LinearRing geometry: " + i │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.Polygon(components) │ │ │ │ + }, │ │ │ │ + multigeometry: function(node) { │ │ │ │ + var child, parser; │ │ │ │ + var parts = []; │ │ │ │ + var children = node.childNodes; │ │ │ │ + for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ + child = children[i]; │ │ │ │ + if (child.nodeType == 1) { │ │ │ │ + var type = child.prefix ? child.nodeName.split(":")[1] : child.nodeName; │ │ │ │ + var parser = this.parseGeometry[type.toLowerCase()]; │ │ │ │ + if (parser) { │ │ │ │ + parts.push(parser.apply(this, [child])) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.Collection(parts) │ │ │ │ } │ │ │ │ - this.map.events.on({ │ │ │ │ - moveend: this.updateLink, │ │ │ │ - changelayer: this.updateLink, │ │ │ │ - changebaselayer: this.updateLink, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.updateLink(); │ │ │ │ - return this.div │ │ │ │ }, │ │ │ │ - updateLink: function() { │ │ │ │ - var separator = this.anchor ? "#" : "?"; │ │ │ │ - var href = this.base; │ │ │ │ - var anchor = null; │ │ │ │ - if (href.indexOf("#") != -1 && this.anchor == false) { │ │ │ │ - anchor = href.substring(href.indexOf("#"), href.length) │ │ │ │ - } │ │ │ │ - if (href.indexOf(separator) != -1) { │ │ │ │ - href = href.substring(0, href.indexOf(separator)) │ │ │ │ - } │ │ │ │ - var splits = href.split("#"); │ │ │ │ - href = splits[0] + separator + OpenLayers.Util.getParameterString(this.createParams()); │ │ │ │ - if (anchor) { │ │ │ │ - href += anchor │ │ │ │ + parseAttributes: function(node) { │ │ │ │ + var attributes = {}; │ │ │ │ + var edNodes = node.getElementsByTagName("ExtendedData"); │ │ │ │ + if (edNodes.length) { │ │ │ │ + attributes = this.parseExtendedData(edNodes[0]) │ │ │ │ } │ │ │ │ - if (this.anchor && !this.element) { │ │ │ │ - window.location.href = href │ │ │ │ - } else { │ │ │ │ - this.element.href = href │ │ │ │ + var child, grandchildren, grandchild; │ │ │ │ + var children = node.childNodes; │ │ │ │ + for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ + child = children[i]; │ │ │ │ + if (child.nodeType == 1) { │ │ │ │ + grandchildren = child.childNodes; │ │ │ │ + if (grandchildren.length >= 1 && grandchildren.length <= 3) { │ │ │ │ + var grandchild; │ │ │ │ + switch (grandchildren.length) { │ │ │ │ + case 1: │ │ │ │ + grandchild = grandchildren[0]; │ │ │ │ + break; │ │ │ │ + case 2: │ │ │ │ + var c1 = grandchildren[0]; │ │ │ │ + var c2 = grandchildren[1]; │ │ │ │ + grandchild = c1.nodeType == 3 || c1.nodeType == 4 ? c1 : c2; │ │ │ │ + break; │ │ │ │ + case 3: │ │ │ │ + default: │ │ │ │ + grandchild = grandchildren[1]; │ │ │ │ + break │ │ │ │ + } │ │ │ │ + if (grandchild.nodeType == 3 || grandchild.nodeType == 4) { │ │ │ │ + var name = child.prefix ? child.nodeName.split(":")[1] : child.nodeName; │ │ │ │ + var value = OpenLayers.Util.getXmlNodeValue(grandchild); │ │ │ │ + if (value) { │ │ │ │ + value = value.replace(this.regExes.trimSpace, ""); │ │ │ │ + attributes[name] = value │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + return attributes │ │ │ │ }, │ │ │ │ - createParams: function(center, zoom, layers) { │ │ │ │ - center = center || this.map.getCenter(); │ │ │ │ - var params = OpenLayers.Util.getParameters(this.base); │ │ │ │ - if (center) { │ │ │ │ - params.zoom = zoom || this.map.getZoom(); │ │ │ │ - var lat = center.lat; │ │ │ │ - var lon = center.lon; │ │ │ │ - if (this.displayProjection) { │ │ │ │ - var mapPosition = OpenLayers.Projection.transform({ │ │ │ │ - x: lon, │ │ │ │ - y: lat │ │ │ │ - }, this.map.getProjectionObject(), this.displayProjection); │ │ │ │ - lon = mapPosition.x; │ │ │ │ - lat = mapPosition.y │ │ │ │ + parseExtendedData: function(node) { │ │ │ │ + var attributes = {}; │ │ │ │ + var i, len, data, key; │ │ │ │ + var dataNodes = node.getElementsByTagName("Data"); │ │ │ │ + for (i = 0, len = dataNodes.length; i < len; i++) { │ │ │ │ + data = dataNodes[i]; │ │ │ │ + key = data.getAttribute("name"); │ │ │ │ + var ed = {}; │ │ │ │ + var valueNode = data.getElementsByTagName("value"); │ │ │ │ + if (valueNode.length) { │ │ │ │ + ed["value"] = this.getChildValue(valueNode[0]) │ │ │ │ } │ │ │ │ - params.lat = Math.round(lat * 1e5) / 1e5; │ │ │ │ - params.lon = Math.round(lon * 1e5) / 1e5; │ │ │ │ - layers = layers || this.map.layers; │ │ │ │ - params.layers = ""; │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - var layer = layers[i]; │ │ │ │ - if (layer.isBaseLayer) { │ │ │ │ - params.layers += layer == this.map.baseLayer ? "B" : "0" │ │ │ │ - } else { │ │ │ │ - params.layers += layer.getVisibility() ? "T" : "F" │ │ │ │ + if (this.kvpAttributes) { │ │ │ │ + attributes[key] = ed["value"] │ │ │ │ + } else { │ │ │ │ + var nameNode = data.getElementsByTagName("displayName"); │ │ │ │ + if (nameNode.length) { │ │ │ │ + ed["displayName"] = this.getChildValue(nameNode[0]) │ │ │ │ } │ │ │ │ + attributes[key] = ed │ │ │ │ } │ │ │ │ } │ │ │ │ - return params │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Permalink" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - callbacks: null, │ │ │ │ - displaySystem: "metric", │ │ │ │ - geodesic: false, │ │ │ │ - displaySystemUnits: { │ │ │ │ - geographic: ["dd"], │ │ │ │ - english: ["mi", "ft", "in"], │ │ │ │ - metric: ["km", "m"] │ │ │ │ - }, │ │ │ │ - partialDelay: 300, │ │ │ │ - delayedTrigger: null, │ │ │ │ - persist: false, │ │ │ │ - immediate: false, │ │ │ │ - initialize: function(handler, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - var callbacks = { │ │ │ │ - done: this.measureComplete, │ │ │ │ - point: this.measurePartial │ │ │ │ - }; │ │ │ │ - if (this.immediate) { │ │ │ │ - callbacks.modify = this.measureImmediate │ │ │ │ + var simpleDataNodes = node.getElementsByTagName("SimpleData"); │ │ │ │ + for (i = 0, len = simpleDataNodes.length; i < len; i++) { │ │ │ │ + var ed = {}; │ │ │ │ + data = simpleDataNodes[i]; │ │ │ │ + key = data.getAttribute("name"); │ │ │ │ + ed["value"] = this.getChildValue(data); │ │ │ │ + if (this.kvpAttributes) { │ │ │ │ + attributes[key] = ed["value"] │ │ │ │ + } else { │ │ │ │ + ed["displayName"] = key; │ │ │ │ + attributes[key] = ed │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); │ │ │ │ - this.handlerOptions = OpenLayers.Util.extend({ │ │ │ │ - persist: this.persist │ │ │ │ - }, this.handlerOptions); │ │ │ │ - this.handler = new handler(this, this.callbacks, this.handlerOptions) │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - this.cancelDelay(); │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - cancel: function() { │ │ │ │ - this.cancelDelay(); │ │ │ │ - this.handler.cancel() │ │ │ │ + return attributes │ │ │ │ }, │ │ │ │ - setImmediate: function(immediate) { │ │ │ │ - this.immediate = immediate; │ │ │ │ - if (this.immediate) { │ │ │ │ - this.callbacks.modify = this.measureImmediate │ │ │ │ - } else { │ │ │ │ - delete this.callbacks.modify │ │ │ │ + parseProperty: function(xmlNode, namespace, tagName) { │ │ │ │ + var value; │ │ │ │ + var nodeList = this.getElementsByTagNameNS(xmlNode, namespace, tagName); │ │ │ │ + try { │ │ │ │ + value = OpenLayers.Util.getXmlNodeValue(nodeList[0]) │ │ │ │ + } catch (e) { │ │ │ │ + value = null │ │ │ │ } │ │ │ │ + return value │ │ │ │ }, │ │ │ │ - updateHandler: function(handler, options) { │ │ │ │ - var active = this.active; │ │ │ │ - if (active) { │ │ │ │ - this.deactivate() │ │ │ │ + write: function(features) { │ │ │ │ + if (!OpenLayers.Util.isArray(features)) { │ │ │ │ + features = [features] │ │ │ │ } │ │ │ │ - this.handler = new handler(this, this.callbacks, options); │ │ │ │ - if (active) { │ │ │ │ - this.activate() │ │ │ │ + var kml = this.createElementNS(this.kmlns, "kml"); │ │ │ │ + var folder = this.createFolderXML(); │ │ │ │ + for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ + folder.appendChild(this.createPlacemarkXML(features[i])) │ │ │ │ } │ │ │ │ + kml.appendChild(folder); │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [kml]) │ │ │ │ }, │ │ │ │ - measureComplete: function(geometry) { │ │ │ │ - this.cancelDelay(); │ │ │ │ - this.measure(geometry, "measure") │ │ │ │ - }, │ │ │ │ - measurePartial: function(point, geometry) { │ │ │ │ - this.cancelDelay(); │ │ │ │ - geometry = geometry.clone(); │ │ │ │ - if (this.handler.freehandMode(this.handler.evt)) { │ │ │ │ - this.measure(geometry, "measurepartial") │ │ │ │ - } else { │ │ │ │ - this.delayedTrigger = window.setTimeout(OpenLayers.Function.bind(function() { │ │ │ │ - this.delayedTrigger = null; │ │ │ │ - this.measure(geometry, "measurepartial") │ │ │ │ - }, this), this.partialDelay) │ │ │ │ + createFolderXML: function() { │ │ │ │ + var folder = this.createElementNS(this.kmlns, "Folder"); │ │ │ │ + if (this.foldersName) { │ │ │ │ + var folderName = this.createElementNS(this.kmlns, "name"); │ │ │ │ + var folderNameText = this.createTextNode(this.foldersName); │ │ │ │ + folderName.appendChild(folderNameText); │ │ │ │ + folder.appendChild(folderName) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - measureImmediate: function(point, feature, drawing) { │ │ │ │ - if (drawing && !this.handler.freehandMode(this.handler.evt)) { │ │ │ │ - this.cancelDelay(); │ │ │ │ - this.measure(feature.geometry, "measurepartial") │ │ │ │ + if (this.foldersDesc) { │ │ │ │ + var folderDesc = this.createElementNS(this.kmlns, "description"); │ │ │ │ + var folderDescText = this.createTextNode(this.foldersDesc); │ │ │ │ + folderDesc.appendChild(folderDescText); │ │ │ │ + folder.appendChild(folderDesc) │ │ │ │ } │ │ │ │ + return folder │ │ │ │ }, │ │ │ │ - cancelDelay: function() { │ │ │ │ - if (this.delayedTrigger !== null) { │ │ │ │ - window.clearTimeout(this.delayedTrigger); │ │ │ │ - this.delayedTrigger = null │ │ │ │ + createPlacemarkXML: function(feature) { │ │ │ │ + var placemarkName = this.createElementNS(this.kmlns, "name"); │ │ │ │ + var label = feature.style && feature.style.label ? feature.style.label : feature.id; │ │ │ │ + var name = feature.attributes.name || label; │ │ │ │ + placemarkName.appendChild(this.createTextNode(name)); │ │ │ │ + var placemarkDesc = this.createElementNS(this.kmlns, "description"); │ │ │ │ + var desc = feature.attributes.description || this.placemarksDesc; │ │ │ │ + placemarkDesc.appendChild(this.createTextNode(desc)); │ │ │ │ + var placemarkNode = this.createElementNS(this.kmlns, "Placemark"); │ │ │ │ + if (feature.fid != null) { │ │ │ │ + placemarkNode.setAttribute("id", feature.fid) │ │ │ │ + } │ │ │ │ + placemarkNode.appendChild(placemarkName); │ │ │ │ + placemarkNode.appendChild(placemarkDesc); │ │ │ │ + var geometryNode = this.buildGeometryNode(feature.geometry); │ │ │ │ + placemarkNode.appendChild(geometryNode); │ │ │ │ + if (feature.attributes) { │ │ │ │ + var edNode = this.buildExtendedData(feature.attributes); │ │ │ │ + if (edNode) { │ │ │ │ + placemarkNode.appendChild(edNode) │ │ │ │ + } │ │ │ │ } │ │ │ │ + return placemarkNode │ │ │ │ }, │ │ │ │ - measure: function(geometry, eventType) { │ │ │ │ - var stat, order; │ │ │ │ - if (geometry.CLASS_NAME.indexOf("LineString") > -1) { │ │ │ │ - stat = this.getBestLength(geometry); │ │ │ │ - order = 1 │ │ │ │ - } else { │ │ │ │ - stat = this.getBestArea(geometry); │ │ │ │ - order = 2 │ │ │ │ + buildGeometryNode: function(geometry) { │ │ │ │ + var className = geometry.CLASS_NAME; │ │ │ │ + var type = className.substring(className.lastIndexOf(".") + 1); │ │ │ │ + var builder = this.buildGeometry[type.toLowerCase()]; │ │ │ │ + var node = null; │ │ │ │ + if (builder) { │ │ │ │ + node = builder.apply(this, [geometry]) │ │ │ │ } │ │ │ │ - this.events.triggerEvent(eventType, { │ │ │ │ - measure: stat[0], │ │ │ │ - units: stat[1], │ │ │ │ - order: order, │ │ │ │ - geometry: geometry │ │ │ │ - }) │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - getBestArea: function(geometry) { │ │ │ │ - var units = this.displaySystemUnits[this.displaySystem]; │ │ │ │ - var unit, area; │ │ │ │ - for (var i = 0, len = units.length; i < len; ++i) { │ │ │ │ - unit = units[i]; │ │ │ │ - area = this.getArea(geometry, unit); │ │ │ │ - if (area > 1) { │ │ │ │ - break │ │ │ │ + buildGeometry: { │ │ │ │ + point: function(geometry) { │ │ │ │ + var kml = this.createElementNS(this.kmlns, "Point"); │ │ │ │ + kml.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ + return kml │ │ │ │ + }, │ │ │ │ + multipoint: function(geometry) { │ │ │ │ + return this.buildGeometry.collection.apply(this, [geometry]) │ │ │ │ + }, │ │ │ │ + linestring: function(geometry) { │ │ │ │ + var kml = this.createElementNS(this.kmlns, "LineString"); │ │ │ │ + kml.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ + return kml │ │ │ │ + }, │ │ │ │ + multilinestring: function(geometry) { │ │ │ │ + return this.buildGeometry.collection.apply(this, [geometry]) │ │ │ │ + }, │ │ │ │ + linearring: function(geometry) { │ │ │ │ + var kml = this.createElementNS(this.kmlns, "LinearRing"); │ │ │ │ + kml.appendChild(this.buildCoordinatesNode(geometry)); │ │ │ │ + return kml │ │ │ │ + }, │ │ │ │ + polygon: function(geometry) { │ │ │ │ + var kml = this.createElementNS(this.kmlns, "Polygon"); │ │ │ │ + var rings = geometry.components; │ │ │ │ + var ringMember, ringGeom, type; │ │ │ │ + for (var i = 0, len = rings.length; i < len; ++i) { │ │ │ │ + type = i == 0 ? "outerBoundaryIs" : "innerBoundaryIs"; │ │ │ │ + ringMember = this.createElementNS(this.kmlns, type); │ │ │ │ + ringGeom = this.buildGeometry.linearring.apply(this, [rings[i]]); │ │ │ │ + ringMember.appendChild(ringGeom); │ │ │ │ + kml.appendChild(ringMember) │ │ │ │ + } │ │ │ │ + return kml │ │ │ │ + }, │ │ │ │ + multipolygon: function(geometry) { │ │ │ │ + return this.buildGeometry.collection.apply(this, [geometry]) │ │ │ │ + }, │ │ │ │ + collection: function(geometry) { │ │ │ │ + var kml = this.createElementNS(this.kmlns, "MultiGeometry"); │ │ │ │ + var child; │ │ │ │ + for (var i = 0, len = geometry.components.length; i < len; ++i) { │ │ │ │ + child = this.buildGeometryNode.apply(this, [geometry.components[i]]); │ │ │ │ + if (child) { │ │ │ │ + kml.appendChild(child) │ │ │ │ + } │ │ │ │ } │ │ │ │ + return kml │ │ │ │ } │ │ │ │ - return [area, unit] │ │ │ │ }, │ │ │ │ - getArea: function(geometry, units) { │ │ │ │ - var area, geomUnits; │ │ │ │ - if (this.geodesic) { │ │ │ │ - area = geometry.getGeodesicArea(this.map.getProjectionObject()); │ │ │ │ - geomUnits = "m" │ │ │ │ + buildCoordinatesNode: function(geometry) { │ │ │ │ + var coordinatesNode = this.createElementNS(this.kmlns, "coordinates"); │ │ │ │ + var path; │ │ │ │ + var points = geometry.components; │ │ │ │ + if (points) { │ │ │ │ + var point; │ │ │ │ + var numPoints = points.length; │ │ │ │ + var parts = new Array(numPoints); │ │ │ │ + for (var i = 0; i < numPoints; ++i) { │ │ │ │ + point = points[i]; │ │ │ │ + parts[i] = this.buildCoordinates(point) │ │ │ │ + } │ │ │ │ + path = parts.join(" ") │ │ │ │ } else { │ │ │ │ - area = geometry.getArea(); │ │ │ │ - geomUnits = this.map.getUnits() │ │ │ │ + path = this.buildCoordinates(geometry) │ │ │ │ } │ │ │ │ - var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units]; │ │ │ │ - if (inPerDisplayUnit) { │ │ │ │ - var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits]; │ │ │ │ - area *= Math.pow(inPerMapUnit / inPerDisplayUnit, 2) │ │ │ │ + var txtNode = this.createTextNode(path); │ │ │ │ + coordinatesNode.appendChild(txtNode); │ │ │ │ + return coordinatesNode │ │ │ │ + }, │ │ │ │ + buildCoordinates: function(point) { │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + point = point.clone(); │ │ │ │ + point.transform(this.internalProjection, this.externalProjection) │ │ │ │ } │ │ │ │ - return area │ │ │ │ + return point.x + "," + point.y │ │ │ │ }, │ │ │ │ - getBestLength: function(geometry) { │ │ │ │ - var units = this.displaySystemUnits[this.displaySystem]; │ │ │ │ - var unit, length; │ │ │ │ - for (var i = 0, len = units.length; i < len; ++i) { │ │ │ │ - unit = units[i]; │ │ │ │ - length = this.getLength(geometry, unit); │ │ │ │ - if (length > 1) { │ │ │ │ - break │ │ │ │ + buildExtendedData: function(attributes) { │ │ │ │ + var extendedData = this.createElementNS(this.kmlns, "ExtendedData"); │ │ │ │ + for (var attributeName in attributes) { │ │ │ │ + if (attributes[attributeName] && attributeName != "name" && attributeName != "description" && attributeName != "styleUrl") { │ │ │ │ + var data = this.createElementNS(this.kmlns, "Data"); │ │ │ │ + data.setAttribute("name", attributeName); │ │ │ │ + var value = this.createElementNS(this.kmlns, "value"); │ │ │ │ + if (typeof attributes[attributeName] == "object") { │ │ │ │ + if (attributes[attributeName].value) { │ │ │ │ + value.appendChild(this.createTextNode(attributes[attributeName].value)) │ │ │ │ + } │ │ │ │ + if (attributes[attributeName].displayName) { │ │ │ │ + var displayName = this.createElementNS(this.kmlns, "displayName"); │ │ │ │ + displayName.appendChild(this.getXMLDoc().createCDATASection(attributes[attributeName].displayName)); │ │ │ │ + data.appendChild(displayName) │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + value.appendChild(this.createTextNode(attributes[attributeName])) │ │ │ │ + } │ │ │ │ + data.appendChild(value); │ │ │ │ + extendedData.appendChild(data) │ │ │ │ } │ │ │ │ } │ │ │ │ - return [length, unit] │ │ │ │ - }, │ │ │ │ - getLength: function(geometry, units) { │ │ │ │ - var length, geomUnits; │ │ │ │ - if (this.geodesic) { │ │ │ │ - length = geometry.getGeodesicLength(this.map.getProjectionObject()); │ │ │ │ - geomUnits = "m" │ │ │ │ + if (this.isSimpleContent(extendedData)) { │ │ │ │ + return null │ │ │ │ } else { │ │ │ │ - length = geometry.getLength(); │ │ │ │ - geomUnits = this.map.getUnits() │ │ │ │ - } │ │ │ │ - var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units]; │ │ │ │ - if (inPerDisplayUnit) { │ │ │ │ - var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits]; │ │ │ │ - length *= inPerMapUnit / inPerDisplayUnit │ │ │ │ + return extendedData │ │ │ │ } │ │ │ │ - return length │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Measure" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.KML" │ │ │ │ }); │ │ │ │ -OpenLayers.Handler.Box = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - dragHandler: null, │ │ │ │ - boxDivClassName: "olHandlerBoxZoomBox", │ │ │ │ - boxOffsets: null, │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - this.dragHandler = new OpenLayers.Handler.Drag(this, { │ │ │ │ - down: this.startBox, │ │ │ │ - move: this.moveBox, │ │ │ │ - out: this.removeBox, │ │ │ │ - up: this.endBox │ │ │ │ - }, { │ │ │ │ - keyMask: this.keyMask │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ - if (this.dragHandler) { │ │ │ │ - this.dragHandler.destroy(); │ │ │ │ - this.dragHandler = null │ │ │ │ +OpenLayers.Format.SOSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + defaultVersion: "1.0.0", │ │ │ │ + CLASS_NAME: "OpenLayers.Format.SOSCapabilities" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.SLD = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + profile: null, │ │ │ │ + defaultVersion: "1.0.0", │ │ │ │ + stringifyOutput: true, │ │ │ │ + namedLayersAsArray: false, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.SLD" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.OSM = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + checkTags: false, │ │ │ │ + interestingTagsExclude: null, │ │ │ │ + areaTags: null, │ │ │ │ + initialize: function(options) { │ │ │ │ + var layer_defaults = { │ │ │ │ + interestingTagsExclude: ["source", "source_ref", "source:ref", "history", "attribution", "created_by"], │ │ │ │ + areaTags: ["area", "building", "leisure", "tourism", "ruins", "historic", "landuse", "military", "natural", "sport"] │ │ │ │ + }; │ │ │ │ + layer_defaults = OpenLayers.Util.extend(layer_defaults, options); │ │ │ │ + var interesting = {}; │ │ │ │ + for (var i = 0; i < layer_defaults.interestingTagsExclude.length; i++) { │ │ │ │ + interesting[layer_defaults.interestingTagsExclude[i]] = true │ │ │ │ } │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Handler.prototype.setMap.apply(this, arguments); │ │ │ │ - if (this.dragHandler) { │ │ │ │ - this.dragHandler.setMap(map) │ │ │ │ + layer_defaults.interestingTagsExclude = interesting; │ │ │ │ + var area = {}; │ │ │ │ + for (var i = 0; i < layer_defaults.areaTags.length; i++) { │ │ │ │ + area[layer_defaults.areaTags[i]] = true │ │ │ │ } │ │ │ │ + layer_defaults.areaTags = area; │ │ │ │ + this.externalProjection = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [layer_defaults]) │ │ │ │ }, │ │ │ │ - startBox: function(xy) { │ │ │ │ - this.callback("start", []); │ │ │ │ - this.zoomBox = OpenLayers.Util.createDiv("zoomBox", { │ │ │ │ - x: -9999, │ │ │ │ - y: -9999 │ │ │ │ - }); │ │ │ │ - this.zoomBox.className = this.boxDivClassName; │ │ │ │ - this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1; │ │ │ │ - this.map.viewPortDiv.appendChild(this.zoomBox); │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, "olDrawBox") │ │ │ │ - }, │ │ │ │ - moveBox: function(xy) { │ │ │ │ - var startX = this.dragHandler.start.x; │ │ │ │ - var startY = this.dragHandler.start.y; │ │ │ │ - var deltaX = Math.abs(startX - xy.x); │ │ │ │ - var deltaY = Math.abs(startY - xy.y); │ │ │ │ - var offset = this.getBoxOffsets(); │ │ │ │ - this.zoomBox.style.width = deltaX + offset.width + 1 + "px"; │ │ │ │ - this.zoomBox.style.height = deltaY + offset.height + 1 + "px"; │ │ │ │ - this.zoomBox.style.left = (xy.x < startX ? startX - deltaX - offset.left : startX - offset.left) + "px"; │ │ │ │ - this.zoomBox.style.top = (xy.y < startY ? startY - deltaY - offset.top : startY - offset.top) + "px" │ │ │ │ + read: function(doc) { │ │ │ │ + if (typeof doc == "string") { │ │ │ │ + doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]) │ │ │ │ + } │ │ │ │ + var nodes = this.getNodes(doc); │ │ │ │ + var ways = this.getWays(doc); │ │ │ │ + var feat_list = new Array(ways.length); │ │ │ │ + for (var i = 0; i < ways.length; i++) { │ │ │ │ + var point_list = new Array(ways[i].nodes.length); │ │ │ │ + var poly = this.isWayArea(ways[i]) ? 1 : 0; │ │ │ │ + for (var j = 0; j < ways[i].nodes.length; j++) { │ │ │ │ + var node = nodes[ways[i].nodes[j]]; │ │ │ │ + var point = new OpenLayers.Geometry.Point(node.lon, node.lat); │ │ │ │ + point.osm_id = parseInt(ways[i].nodes[j]); │ │ │ │ + point_list[j] = point; │ │ │ │ + node.used = true │ │ │ │ + } │ │ │ │ + var geometry = null; │ │ │ │ + if (poly) { │ │ │ │ + geometry = new OpenLayers.Geometry.Polygon(new OpenLayers.Geometry.LinearRing(point_list)) │ │ │ │ + } else { │ │ │ │ + geometry = new OpenLayers.Geometry.LineString(point_list) │ │ │ │ + } │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry.transform(this.externalProjection, this.internalProjection) │ │ │ │ + } │ │ │ │ + var feat = new OpenLayers.Feature.Vector(geometry, ways[i].tags); │ │ │ │ + feat.osm_id = parseInt(ways[i].id); │ │ │ │ + feat.fid = "way." + feat.osm_id; │ │ │ │ + feat_list[i] = feat │ │ │ │ + } │ │ │ │ + for (var node_id in nodes) { │ │ │ │ + var node = nodes[node_id]; │ │ │ │ + if (!node.used || this.checkTags) { │ │ │ │ + var tags = null; │ │ │ │ + if (this.checkTags) { │ │ │ │ + var result = this.getTags(node.node, true); │ │ │ │ + if (node.used && !result[1]) { │ │ │ │ + continue │ │ │ │ + } │ │ │ │ + tags = result[0] │ │ │ │ + } else { │ │ │ │ + tags = this.getTags(node.node) │ │ │ │ + } │ │ │ │ + var feat = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(node["lon"], node["lat"]), tags); │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + feat.geometry.transform(this.externalProjection, this.internalProjection) │ │ │ │ + } │ │ │ │ + feat.osm_id = parseInt(node_id); │ │ │ │ + feat.fid = "node." + feat.osm_id; │ │ │ │ + feat_list.push(feat) │ │ │ │ + } │ │ │ │ + node.node = null │ │ │ │ + } │ │ │ │ + return feat_list │ │ │ │ }, │ │ │ │ - endBox: function(end) { │ │ │ │ - var result; │ │ │ │ - if (Math.abs(this.dragHandler.start.x - end.x) > 5 || Math.abs(this.dragHandler.start.y - end.y) > 5) { │ │ │ │ - var start = this.dragHandler.start; │ │ │ │ - var top = Math.min(start.y, end.y); │ │ │ │ - var bottom = Math.max(start.y, end.y); │ │ │ │ - var left = Math.min(start.x, end.x); │ │ │ │ - var right = Math.max(start.x, end.x); │ │ │ │ - result = new OpenLayers.Bounds(left, bottom, right, top) │ │ │ │ - } else { │ │ │ │ - result = this.dragHandler.start.clone() │ │ │ │ + getNodes: function(doc) { │ │ │ │ + var node_list = doc.getElementsByTagName("node"); │ │ │ │ + var nodes = {}; │ │ │ │ + for (var i = 0; i < node_list.length; i++) { │ │ │ │ + var node = node_list[i]; │ │ │ │ + var id = node.getAttribute("id"); │ │ │ │ + nodes[id] = { │ │ │ │ + lat: node.getAttribute("lat"), │ │ │ │ + lon: node.getAttribute("lon"), │ │ │ │ + node: node │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.removeBox(); │ │ │ │ - this.callback("done", [result]) │ │ │ │ + return nodes │ │ │ │ }, │ │ │ │ - removeBox: function() { │ │ │ │ - this.map.viewPortDiv.removeChild(this.zoomBox); │ │ │ │ - this.zoomBox = null; │ │ │ │ - this.boxOffsets = null; │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olDrawBox") │ │ │ │ + getWays: function(doc) { │ │ │ │ + var way_list = doc.getElementsByTagName("way"); │ │ │ │ + var return_ways = []; │ │ │ │ + for (var i = 0; i < way_list.length; i++) { │ │ │ │ + var way = way_list[i]; │ │ │ │ + var way_object = { │ │ │ │ + id: way.getAttribute("id") │ │ │ │ + }; │ │ │ │ + way_object.tags = this.getTags(way); │ │ │ │ + var node_list = way.getElementsByTagName("nd"); │ │ │ │ + way_object.nodes = new Array(node_list.length); │ │ │ │ + for (var j = 0; j < node_list.length; j++) { │ │ │ │ + way_object.nodes[j] = node_list[j].getAttribute("ref") │ │ │ │ + } │ │ │ │ + return_ways.push(way_object) │ │ │ │ + } │ │ │ │ + return return_ways │ │ │ │ }, │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.dragHandler.activate(); │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ + getTags: function(dom_node, interesting_tags) { │ │ │ │ + var tag_list = dom_node.getElementsByTagName("tag"); │ │ │ │ + var tags = {}; │ │ │ │ + var interesting = false; │ │ │ │ + for (var j = 0; j < tag_list.length; j++) { │ │ │ │ + var key = tag_list[j].getAttribute("k"); │ │ │ │ + tags[key] = tag_list[j].getAttribute("v"); │ │ │ │ + if (interesting_tags) { │ │ │ │ + if (!this.interestingTagsExclude[key]) { │ │ │ │ + interesting = true │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + return interesting_tags ? [tags, interesting] : tags │ │ │ │ }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - if (this.dragHandler.deactivate()) { │ │ │ │ - if (this.zoomBox) { │ │ │ │ - this.removeBox() │ │ │ │ + isWayArea: function(way) { │ │ │ │ + var poly_shaped = false; │ │ │ │ + var poly_tags = false; │ │ │ │ + if (way.nodes[0] == way.nodes[way.nodes.length - 1]) { │ │ │ │ + poly_shaped = true │ │ │ │ + } │ │ │ │ + if (this.checkTags) { │ │ │ │ + for (var key in way.tags) { │ │ │ │ + if (this.areaTags[key]) { │ │ │ │ + poly_tags = true; │ │ │ │ + break │ │ │ │ } │ │ │ │ } │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ } │ │ │ │ + return poly_shaped && (this.checkTags ? poly_tags : true) │ │ │ │ }, │ │ │ │ - getBoxOffsets: function() { │ │ │ │ - if (!this.boxOffsets) { │ │ │ │ - var testDiv = document.createElement("div"); │ │ │ │ - testDiv.style.position = "absolute"; │ │ │ │ - testDiv.style.border = "1px solid black"; │ │ │ │ - testDiv.style.width = "3px"; │ │ │ │ - document.body.appendChild(testDiv); │ │ │ │ - var w3cBoxModel = testDiv.clientWidth == 3; │ │ │ │ - document.body.removeChild(testDiv); │ │ │ │ - var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-left-width")); │ │ │ │ - var right = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-right-width")); │ │ │ │ - var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-top-width")); │ │ │ │ - var bottom = parseInt(OpenLayers.Element.getStyle(this.zoomBox, "border-bottom-width")); │ │ │ │ - this.boxOffsets = { │ │ │ │ - left: left, │ │ │ │ - right: right, │ │ │ │ - top: top, │ │ │ │ - bottom: bottom, │ │ │ │ - width: w3cBoxModel === false ? left + right : 0, │ │ │ │ - height: w3cBoxModel === false ? top + bottom : 0 │ │ │ │ + write: function(features) { │ │ │ │ + if (!OpenLayers.Util.isArray(features)) { │ │ │ │ + features = [features] │ │ │ │ + } │ │ │ │ + this.osm_id = 1; │ │ │ │ + this.created_nodes = {}; │ │ │ │ + var root_node = this.createElementNS(null, "osm"); │ │ │ │ + root_node.setAttribute("version", "0.5"); │ │ │ │ + root_node.setAttribute("generator", "OpenLayers " + OpenLayers.VERSION_NUMBER); │ │ │ │ + for (var i = features.length - 1; i >= 0; i--) { │ │ │ │ + var nodes = this.createFeatureNodes(features[i]); │ │ │ │ + for (var j = 0; j < nodes.length; j++) { │ │ │ │ + root_node.appendChild(nodes[j]) │ │ │ │ } │ │ │ │ } │ │ │ │ - return this.boxOffsets │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [root_node]) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Box" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.ZoomBox = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ - out: false, │ │ │ │ - keyMask: null, │ │ │ │ - alwaysZoom: false, │ │ │ │ - zoomOnClick: true, │ │ │ │ - draw: function() { │ │ │ │ - this.handler = new OpenLayers.Handler.Box(this, { │ │ │ │ - done: this.zoomBox │ │ │ │ - }, { │ │ │ │ - keyMask: this.keyMask │ │ │ │ - }) │ │ │ │ + createFeatureNodes: function(feature) { │ │ │ │ + var nodes = []; │ │ │ │ + var className = feature.geometry.CLASS_NAME; │ │ │ │ + var type = className.substring(className.lastIndexOf(".") + 1); │ │ │ │ + type = type.toLowerCase(); │ │ │ │ + var builder = this.createXML[type]; │ │ │ │ + if (builder) { │ │ │ │ + nodes = builder.apply(this, [feature]) │ │ │ │ + } │ │ │ │ + return nodes │ │ │ │ }, │ │ │ │ - zoomBox: function(position) { │ │ │ │ - if (position instanceof OpenLayers.Bounds) { │ │ │ │ - var bounds, targetCenterPx = position.getCenterPixel(); │ │ │ │ - if (!this.out) { │ │ │ │ - var minXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.left, │ │ │ │ - y: position.bottom │ │ │ │ - }); │ │ │ │ - var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.right, │ │ │ │ - y: position.top │ │ │ │ - }); │ │ │ │ - bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat) │ │ │ │ + createXML: { │ │ │ │ + point: function(point) { │ │ │ │ + var id = null; │ │ │ │ + var geometry = point.geometry ? point.geometry : point; │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry = geometry.clone(); │ │ │ │ + geometry.transform(this.internalProjection, this.externalProjection) │ │ │ │ + } │ │ │ │ + var already_exists = false; │ │ │ │ + if (point.osm_id) { │ │ │ │ + id = point.osm_id; │ │ │ │ + if (this.created_nodes[id]) { │ │ │ │ + already_exists = true │ │ │ │ + } │ │ │ │ } else { │ │ │ │ - var pixWidth = position.right - position.left; │ │ │ │ - var pixHeight = position.bottom - position.top; │ │ │ │ - var zoomFactor = Math.min(this.map.size.h / pixHeight, this.map.size.w / pixWidth); │ │ │ │ - var extent = this.map.getExtent(); │ │ │ │ - var center = this.map.getLonLatFromPixel(targetCenterPx); │ │ │ │ - var xmin = center.lon - extent.getWidth() / 2 * zoomFactor; │ │ │ │ - var xmax = center.lon + extent.getWidth() / 2 * zoomFactor; │ │ │ │ - var ymin = center.lat - extent.getHeight() / 2 * zoomFactor; │ │ │ │ - var ymax = center.lat + extent.getHeight() / 2 * zoomFactor; │ │ │ │ - bounds = new OpenLayers.Bounds(xmin, ymin, xmax, ymax) │ │ │ │ + id = -this.osm_id; │ │ │ │ + this.osm_id++ │ │ │ │ } │ │ │ │ - var lastZoom = this.map.getZoom(), │ │ │ │ - size = this.map.getSize(), │ │ │ │ - centerPx = { │ │ │ │ - x: size.w / 2, │ │ │ │ - y: size.h / 2 │ │ │ │ - }, │ │ │ │ - zoom = this.map.getZoomForExtent(bounds), │ │ │ │ - oldRes = this.map.getResolution(), │ │ │ │ - newRes = this.map.getResolutionForZoom(zoom); │ │ │ │ - if (oldRes == newRes) { │ │ │ │ - this.map.setCenter(this.map.getLonLatFromPixel(targetCenterPx)) │ │ │ │ + if (already_exists) { │ │ │ │ + node = this.created_nodes[id] │ │ │ │ } else { │ │ │ │ - var zoomOriginPx = { │ │ │ │ - x: (oldRes * targetCenterPx.x - newRes * centerPx.x) / (oldRes - newRes), │ │ │ │ - y: (oldRes * targetCenterPx.y - newRes * centerPx.y) / (oldRes - newRes) │ │ │ │ - }; │ │ │ │ - this.map.zoomTo(zoom, zoomOriginPx) │ │ │ │ + var node = this.createElementNS(null, "node") │ │ │ │ } │ │ │ │ - if (lastZoom == this.map.getZoom() && this.alwaysZoom == true) { │ │ │ │ - this.map.zoomTo(lastZoom + (this.out ? -1 : 1)) │ │ │ │ + this.created_nodes[id] = node; │ │ │ │ + node.setAttribute("id", id); │ │ │ │ + node.setAttribute("lon", geometry.x); │ │ │ │ + node.setAttribute("lat", geometry.y); │ │ │ │ + if (point.attributes) { │ │ │ │ + this.serializeTags(point, node) │ │ │ │ } │ │ │ │ - } else if (this.zoomOnClick) { │ │ │ │ - if (!this.out) { │ │ │ │ - this.map.zoomTo(this.map.getZoom() + 1, position) │ │ │ │ + this.setState(point, node); │ │ │ │ + return already_exists ? [] : [node] │ │ │ │ + }, │ │ │ │ + linestring: function(feature) { │ │ │ │ + var id; │ │ │ │ + var nodes = []; │ │ │ │ + var geometry = feature.geometry; │ │ │ │ + if (feature.osm_id) { │ │ │ │ + id = feature.osm_id │ │ │ │ } else { │ │ │ │ - this.map.zoomTo(this.map.getZoom() - 1, position) │ │ │ │ + id = -this.osm_id; │ │ │ │ + this.osm_id++ │ │ │ │ } │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ZoomBox" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.DragPan = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - type: OpenLayers.Control.TYPE_TOOL, │ │ │ │ - panned: false, │ │ │ │ - interval: 0, │ │ │ │ - documentDrag: false, │ │ │ │ - kinetic: null, │ │ │ │ - enableKinetic: true, │ │ │ │ - kineticInterval: 10, │ │ │ │ - draw: function() { │ │ │ │ - if (this.enableKinetic && OpenLayers.Kinetic) { │ │ │ │ - var config = { │ │ │ │ - interval: this.kineticInterval │ │ │ │ - }; │ │ │ │ - if (typeof this.enableKinetic === "object") { │ │ │ │ - config = OpenLayers.Util.extend(config, this.enableKinetic) │ │ │ │ + var way = this.createElementNS(null, "way"); │ │ │ │ + way.setAttribute("id", id); │ │ │ │ + for (var i = 0; i < geometry.components.length; i++) { │ │ │ │ + var node = this.createXML["point"].apply(this, [geometry.components[i]]); │ │ │ │ + if (node.length) { │ │ │ │ + node = node[0]; │ │ │ │ + var node_ref = node.getAttribute("id"); │ │ │ │ + nodes.push(node) │ │ │ │ + } else { │ │ │ │ + node_ref = geometry.components[i].osm_id; │ │ │ │ + node = this.created_nodes[node_ref] │ │ │ │ + } │ │ │ │ + this.setState(feature, node); │ │ │ │ + var nd_dom = this.createElementNS(null, "nd"); │ │ │ │ + nd_dom.setAttribute("ref", node_ref); │ │ │ │ + way.appendChild(nd_dom) │ │ │ │ } │ │ │ │ - this.kinetic = new OpenLayers.Kinetic(config) │ │ │ │ - } │ │ │ │ - this.handler = new OpenLayers.Handler.Drag(this, { │ │ │ │ - move: this.panMap, │ │ │ │ - done: this.panMapDone, │ │ │ │ - down: this.panMapStart │ │ │ │ - }, { │ │ │ │ - interval: this.interval, │ │ │ │ - documentDrag: this.documentDrag │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - panMapStart: function() { │ │ │ │ - if (this.kinetic) { │ │ │ │ - this.kinetic.begin() │ │ │ │ + this.serializeTags(feature, way); │ │ │ │ + nodes.push(way); │ │ │ │ + return nodes │ │ │ │ + }, │ │ │ │ + polygon: function(feature) { │ │ │ │ + var attrs = OpenLayers.Util.extend({ │ │ │ │ + area: "yes" │ │ │ │ + }, feature.attributes); │ │ │ │ + var feat = new OpenLayers.Feature.Vector(feature.geometry.components[0], attrs); │ │ │ │ + feat.osm_id = feature.osm_id; │ │ │ │ + return this.createXML["linestring"].apply(this, [feat]) │ │ │ │ } │ │ │ │ }, │ │ │ │ - panMap: function(xy) { │ │ │ │ - if (this.kinetic) { │ │ │ │ - this.kinetic.update(xy) │ │ │ │ + serializeTags: function(feature, node) { │ │ │ │ + for (var key in feature.attributes) { │ │ │ │ + var tag = this.createElementNS(null, "tag"); │ │ │ │ + tag.setAttribute("k", key); │ │ │ │ + tag.setAttribute("v", feature.attributes[key]); │ │ │ │ + node.appendChild(tag) │ │ │ │ } │ │ │ │ - this.panned = true; │ │ │ │ - this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, { │ │ │ │ - dragging: true, │ │ │ │ - animate: false │ │ │ │ - }) │ │ │ │ }, │ │ │ │ - panMapDone: function(xy) { │ │ │ │ - if (this.panned) { │ │ │ │ - var res = null; │ │ │ │ - if (this.kinetic) { │ │ │ │ - res = this.kinetic.end(xy) │ │ │ │ + setState: function(feature, node) { │ │ │ │ + if (feature.state) { │ │ │ │ + var state = null; │ │ │ │ + switch (feature.state) { │ │ │ │ + case OpenLayers.State.UPDATE: │ │ │ │ + state = "modify"; │ │ │ │ + case OpenLayers.State.DELETE: │ │ │ │ + state = "delete" │ │ │ │ } │ │ │ │ - this.map.pan(this.handler.last.x - xy.x, this.handler.last.y - xy.y, { │ │ │ │ - dragging: !!res, │ │ │ │ - animate: false │ │ │ │ - }); │ │ │ │ - if (res) { │ │ │ │ - var self = this; │ │ │ │ - this.kinetic.move(res, function(x, y, end) { │ │ │ │ - self.map.pan(x, y, { │ │ │ │ - dragging: !end, │ │ │ │ - animate: false │ │ │ │ - }) │ │ │ │ - }) │ │ │ │ + if (state) { │ │ │ │ + node.setAttribute("action", state) │ │ │ │ } │ │ │ │ - this.panned = false │ │ │ │ } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.DragPan" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.OSM" │ │ │ │ }); │ │ │ │ -OpenLayers.Handler.MouseWheel = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - wheelListener: null, │ │ │ │ - interval: 0, │ │ │ │ - maxDelta: Number.POSITIVE_INFINITY, │ │ │ │ - delta: 0, │ │ │ │ - cumulative: true, │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - this.wheelListener = OpenLayers.Function.bindAsEventListener(this.onWheelEvent, this) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - OpenLayers.Handler.prototype.destroy.apply(this, arguments); │ │ │ │ - this.wheelListener = null │ │ │ │ - }, │ │ │ │ - onWheelEvent: function(e) { │ │ │ │ - if (!this.map || !this.checkModifiers(e)) { │ │ │ │ - return │ │ │ │ +OpenLayers.Format.WMC = OpenLayers.Class(OpenLayers.Format.Context, { │ │ │ │ + defaultVersion: "1.1.0", │ │ │ │ + layerToContext: function(layer) { │ │ │ │ + var parser = this.getParser(); │ │ │ │ + var layerContext = { │ │ │ │ + queryable: layer.queryable, │ │ │ │ + visibility: layer.visibility, │ │ │ │ + name: layer.params["LAYERS"], │ │ │ │ + title: layer.name, │ │ │ │ + abstract: layer.metadata["abstract"], │ │ │ │ + dataURL: layer.metadata.dataURL, │ │ │ │ + metadataURL: layer.metadataURL, │ │ │ │ + server: { │ │ │ │ + version: layer.params["VERSION"], │ │ │ │ + url: layer.url │ │ │ │ + }, │ │ │ │ + maxExtent: layer.maxExtent, │ │ │ │ + transparent: layer.params["TRANSPARENT"], │ │ │ │ + numZoomLevels: layer.numZoomLevels, │ │ │ │ + units: layer.units, │ │ │ │ + isBaseLayer: layer.isBaseLayer, │ │ │ │ + opacity: layer.opacity == 1 ? undefined : layer.opacity, │ │ │ │ + displayInLayerSwitcher: layer.displayInLayerSwitcher, │ │ │ │ + singleTile: layer.singleTile, │ │ │ │ + tileSize: layer.singleTile || !layer.tileSize ? undefined : { │ │ │ │ + width: layer.tileSize.w, │ │ │ │ + height: layer.tileSize.h │ │ │ │ + }, │ │ │ │ + minScale: layer.options.resolutions || layer.options.scales || layer.options.maxResolution || layer.options.minScale ? layer.minScale : undefined, │ │ │ │ + maxScale: layer.options.resolutions || layer.options.scales || layer.options.minResolution || layer.options.maxScale ? layer.maxScale : undefined, │ │ │ │ + formats: [], │ │ │ │ + styles: [], │ │ │ │ + srs: layer.srs, │ │ │ │ + dimensions: layer.dimensions │ │ │ │ + }; │ │ │ │ + if (layer.metadata.servertitle) { │ │ │ │ + layerContext.server.title = layer.metadata.servertitle │ │ │ │ } │ │ │ │ - var overScrollableDiv = false; │ │ │ │ - var allowScroll = false; │ │ │ │ - var overMapDiv = false; │ │ │ │ - var elem = OpenLayers.Event.element(e); │ │ │ │ - while (elem != null && !overMapDiv && !overScrollableDiv) { │ │ │ │ - if (!overScrollableDiv) { │ │ │ │ - try { │ │ │ │ - var overflow; │ │ │ │ - if (elem.currentStyle) { │ │ │ │ - overflow = elem.currentStyle["overflow"] │ │ │ │ - } else { │ │ │ │ - var style = document.defaultView.getComputedStyle(elem, null); │ │ │ │ - overflow = style.getPropertyValue("overflow") │ │ │ │ - } │ │ │ │ - overScrollableDiv = overflow && overflow == "auto" || overflow == "scroll" │ │ │ │ - } catch (err) {} │ │ │ │ - } │ │ │ │ - if (!allowScroll) { │ │ │ │ - allowScroll = OpenLayers.Element.hasClass(elem, "olScrollable"); │ │ │ │ - if (!allowScroll) { │ │ │ │ - for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - if (elem == layer.div || elem == layer.pane) { │ │ │ │ - allowScroll = true; │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + if (layer.metadata.formats && layer.metadata.formats.length > 0) { │ │ │ │ + for (var i = 0, len = layer.metadata.formats.length; i < len; i++) { │ │ │ │ + var format = layer.metadata.formats[i]; │ │ │ │ + layerContext.formats.push({ │ │ │ │ + value: format.value, │ │ │ │ + current: format.value == layer.params["FORMAT"] │ │ │ │ + }) │ │ │ │ } │ │ │ │ - overMapDiv = elem == this.map.div; │ │ │ │ - elem = elem.parentNode │ │ │ │ + } else { │ │ │ │ + layerContext.formats.push({ │ │ │ │ + value: layer.params["FORMAT"], │ │ │ │ + current: true │ │ │ │ + }) │ │ │ │ } │ │ │ │ - if (!overScrollableDiv && overMapDiv) { │ │ │ │ - if (allowScroll) { │ │ │ │ - var delta = 0; │ │ │ │ - if (e.wheelDelta) { │ │ │ │ - delta = e.wheelDelta; │ │ │ │ - if (delta % 160 === 0) { │ │ │ │ - delta = delta * .75 │ │ │ │ - } │ │ │ │ - delta = delta / 120 │ │ │ │ - } else if (e.detail) { │ │ │ │ - delta = -(e.detail / Math.abs(e.detail)) │ │ │ │ - } │ │ │ │ - this.delta += delta; │ │ │ │ - window.clearTimeout(this._timeoutId); │ │ │ │ - if (this.interval && Math.abs(this.delta) < this.maxDelta) { │ │ │ │ - var evt = OpenLayers.Util.extend({}, e); │ │ │ │ - this._timeoutId = window.setTimeout(OpenLayers.Function.bind(function() { │ │ │ │ - this.wheelZoom(evt) │ │ │ │ - }, this), this.interval) │ │ │ │ + if (layer.metadata.styles && layer.metadata.styles.length > 0) { │ │ │ │ + for (var i = 0, len = layer.metadata.styles.length; i < len; i++) { │ │ │ │ + var style = layer.metadata.styles[i]; │ │ │ │ + if (style.href == layer.params["SLD"] || style.body == layer.params["SLD_BODY"] || style.name == layer.params["STYLES"]) { │ │ │ │ + style.current = true │ │ │ │ } else { │ │ │ │ - this.wheelZoom(e) │ │ │ │ + style.current = false │ │ │ │ } │ │ │ │ + layerContext.styles.push(style) │ │ │ │ } │ │ │ │ - OpenLayers.Event.stop(e) │ │ │ │ + } else { │ │ │ │ + layerContext.styles.push({ │ │ │ │ + href: layer.params["SLD"], │ │ │ │ + body: layer.params["SLD_BODY"], │ │ │ │ + name: layer.params["STYLES"] || parser.defaultStyleName, │ │ │ │ + title: parser.defaultStyleTitle, │ │ │ │ + current: true │ │ │ │ + }) │ │ │ │ } │ │ │ │ + return layerContext │ │ │ │ }, │ │ │ │ - wheelZoom: function(e) { │ │ │ │ - var delta = this.delta; │ │ │ │ - this.delta = 0; │ │ │ │ - if (delta) { │ │ │ │ - e.xy = this.map.events.getMousePosition(e); │ │ │ │ - if (delta < 0) { │ │ │ │ - this.callback("down", [e, this.cumulative ? Math.max(-this.maxDelta, delta) : -1]) │ │ │ │ - } else { │ │ │ │ - this.callback("up", [e, this.cumulative ? Math.min(this.maxDelta, delta) : 1]) │ │ │ │ + toContext: function(obj) { │ │ │ │ + var context = {}; │ │ │ │ + var layers = obj.layers; │ │ │ │ + if (obj.CLASS_NAME == "OpenLayers.Map") { │ │ │ │ + var metadata = obj.metadata || {}; │ │ │ │ + context.size = obj.getSize(); │ │ │ │ + context.bounds = obj.getExtent(); │ │ │ │ + context.projection = obj.projection; │ │ │ │ + context.title = obj.title; │ │ │ │ + context.keywords = metadata.keywords; │ │ │ │ + context["abstract"] = metadata["abstract"]; │ │ │ │ + context.logo = metadata.logo; │ │ │ │ + context.descriptionURL = metadata.descriptionURL; │ │ │ │ + context.contactInformation = metadata.contactInformation; │ │ │ │ + context.maxExtent = obj.maxExtent │ │ │ │ + } else { │ │ │ │ + OpenLayers.Util.applyDefaults(context, obj); │ │ │ │ + if (context.layers != undefined) { │ │ │ │ + delete context.layers │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - activate: function(evt) { │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - var wheelListener = this.wheelListener; │ │ │ │ - OpenLayers.Event.observe(window, "DOMMouseScroll", wheelListener); │ │ │ │ - OpenLayers.Event.observe(window, "mousewheel", wheelListener); │ │ │ │ - OpenLayers.Event.observe(document, "mousewheel", wheelListener); │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ + if (context.layersContext == undefined) { │ │ │ │ + context.layersContext = [] │ │ │ │ } │ │ │ │ - }, │ │ │ │ - deactivate: function(evt) { │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - var wheelListener = this.wheelListener; │ │ │ │ - OpenLayers.Event.stopObserving(window, "DOMMouseScroll", wheelListener); │ │ │ │ - OpenLayers.Event.stopObserving(window, "mousewheel", wheelListener); │ │ │ │ - OpenLayers.Event.stopObserving(document, "mousewheel", wheelListener); │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ + if (layers != undefined && OpenLayers.Util.isArray(layers)) { │ │ │ │ + for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ + var layer = layers[i]; │ │ │ │ + if (layer instanceof OpenLayers.Layer.WMS) { │ │ │ │ + context.layersContext.push(this.layerToContext(layer)) │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + return context │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.MouseWheel" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMC" │ │ │ │ }); │ │ │ │ -OpenLayers.Control.Navigation = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - dragPan: null, │ │ │ │ - dragPanOptions: null, │ │ │ │ - pinchZoom: null, │ │ │ │ - pinchZoomOptions: null, │ │ │ │ - documentDrag: false, │ │ │ │ - zoomBox: null, │ │ │ │ - zoomBoxEnabled: true, │ │ │ │ - zoomWheelEnabled: true, │ │ │ │ - mouseWheelOptions: null, │ │ │ │ - handleRightClicks: false, │ │ │ │ - zoomBoxKeyMask: OpenLayers.Handler.MOD_SHIFT, │ │ │ │ - autoActivate: true, │ │ │ │ - initialize: function(options) { │ │ │ │ - this.handlers = {}; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - if (this.dragPan) { │ │ │ │ - this.dragPan.destroy() │ │ │ │ - } │ │ │ │ - this.dragPan = null; │ │ │ │ - if (this.zoomBox) { │ │ │ │ - this.zoomBox.destroy() │ │ │ │ - } │ │ │ │ - this.zoomBox = null; │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.destroy() │ │ │ │ - } │ │ │ │ - this.pinchZoom = null; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ +OpenLayers.Format.WFSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + defaultVersion: "1.1.0", │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFSCapabilities" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.QueryStringFilter = function() { │ │ │ │ + var cmpToStr = {}; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = "eq"; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = "ne"; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = "lt"; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = "lte"; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = "gt"; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = "gte"; │ │ │ │ + cmpToStr[OpenLayers.Filter.Comparison.LIKE] = "ilike"; │ │ │ │ + │ │ │ │ + function regex2value(value) { │ │ │ │ + value = value.replace(/%/g, "\\%"); │ │ │ │ + value = value.replace(/\\\\\.(\*)?/g, function($0, $1) { │ │ │ │ + return $1 ? $0 : "\\\\_" │ │ │ │ + }); │ │ │ │ + value = value.replace(/\\\\\.\*/g, "\\\\%"); │ │ │ │ + value = value.replace(/(\\)?\.(\*)?/g, function($0, $1, $2) { │ │ │ │ + return $1 || $2 ? $0 : "_" │ │ │ │ + }); │ │ │ │ + value = value.replace(/(\\)?\.\*/g, function($0, $1) { │ │ │ │ + return $1 ? $0 : "%" │ │ │ │ + }); │ │ │ │ + value = value.replace(/\\\./g, "."); │ │ │ │ + value = value.replace(/(\\)?\\\*/g, function($0, $1) { │ │ │ │ + return $1 ? $0 : "*" │ │ │ │ + }); │ │ │ │ + return value │ │ │ │ + } │ │ │ │ + return OpenLayers.Class(OpenLayers.Format, { │ │ │ │ + wildcarded: false, │ │ │ │ + srsInBBOX: false, │ │ │ │ + write: function(filter, params) { │ │ │ │ + params = params || {}; │ │ │ │ + var className = filter.CLASS_NAME; │ │ │ │ + var filterType = className.substring(className.lastIndexOf(".") + 1); │ │ │ │ + switch (filterType) { │ │ │ │ + case "Spatial": │ │ │ │ + switch (filter.type) { │ │ │ │ + case OpenLayers.Filter.Spatial.BBOX: │ │ │ │ + params.bbox = filter.value.toArray(); │ │ │ │ + if (this.srsInBBOX && filter.projection) { │ │ │ │ + params.bbox.push(filter.projection.getCode()) │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case OpenLayers.Filter.Spatial.DWITHIN: │ │ │ │ + params.tolerance = filter.distance; │ │ │ │ + case OpenLayers.Filter.Spatial.WITHIN: │ │ │ │ + params.lon = filter.value.x; │ │ │ │ + params.lat = filter.value.y; │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + OpenLayers.Console.warn("Unknown spatial filter type " + filter.type) │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case "Comparison": │ │ │ │ + var op = cmpToStr[filter.type]; │ │ │ │ + if (op !== undefined) { │ │ │ │ + var value = filter.value; │ │ │ │ + if (filter.type == OpenLayers.Filter.Comparison.LIKE) { │ │ │ │ + value = regex2value(value); │ │ │ │ + if (this.wildcarded) { │ │ │ │ + value = "%" + value + "%" │ │ │ │ + } │ │ │ │ + } │ │ │ │ + params[filter.property + "__" + op] = value; │ │ │ │ + params.queryable = params.queryable || []; │ │ │ │ + params.queryable.push(filter.property) │ │ │ │ + } else { │ │ │ │ + OpenLayers.Console.warn("Unknown comparison filter type " + filter.type) │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + case "Logical": │ │ │ │ + if (filter.type === OpenLayers.Filter.Logical.AND) { │ │ │ │ + for (var i = 0, len = filter.filters.length; i < len; i++) { │ │ │ │ + params = this.write(filter.filters[i], params) │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + OpenLayers.Console.warn("Unsupported logical filter type " + filter.type) │ │ │ │ + } │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + OpenLayers.Console.warn("Unknown filter type " + filterType) │ │ │ │ + } │ │ │ │ + return params │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.QueryStringFilter" │ │ │ │ + }) │ │ │ │ +}(); │ │ │ │ +OpenLayers.Format.WMSGetFeatureInfo = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + layerIdentifier: "_layer", │ │ │ │ + featureIdentifier: "_feature", │ │ │ │ + regExes: { │ │ │ │ + trimSpace: /^\s*|\s*$/g, │ │ │ │ + removeSpace: /\s*/g, │ │ │ │ + splitSpace: /\s+/, │ │ │ │ + trimComma: /\s*,\s*/g │ │ │ │ }, │ │ │ │ - activate: function() { │ │ │ │ - this.dragPan.activate(); │ │ │ │ - if (this.zoomWheelEnabled) { │ │ │ │ - this.handlers.wheel.activate() │ │ │ │ - } │ │ │ │ - this.handlers.click.activate(); │ │ │ │ - if (this.zoomBoxEnabled) { │ │ │ │ - this.zoomBox.activate() │ │ │ │ + gmlFormat: null, │ │ │ │ + read: function(data) { │ │ │ │ + var result; │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]) │ │ │ │ } │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.activate() │ │ │ │ + var root = data.documentElement; │ │ │ │ + if (root) { │ │ │ │ + var scope = this; │ │ │ │ + var read = this["read_" + root.nodeName]; │ │ │ │ + if (read) { │ │ │ │ + result = read.call(this, root) │ │ │ │ + } else { │ │ │ │ + result = new OpenLayers.Format.GML(this.options ? this.options : {}).read(data) │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + result = data │ │ │ │ } │ │ │ │ - return OpenLayers.Control.prototype.activate.apply(this, arguments) │ │ │ │ + return result │ │ │ │ }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.deactivate() │ │ │ │ + read_msGMLOutput: function(data) { │ │ │ │ + var response = []; │ │ │ │ + var layerNodes = this.getSiblingNodesByTagCriteria(data, this.layerIdentifier); │ │ │ │ + if (layerNodes) { │ │ │ │ + for (var i = 0, len = layerNodes.length; i < len; ++i) { │ │ │ │ + var node = layerNodes[i]; │ │ │ │ + var layerName = node.nodeName; │ │ │ │ + if (node.prefix) { │ │ │ │ + layerName = layerName.split(":")[1] │ │ │ │ + } │ │ │ │ + var layerName = layerName.replace(this.layerIdentifier, ""); │ │ │ │ + var featureNodes = this.getSiblingNodesByTagCriteria(node, this.featureIdentifier); │ │ │ │ + if (featureNodes) { │ │ │ │ + for (var j = 0; j < featureNodes.length; j++) { │ │ │ │ + var featureNode = featureNodes[j]; │ │ │ │ + var geomInfo = this.parseGeometry(featureNode); │ │ │ │ + var attributes = this.parseAttributes(featureNode); │ │ │ │ + var feature = new OpenLayers.Feature.Vector(geomInfo.geometry, attributes, null); │ │ │ │ + feature.bounds = geomInfo.bounds; │ │ │ │ + feature.type = layerName; │ │ │ │ + response.push(feature) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.zoomBox.deactivate(); │ │ │ │ - this.dragPan.deactivate(); │ │ │ │ - this.handlers.click.deactivate(); │ │ │ │ - this.handlers.wheel.deactivate(); │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply(this, arguments) │ │ │ │ + return response │ │ │ │ }, │ │ │ │ - draw: function() { │ │ │ │ - if (this.handleRightClicks) { │ │ │ │ - this.map.viewPortDiv.oncontextmenu = OpenLayers.Function.False │ │ │ │ - } │ │ │ │ - var clickCallbacks = { │ │ │ │ - click: this.defaultClick, │ │ │ │ - dblclick: this.defaultDblClick, │ │ │ │ - dblrightclick: this.defaultDblRightClick │ │ │ │ - }; │ │ │ │ - var clickOptions = { │ │ │ │ - double: true, │ │ │ │ - stopDouble: true │ │ │ │ - }; │ │ │ │ - this.handlers.click = new OpenLayers.Handler.Click(this, clickCallbacks, clickOptions); │ │ │ │ - this.dragPan = new OpenLayers.Control.DragPan(OpenLayers.Util.extend({ │ │ │ │ - map: this.map, │ │ │ │ - documentDrag: this.documentDrag │ │ │ │ - }, this.dragPanOptions)); │ │ │ │ - this.zoomBox = new OpenLayers.Control.ZoomBox({ │ │ │ │ - map: this.map, │ │ │ │ - keyMask: this.zoomBoxKeyMask │ │ │ │ - }); │ │ │ │ - this.dragPan.draw(); │ │ │ │ - this.zoomBox.draw(); │ │ │ │ - var wheelOptions = this.map.fractionalZoom ? {} : { │ │ │ │ - cumulative: false, │ │ │ │ - interval: 50, │ │ │ │ - maxDelta: 6 │ │ │ │ - }; │ │ │ │ - this.handlers.wheel = new OpenLayers.Handler.MouseWheel(this, { │ │ │ │ - up: this.wheelUp, │ │ │ │ - down: this.wheelDown │ │ │ │ - }, OpenLayers.Util.extend(wheelOptions, this.mouseWheelOptions)); │ │ │ │ - if (OpenLayers.Control.PinchZoom) { │ │ │ │ - this.pinchZoom = new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({ │ │ │ │ - map: this.map │ │ │ │ - }, this.pinchZoomOptions)) │ │ │ │ + read_FeatureInfoResponse: function(data) { │ │ │ │ + var response = []; │ │ │ │ + var featureNodes = this.getElementsByTagNameNS(data, "*", "FIELDS"); │ │ │ │ + for (var i = 0, len = featureNodes.length; i < len; i++) { │ │ │ │ + var featureNode = featureNodes[i]; │ │ │ │ + var geom = null; │ │ │ │ + var attributes = {}; │ │ │ │ + var j; │ │ │ │ + var jlen = featureNode.attributes.length; │ │ │ │ + if (jlen > 0) { │ │ │ │ + for (j = 0; j < jlen; j++) { │ │ │ │ + var attribute = featureNode.attributes[j]; │ │ │ │ + attributes[attribute.nodeName] = attribute.nodeValue │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + var nodes = featureNode.childNodes; │ │ │ │ + for (j = 0, jlen = nodes.length; j < jlen; ++j) { │ │ │ │ + var node = nodes[j]; │ │ │ │ + if (node.nodeType != 3) { │ │ │ │ + attributes[node.getAttribute("name")] = node.getAttribute("value") │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + response.push(new OpenLayers.Feature.Vector(geom, attributes, null)) │ │ │ │ } │ │ │ │ + return response │ │ │ │ }, │ │ │ │ - defaultClick: function(evt) { │ │ │ │ - if (evt.lastTouches && evt.lastTouches.length == 2) { │ │ │ │ - this.map.zoomOut() │ │ │ │ + getSiblingNodesByTagCriteria: function(node, criteria) { │ │ │ │ + var nodes = []; │ │ │ │ + var children, tagName, n, matchNodes, child; │ │ │ │ + if (node && node.hasChildNodes()) { │ │ │ │ + children = node.childNodes; │ │ │ │ + n = children.length; │ │ │ │ + for (var k = 0; k < n; k++) { │ │ │ │ + child = children[k]; │ │ │ │ + while (child && child.nodeType != 1) { │ │ │ │ + child = child.nextSibling; │ │ │ │ + k++ │ │ │ │ + } │ │ │ │ + tagName = child ? child.nodeName : ""; │ │ │ │ + if (tagName.length > 0 && tagName.indexOf(criteria) > -1) { │ │ │ │ + nodes.push(child) │ │ │ │ + } else { │ │ │ │ + matchNodes = this.getSiblingNodesByTagCriteria(child, criteria); │ │ │ │ + if (matchNodes.length > 0) { │ │ │ │ + nodes.length == 0 ? nodes = matchNodes : nodes.push(matchNodes) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + return nodes │ │ │ │ }, │ │ │ │ - defaultDblClick: function(evt) { │ │ │ │ - this.map.zoomTo(this.map.zoom + 1, evt.xy) │ │ │ │ - }, │ │ │ │ - defaultDblRightClick: function(evt) { │ │ │ │ - this.map.zoomTo(this.map.zoom - 1, evt.xy) │ │ │ │ - }, │ │ │ │ - wheelChange: function(evt, deltaZ) { │ │ │ │ - if (!this.map.fractionalZoom) { │ │ │ │ - deltaZ = Math.round(deltaZ) │ │ │ │ - } │ │ │ │ - var currentZoom = this.map.getZoom(), │ │ │ │ - newZoom = currentZoom + deltaZ; │ │ │ │ - newZoom = Math.max(newZoom, 0); │ │ │ │ - newZoom = Math.min(newZoom, this.map.getNumZoomLevels()); │ │ │ │ - if (newZoom === currentZoom) { │ │ │ │ - return │ │ │ │ + parseAttributes: function(node) { │ │ │ │ + var attributes = {}; │ │ │ │ + if (node.nodeType == 1) { │ │ │ │ + var children = node.childNodes; │ │ │ │ + var n = children.length; │ │ │ │ + for (var i = 0; i < n; ++i) { │ │ │ │ + var child = children[i]; │ │ │ │ + if (child.nodeType == 1) { │ │ │ │ + var grandchildren = child.childNodes; │ │ │ │ + var name = child.prefix ? child.nodeName.split(":")[1] : child.nodeName; │ │ │ │ + if (grandchildren.length == 0) { │ │ │ │ + attributes[name] = null │ │ │ │ + } else if (grandchildren.length == 1) { │ │ │ │ + var grandchild = grandchildren[0]; │ │ │ │ + if (grandchild.nodeType == 3 || grandchild.nodeType == 4) { │ │ │ │ + var value = grandchild.nodeValue.replace(this.regExes.trimSpace, ""); │ │ │ │ + attributes[name] = value │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.map.zoomTo(newZoom, evt.xy) │ │ │ │ - }, │ │ │ │ - wheelUp: function(evt, delta) { │ │ │ │ - this.wheelChange(evt, delta || 1) │ │ │ │ - }, │ │ │ │ - wheelDown: function(evt, delta) { │ │ │ │ - this.wheelChange(evt, delta || -1) │ │ │ │ - }, │ │ │ │ - disableZoomBox: function() { │ │ │ │ - this.zoomBoxEnabled = false; │ │ │ │ - this.zoomBox.deactivate() │ │ │ │ + return attributes │ │ │ │ }, │ │ │ │ - enableZoomBox: function() { │ │ │ │ - this.zoomBoxEnabled = true; │ │ │ │ - if (this.active) { │ │ │ │ - this.zoomBox.activate() │ │ │ │ + parseGeometry: function(node) { │ │ │ │ + if (!this.gmlFormat) { │ │ │ │ + this.gmlFormat = new OpenLayers.Format.GML │ │ │ │ } │ │ │ │ - }, │ │ │ │ - disableZoomWheel: function() { │ │ │ │ - this.zoomWheelEnabled = false; │ │ │ │ - this.handlers.wheel.deactivate() │ │ │ │ - }, │ │ │ │ - enableZoomWheel: function() { │ │ │ │ - this.zoomWheelEnabled = true; │ │ │ │ - if (this.active) { │ │ │ │ - this.handlers.wheel.activate() │ │ │ │ + var feature = this.gmlFormat.parseFeature(node); │ │ │ │ + var geometry, bounds = null; │ │ │ │ + if (feature) { │ │ │ │ + geometry = feature.geometry && feature.geometry.clone(); │ │ │ │ + bounds = feature.bounds && feature.bounds.clone(); │ │ │ │ + feature.destroy() │ │ │ │ } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Navigation" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.NavToolbar = OpenLayers.Class(OpenLayers.Control.Panel, { │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); │ │ │ │ - this.addControls([new OpenLayers.Control.Navigation, new OpenLayers.Control.ZoomBox]) │ │ │ │ - }, │ │ │ │ - draw: function() { │ │ │ │ - var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments); │ │ │ │ - if (this.defaultControl === null) { │ │ │ │ - this.defaultControl = this.controls[0] │ │ │ │ + return { │ │ │ │ + geometry: geometry, │ │ │ │ + bounds: bounds │ │ │ │ } │ │ │ │ - return div │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.NavToolbar" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSGetFeatureInfo" │ │ │ │ }); │ │ │ │ -OpenLayers.Control.TransformFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - geometryTypes: null, │ │ │ │ - layer: null, │ │ │ │ - preserveAspectRatio: false, │ │ │ │ - rotate: true, │ │ │ │ - feature: null, │ │ │ │ - renderIntent: "temporary", │ │ │ │ - rotationHandleSymbolizer: null, │ │ │ │ - box: null, │ │ │ │ - center: null, │ │ │ │ - scale: 1, │ │ │ │ - ratio: 1, │ │ │ │ - rotation: 0, │ │ │ │ - handles: null, │ │ │ │ - rotationHandles: null, │ │ │ │ - dragControl: null, │ │ │ │ - irregular: false, │ │ │ │ - initialize: function(layer, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.layer = layer; │ │ │ │ - if (!this.rotationHandleSymbolizer) { │ │ │ │ - this.rotationHandleSymbolizer = { │ │ │ │ - stroke: false, │ │ │ │ - pointRadius: 10, │ │ │ │ - fillOpacity: 0, │ │ │ │ - cursor: "pointer" │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.createBox(); │ │ │ │ - this.createControl() │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - var activated = false; │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.dragControl.activate(); │ │ │ │ - this.layer.addFeatures([this.box]); │ │ │ │ - this.rotate && this.layer.addFeatures(this.rotationHandles); │ │ │ │ - this.layer.addFeatures(this.handles); │ │ │ │ - activated = true │ │ │ │ - } │ │ │ │ - return activated │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.layer.removeFeatures(this.handles); │ │ │ │ - this.rotate && this.layer.removeFeatures(this.rotationHandles); │ │ │ │ - this.layer.removeFeatures([this.box]); │ │ │ │ - this.dragControl.deactivate(); │ │ │ │ - deactivated = true │ │ │ │ - } │ │ │ │ - return deactivated │ │ │ │ +OpenLayers.Format.SOSGetObservation = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + namespaces: { │ │ │ │ + ows: "http://www.opengis.net/ows", │ │ │ │ + gml: "http://www.opengis.net/gml", │ │ │ │ + sos: "http://www.opengis.net/sos/1.0", │ │ │ │ + ogc: "http://www.opengis.net/ogc", │ │ │ │ + om: "http://www.opengis.net/om/1.0", │ │ │ │ + sa: "http://www.opengis.net/sampling/1.0", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - this.dragControl.setMap(map); │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments) │ │ │ │ + regExes: { │ │ │ │ + trimSpace: /^\s*|\s*$/g, │ │ │ │ + removeSpace: /\s*/g, │ │ │ │ + splitSpace: /\s+/, │ │ │ │ + trimComma: /\s*,\s*/g │ │ │ │ }, │ │ │ │ - setFeature: function(feature, initialParams) { │ │ │ │ - initialParams = OpenLayers.Util.applyDefaults(initialParams, { │ │ │ │ - rotation: 0, │ │ │ │ - scale: 1, │ │ │ │ - ratio: 1 │ │ │ │ - }); │ │ │ │ - var oldRotation = this.rotation; │ │ │ │ - var oldCenter = this.center; │ │ │ │ - OpenLayers.Util.extend(this, initialParams); │ │ │ │ - var cont = this.events.triggerEvent("beforesetfeature", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (cont === false) { │ │ │ │ - return │ │ │ │ + VERSION: "1.0.0", │ │ │ │ + schemaLocation: "http://www.opengis.net/sos/1.0 http://schemas.opengis.net/sos/1.0.0/sosGetObservation.xsd", │ │ │ │ + defaultPrefix: "sos", │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]) │ │ │ │ } │ │ │ │ - this.feature = feature; │ │ │ │ - this.activate(); │ │ │ │ - this._setfeature = true; │ │ │ │ - var featureBounds = this.feature.geometry.getBounds(); │ │ │ │ - this.box.move(featureBounds.getCenterLonLat()); │ │ │ │ - this.box.geometry.rotate(-oldRotation, oldCenter); │ │ │ │ - this._angle = 0; │ │ │ │ - var ll; │ │ │ │ - if (this.rotation) { │ │ │ │ - var geom = feature.geometry.clone(); │ │ │ │ - geom.rotate(-this.rotation, this.center); │ │ │ │ - var box = new OpenLayers.Feature.Vector(geom.getBounds().toGeometry()); │ │ │ │ - box.geometry.rotate(this.rotation, this.center); │ │ │ │ - this.box.geometry.rotate(this.rotation, this.center); │ │ │ │ - this.box.move(box.geometry.getBounds().getCenterLonLat()); │ │ │ │ - var llGeom = box.geometry.components[0].components[0]; │ │ │ │ - ll = llGeom.getBounds().getCenterLonLat() │ │ │ │ - } else { │ │ │ │ - ll = new OpenLayers.LonLat(featureBounds.left, featureBounds.bottom) │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement │ │ │ │ } │ │ │ │ - this.handles[0].move(ll); │ │ │ │ - delete this._setfeature; │ │ │ │ - this.events.triggerEvent("setfeature", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ + var info = { │ │ │ │ + measurements: [], │ │ │ │ + observations: [] │ │ │ │ + }; │ │ │ │ + this.readNode(data, info); │ │ │ │ + return info │ │ │ │ }, │ │ │ │ - unsetFeature: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.deactivate() │ │ │ │ - } else { │ │ │ │ - this.feature = null; │ │ │ │ - this.rotation = 0; │ │ │ │ - this.scale = 1; │ │ │ │ - this.ratio = 1 │ │ │ │ - } │ │ │ │ + write: function(options) { │ │ │ │ + var node = this.writeNode("sos:GetObservation", options); │ │ │ │ + node.setAttribute("xmlns:om", this.namespaces.om); │ │ │ │ + node.setAttribute("xmlns:ogc", this.namespaces.ogc); │ │ │ │ + this.setAttributeNS(node, this.namespaces.xsi, "xsi:schemaLocation", this.schemaLocation); │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [node]) │ │ │ │ }, │ │ │ │ - createBox: function() { │ │ │ │ - var control = this; │ │ │ │ - this.center = new OpenLayers.Geometry.Point(0, 0); │ │ │ │ - this.box = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString([new OpenLayers.Geometry.Point(-1, -1), new OpenLayers.Geometry.Point(0, -1), new OpenLayers.Geometry.Point(1, -1), new OpenLayers.Geometry.Point(1, 0), new OpenLayers.Geometry.Point(1, 1), new OpenLayers.Geometry.Point(0, 1), new OpenLayers.Geometry.Point(-1, 1), new OpenLayers.Geometry.Point(-1, 0), new OpenLayers.Geometry.Point(-1, -1)]), null, typeof this.renderIntent == "string" ? null : this.renderIntent); │ │ │ │ - this.box.geometry.move = function(x, y) { │ │ │ │ - control._moving = true; │ │ │ │ - OpenLayers.Geometry.LineString.prototype.move.apply(this, arguments); │ │ │ │ - control.center.move(x, y); │ │ │ │ - delete control._moving │ │ │ │ - }; │ │ │ │ - var vertexMoveFn = function(x, y) { │ │ │ │ - OpenLayers.Geometry.Point.prototype.move.apply(this, arguments); │ │ │ │ - this._rotationHandle && this._rotationHandle.geometry.move(x, y); │ │ │ │ - this._handle.geometry.move(x, y) │ │ │ │ - }; │ │ │ │ - var vertexResizeFn = function(scale, center, ratio) { │ │ │ │ - OpenLayers.Geometry.Point.prototype.resize.apply(this, arguments); │ │ │ │ - this._rotationHandle && this._rotationHandle.geometry.resize(scale, center, ratio); │ │ │ │ - this._handle.geometry.resize(scale, center, ratio) │ │ │ │ - }; │ │ │ │ - var vertexRotateFn = function(angle, center) { │ │ │ │ - OpenLayers.Geometry.Point.prototype.rotate.apply(this, arguments); │ │ │ │ - this._rotationHandle && this._rotationHandle.geometry.rotate(angle, center); │ │ │ │ - this._handle.geometry.rotate(angle, center) │ │ │ │ - }; │ │ │ │ - var handleMoveFn = function(x, y) { │ │ │ │ - var oldX = this.x, │ │ │ │ - oldY = this.y; │ │ │ │ - OpenLayers.Geometry.Point.prototype.move.call(this, x, y); │ │ │ │ - if (control._moving) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var evt = control.dragControl.handlers.drag.evt; │ │ │ │ - var preserveAspectRatio = !control._setfeature && control.preserveAspectRatio; │ │ │ │ - var reshape = !preserveAspectRatio && !(evt && evt.shiftKey); │ │ │ │ - var oldGeom = new OpenLayers.Geometry.Point(oldX, oldY); │ │ │ │ - var centerGeometry = control.center; │ │ │ │ - this.rotate(-control.rotation, centerGeometry); │ │ │ │ - oldGeom.rotate(-control.rotation, centerGeometry); │ │ │ │ - var dx1 = this.x - centerGeometry.x; │ │ │ │ - var dy1 = this.y - centerGeometry.y; │ │ │ │ - var dx0 = dx1 - (this.x - oldGeom.x); │ │ │ │ - var dy0 = dy1 - (this.y - oldGeom.y); │ │ │ │ - if (control.irregular && !control._setfeature) { │ │ │ │ - dx1 -= (this.x - oldGeom.x) / 2; │ │ │ │ - dy1 -= (this.y - oldGeom.y) / 2 │ │ │ │ + readers: { │ │ │ │ + om: { │ │ │ │ + ObservationCollection: function(node, obj) { │ │ │ │ + obj.id = this.getAttributeNS(node, this.namespaces.gml, "id"); │ │ │ │ + this.readChildNodes(node, obj) │ │ │ │ + }, │ │ │ │ + member: function(node, observationCollection) { │ │ │ │ + this.readChildNodes(node, observationCollection) │ │ │ │ + }, │ │ │ │ + Measurement: function(node, observationCollection) { │ │ │ │ + var measurement = {}; │ │ │ │ + observationCollection.measurements.push(measurement); │ │ │ │ + this.readChildNodes(node, measurement) │ │ │ │ + }, │ │ │ │ + Observation: function(node, observationCollection) { │ │ │ │ + var observation = {}; │ │ │ │ + observationCollection.observations.push(observation); │ │ │ │ + this.readChildNodes(node, observation) │ │ │ │ + }, │ │ │ │ + samplingTime: function(node, measurement) { │ │ │ │ + var samplingTime = {}; │ │ │ │ + measurement.samplingTime = samplingTime; │ │ │ │ + this.readChildNodes(node, samplingTime) │ │ │ │ + }, │ │ │ │ + observedProperty: function(node, measurement) { │ │ │ │ + measurement.observedProperty = this.getAttributeNS(node, this.namespaces.xlink, "href"); │ │ │ │ + this.readChildNodes(node, measurement) │ │ │ │ + }, │ │ │ │ + procedure: function(node, measurement) { │ │ │ │ + measurement.procedure = this.getAttributeNS(node, this.namespaces.xlink, "href"); │ │ │ │ + this.readChildNodes(node, measurement) │ │ │ │ + }, │ │ │ │ + featureOfInterest: function(node, observation) { │ │ │ │ + var foi = { │ │ │ │ + features: [] │ │ │ │ + }; │ │ │ │ + observation.fois = []; │ │ │ │ + observation.fois.push(foi); │ │ │ │ + this.readChildNodes(node, foi); │ │ │ │ + var features = []; │ │ │ │ + for (var i = 0, len = foi.features.length; i < len; i++) { │ │ │ │ + var feature = foi.features[i]; │ │ │ │ + features.push(new OpenLayers.Feature.Vector(feature.components[0], feature.attributes)) │ │ │ │ + } │ │ │ │ + foi.features = features │ │ │ │ + }, │ │ │ │ + result: function(node, measurement) { │ │ │ │ + var result = {}; │ │ │ │ + measurement.result = result; │ │ │ │ + if (this.getChildValue(node) !== "") { │ │ │ │ + result.value = this.getChildValue(node); │ │ │ │ + result.uom = node.getAttribute("uom") │ │ │ │ + } else { │ │ │ │ + this.readChildNodes(node, result) │ │ │ │ + } │ │ │ │ } │ │ │ │ - this.x = oldX; │ │ │ │ - this.y = oldY; │ │ │ │ - var scale, ratio = 1; │ │ │ │ - if (reshape) { │ │ │ │ - scale = Math.abs(dy0) < 1e-5 ? 1 : dy1 / dy0; │ │ │ │ - ratio = (Math.abs(dx0) < 1e-5 ? 1 : dx1 / dx0) / scale │ │ │ │ - } else { │ │ │ │ - var l0 = Math.sqrt(dx0 * dx0 + dy0 * dy0); │ │ │ │ - var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1); │ │ │ │ - scale = l1 / l0 │ │ │ │ + }, │ │ │ │ + sa: OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.sa, │ │ │ │ + gml: OpenLayers.Util.applyDefaults({ │ │ │ │ + TimeInstant: function(node, samplingTime) { │ │ │ │ + var timeInstant = {}; │ │ │ │ + samplingTime.timeInstant = timeInstant; │ │ │ │ + this.readChildNodes(node, timeInstant) │ │ │ │ + }, │ │ │ │ + timePosition: function(node, timeInstant) { │ │ │ │ + timeInstant.timePosition = this.getChildValue(node) │ │ │ │ } │ │ │ │ - control._moving = true; │ │ │ │ - control.box.geometry.rotate(-control.rotation, centerGeometry); │ │ │ │ - delete control._moving; │ │ │ │ - control.box.geometry.resize(scale, centerGeometry, ratio); │ │ │ │ - control.box.geometry.rotate(control.rotation, centerGeometry); │ │ │ │ - control.transformFeature({ │ │ │ │ - scale: scale, │ │ │ │ - ratio: ratio │ │ │ │ - }); │ │ │ │ - if (control.irregular && !control._setfeature) { │ │ │ │ - var newCenter = centerGeometry.clone(); │ │ │ │ - newCenter.x += Math.abs(oldX - centerGeometry.x) < 1e-5 ? 0 : this.x - oldX; │ │ │ │ - newCenter.y += Math.abs(oldY - centerGeometry.y) < 1e-5 ? 0 : this.y - oldY; │ │ │ │ - control.box.geometry.move(this.x - oldX, this.y - oldY); │ │ │ │ - control.transformFeature({ │ │ │ │ - center: newCenter │ │ │ │ + }, OpenLayers.Format.SOSGetFeatureOfInterest.prototype.readers.gml) │ │ │ │ + }, │ │ │ │ + writers: { │ │ │ │ + sos: { │ │ │ │ + GetObservation: function(options) { │ │ │ │ + var node = this.createElementNSPlus("GetObservation", { │ │ │ │ + attributes: { │ │ │ │ + version: this.VERSION, │ │ │ │ + service: "SOS" │ │ │ │ + } │ │ │ │ + }); │ │ │ │ + this.writeNode("offering", options, node); │ │ │ │ + if (options.eventTime) { │ │ │ │ + this.writeNode("eventTime", options, node) │ │ │ │ + } │ │ │ │ + for (var procedure in options.procedures) { │ │ │ │ + this.writeNode("procedure", options.procedures[procedure], node) │ │ │ │ + } │ │ │ │ + for (var observedProperty in options.observedProperties) { │ │ │ │ + this.writeNode("observedProperty", options.observedProperties[observedProperty], node) │ │ │ │ + } │ │ │ │ + if (options.foi) { │ │ │ │ + this.writeNode("featureOfInterest", options.foi, node) │ │ │ │ + } │ │ │ │ + this.writeNode("responseFormat", options, node); │ │ │ │ + if (options.resultModel) { │ │ │ │ + this.writeNode("resultModel", options, node) │ │ │ │ + } │ │ │ │ + if (options.responseMode) { │ │ │ │ + this.writeNode("responseMode", options, node) │ │ │ │ + } │ │ │ │ + return node │ │ │ │ + }, │ │ │ │ + featureOfInterest: function(foi) { │ │ │ │ + var node = this.createElementNSPlus("featureOfInterest"); │ │ │ │ + this.writeNode("ObjectID", foi.objectId, node); │ │ │ │ + return node │ │ │ │ + }, │ │ │ │ + ObjectID: function(options) { │ │ │ │ + return this.createElementNSPlus("ObjectID", { │ │ │ │ + value: options │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + responseFormat: function(options) { │ │ │ │ + return this.createElementNSPlus("responseFormat", { │ │ │ │ + value: options.responseFormat │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + procedure: function(procedure) { │ │ │ │ + return this.createElementNSPlus("procedure", { │ │ │ │ + value: procedure │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + offering: function(options) { │ │ │ │ + return this.createElementNSPlus("offering", { │ │ │ │ + value: options.offering │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + observedProperty: function(observedProperty) { │ │ │ │ + return this.createElementNSPlus("observedProperty", { │ │ │ │ + value: observedProperty │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + eventTime: function(options) { │ │ │ │ + var node = this.createElementNSPlus("eventTime"); │ │ │ │ + if (options.eventTime === "latest") { │ │ │ │ + this.writeNode("ogc:TM_Equals", options, node) │ │ │ │ + } │ │ │ │ + return node │ │ │ │ + }, │ │ │ │ + resultModel: function(options) { │ │ │ │ + return this.createElementNSPlus("resultModel", { │ │ │ │ + value: options.resultModel │ │ │ │ + }) │ │ │ │ + }, │ │ │ │ + responseMode: function(options) { │ │ │ │ + return this.createElementNSPlus("responseMode", { │ │ │ │ + value: options.responseMode │ │ │ │ }) │ │ │ │ } │ │ │ │ - }; │ │ │ │ - var rotationHandleMoveFn = function(x, y) { │ │ │ │ - var oldX = this.x, │ │ │ │ - oldY = this.y; │ │ │ │ - OpenLayers.Geometry.Point.prototype.move.call(this, x, y); │ │ │ │ - if (control._moving) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - var evt = control.dragControl.handlers.drag.evt; │ │ │ │ - var constrain = evt && evt.shiftKey ? 45 : 1; │ │ │ │ - var centerGeometry = control.center; │ │ │ │ - var dx1 = this.x - centerGeometry.x; │ │ │ │ - var dy1 = this.y - centerGeometry.y; │ │ │ │ - var dx0 = dx1 - x; │ │ │ │ - var dy0 = dy1 - y; │ │ │ │ - this.x = oldX; │ │ │ │ - this.y = oldY; │ │ │ │ - var a0 = Math.atan2(dy0, dx0); │ │ │ │ - var a1 = Math.atan2(dy1, dx1); │ │ │ │ - var angle = a1 - a0; │ │ │ │ - angle *= 180 / Math.PI; │ │ │ │ - control._angle = (control._angle + angle) % 360; │ │ │ │ - var diff = control.rotation % constrain; │ │ │ │ - if (Math.abs(control._angle) >= constrain || diff !== 0) { │ │ │ │ - angle = Math.round(control._angle / constrain) * constrain - diff; │ │ │ │ - control._angle = 0; │ │ │ │ - control.box.geometry.rotate(angle, centerGeometry); │ │ │ │ - control.transformFeature({ │ │ │ │ - rotation: angle │ │ │ │ + }, │ │ │ │ + ogc: { │ │ │ │ + TM_Equals: function(options) { │ │ │ │ + var node = this.createElementNSPlus("ogc:TM_Equals"); │ │ │ │ + this.writeNode("ogc:PropertyName", { │ │ │ │ + property: "urn:ogc:data:time:iso8601" │ │ │ │ + }, node); │ │ │ │ + if (options.eventTime === "latest") { │ │ │ │ + this.writeNode("gml:TimeInstant", { │ │ │ │ + value: "latest" │ │ │ │ + }, node) │ │ │ │ + } │ │ │ │ + return node │ │ │ │ + }, │ │ │ │ + PropertyName: function(options) { │ │ │ │ + return this.createElementNSPlus("ogc:PropertyName", { │ │ │ │ + value: options.property │ │ │ │ }) │ │ │ │ } │ │ │ │ - }; │ │ │ │ - var handles = new Array(8); │ │ │ │ - var rotationHandles = new Array(4); │ │ │ │ - var geom, handle, rotationHandle; │ │ │ │ - var positions = ["sw", "s", "se", "e", "ne", "n", "nw", "w"]; │ │ │ │ - for (var i = 0; i < 8; ++i) { │ │ │ │ - geom = this.box.geometry.components[i]; │ │ │ │ - handle = new OpenLayers.Feature.Vector(geom.clone(), { │ │ │ │ - role: positions[i] + "-resize" │ │ │ │ - }, typeof this.renderIntent == "string" ? null : this.renderIntent); │ │ │ │ - if (i % 2 == 0) { │ │ │ │ - rotationHandle = new OpenLayers.Feature.Vector(geom.clone(), { │ │ │ │ - role: positions[i] + "-rotate" │ │ │ │ - }, typeof this.rotationHandleSymbolizer == "string" ? null : this.rotationHandleSymbolizer); │ │ │ │ - rotationHandle.geometry.move = rotationHandleMoveFn; │ │ │ │ - geom._rotationHandle = rotationHandle; │ │ │ │ - rotationHandles[i / 2] = rotationHandle │ │ │ │ + }, │ │ │ │ + gml: { │ │ │ │ + TimeInstant: function(options) { │ │ │ │ + var node = this.createElementNSPlus("gml:TimeInstant"); │ │ │ │ + this.writeNode("gml:timePosition", options, node); │ │ │ │ + return node │ │ │ │ + }, │ │ │ │ + timePosition: function(options) { │ │ │ │ + var node = this.createElementNSPlus("gml:timePosition", { │ │ │ │ + value: options.value │ │ │ │ + }); │ │ │ │ + return node │ │ │ │ } │ │ │ │ - geom.move = vertexMoveFn; │ │ │ │ - geom.resize = vertexResizeFn; │ │ │ │ - geom.rotate = vertexRotateFn; │ │ │ │ - handle.geometry.move = handleMoveFn; │ │ │ │ - geom._handle = handle; │ │ │ │ - handles[i] = handle │ │ │ │ } │ │ │ │ - this.rotationHandles = rotationHandles; │ │ │ │ - this.handles = handles │ │ │ │ }, │ │ │ │ - createControl: function() { │ │ │ │ - var control = this; │ │ │ │ - this.dragControl = new OpenLayers.Control.DragFeature(this.layer, { │ │ │ │ - documentDrag: true, │ │ │ │ - moveFeature: function(pixel) { │ │ │ │ - if (this.feature === control.feature) { │ │ │ │ - this.feature = control.box │ │ │ │ + CLASS_NAME: "OpenLayers.Format.SOSGetObservation" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.WMSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + defaultVersion: "1.1.1", │ │ │ │ + profile: null, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSCapabilities" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.WFSDescribeFeatureType = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + regExes: { │ │ │ │ + trimSpace: /^\s*|\s*$/g │ │ │ │ + }, │ │ │ │ + namespaces: { │ │ │ │ + xsd: "http://www.w3.org/2001/XMLSchema" │ │ │ │ + }, │ │ │ │ + readers: { │ │ │ │ + xsd: { │ │ │ │ + schema: function(node, obj) { │ │ │ │ + var complexTypes = []; │ │ │ │ + var customTypes = {}; │ │ │ │ + var schema = { │ │ │ │ + complexTypes: complexTypes, │ │ │ │ + customTypes: customTypes │ │ │ │ + }; │ │ │ │ + var i, len; │ │ │ │ + this.readChildNodes(node, schema); │ │ │ │ + var attributes = node.attributes; │ │ │ │ + var attr, name; │ │ │ │ + for (i = 0, len = attributes.length; i < len; ++i) { │ │ │ │ + attr = attributes[i]; │ │ │ │ + name = attr.name; │ │ │ │ + if (name.indexOf("xmlns") === 0) { │ │ │ │ + this.setNamespace(name.split(":")[1] || "", attr.value) │ │ │ │ + } else { │ │ │ │ + obj[name] = attr.value │ │ │ │ + } │ │ │ │ + } │ │ │ │ + obj.featureTypes = complexTypes; │ │ │ │ + obj.targetPrefix = this.namespaceAlias[obj.targetNamespace]; │ │ │ │ + var complexType, customType; │ │ │ │ + for (i = 0, len = complexTypes.length; i < len; ++i) { │ │ │ │ + complexType = complexTypes[i]; │ │ │ │ + customType = customTypes[complexType.typeName]; │ │ │ │ + if (customTypes[complexType.typeName]) { │ │ │ │ + complexType.typeName = customType.name │ │ │ │ + } │ │ │ │ } │ │ │ │ - OpenLayers.Control.DragFeature.prototype.moveFeature.apply(this, arguments) │ │ │ │ }, │ │ │ │ - onDrag: function(feature, pixel) { │ │ │ │ - if (feature === control.box) { │ │ │ │ - control.transformFeature({ │ │ │ │ - center: control.center │ │ │ │ - }) │ │ │ │ + complexType: function(node, obj) { │ │ │ │ + var complexType = { │ │ │ │ + typeName: node.getAttribute("name") │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, complexType); │ │ │ │ + obj.complexTypes.push(complexType) │ │ │ │ + }, │ │ │ │ + complexContent: function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj) │ │ │ │ + }, │ │ │ │ + extension: function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj) │ │ │ │ + }, │ │ │ │ + sequence: function(node, obj) { │ │ │ │ + var sequence = { │ │ │ │ + elements: [] │ │ │ │ + }; │ │ │ │ + this.readChildNodes(node, sequence); │ │ │ │ + obj.properties = sequence.elements │ │ │ │ + }, │ │ │ │ + element: function(node, obj) { │ │ │ │ + var type; │ │ │ │ + if (obj.elements) { │ │ │ │ + var element = {}; │ │ │ │ + var attributes = node.attributes; │ │ │ │ + var attr; │ │ │ │ + for (var i = 0, len = attributes.length; i < len; ++i) { │ │ │ │ + attr = attributes[i]; │ │ │ │ + element[attr.name] = attr.value │ │ │ │ + } │ │ │ │ + type = element.type; │ │ │ │ + if (!type) { │ │ │ │ + type = {}; │ │ │ │ + this.readChildNodes(node, type); │ │ │ │ + element.restriction = type; │ │ │ │ + element.type = type.base │ │ │ │ + } │ │ │ │ + var fullType = type.base || type; │ │ │ │ + element.localType = fullType.split(":").pop(); │ │ │ │ + obj.elements.push(element); │ │ │ │ + this.readChildNodes(node, element) │ │ │ │ + } │ │ │ │ + if (obj.complexTypes) { │ │ │ │ + type = node.getAttribute("type"); │ │ │ │ + var localType = type.split(":").pop(); │ │ │ │ + obj.customTypes[localType] = { │ │ │ │ + name: node.getAttribute("name"), │ │ │ │ + type: type │ │ │ │ + } │ │ │ │ } │ │ │ │ }, │ │ │ │ - onStart: function(feature, pixel) { │ │ │ │ - var eligible = !control.geometryTypes || OpenLayers.Util.indexOf(control.geometryTypes, feature.geometry.CLASS_NAME) !== -1; │ │ │ │ - var i = OpenLayers.Util.indexOf(control.handles, feature); │ │ │ │ - i += OpenLayers.Util.indexOf(control.rotationHandles, feature); │ │ │ │ - if (feature !== control.feature && feature !== control.box && i == -2 && eligible) { │ │ │ │ - control.setFeature(feature) │ │ │ │ + annotation: function(node, obj) { │ │ │ │ + obj.annotation = {}; │ │ │ │ + this.readChildNodes(node, obj.annotation) │ │ │ │ + }, │ │ │ │ + appinfo: function(node, obj) { │ │ │ │ + if (!obj.appinfo) { │ │ │ │ + obj.appinfo = [] │ │ │ │ } │ │ │ │ + obj.appinfo.push(this.getChildValue(node)) │ │ │ │ }, │ │ │ │ - onComplete: function(feature, pixel) { │ │ │ │ - control.events.triggerEvent("transformcomplete", { │ │ │ │ - feature: control.feature │ │ │ │ + documentation: function(node, obj) { │ │ │ │ + if (!obj.documentation) { │ │ │ │ + obj.documentation = [] │ │ │ │ + } │ │ │ │ + var value = this.getChildValue(node); │ │ │ │ + obj.documentation.push({ │ │ │ │ + lang: node.getAttribute("xml:lang"), │ │ │ │ + textContent: value.replace(this.regExes.trimSpace, "") │ │ │ │ }) │ │ │ │ + }, │ │ │ │ + simpleType: function(node, obj) { │ │ │ │ + this.readChildNodes(node, obj) │ │ │ │ + }, │ │ │ │ + restriction: function(node, obj) { │ │ │ │ + obj.base = node.getAttribute("base"); │ │ │ │ + this.readRestriction(node, obj) │ │ │ │ } │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - drawHandles: function() { │ │ │ │ - var layer = this.layer; │ │ │ │ - for (var i = 0; i < 8; ++i) { │ │ │ │ - if (this.rotate && i % 2 === 0) { │ │ │ │ - layer.drawFeature(this.rotationHandles[i / 2], this.rotationHandleSymbolizer) │ │ │ │ - } │ │ │ │ - layer.drawFeature(this.handles[i], this.renderIntent) │ │ │ │ } │ │ │ │ }, │ │ │ │ - transformFeature: function(mods) { │ │ │ │ - if (!this._setfeature) { │ │ │ │ - this.scale *= mods.scale || 1; │ │ │ │ - this.ratio *= mods.ratio || 1; │ │ │ │ - var oldRotation = this.rotation; │ │ │ │ - this.rotation = (this.rotation + (mods.rotation || 0)) % 360; │ │ │ │ - if (this.events.triggerEvent("beforetransform", mods) !== false) { │ │ │ │ - var feature = this.feature; │ │ │ │ - var geom = feature.geometry; │ │ │ │ - var center = this.center; │ │ │ │ - geom.rotate(-oldRotation, center); │ │ │ │ - if (mods.scale || mods.ratio) { │ │ │ │ - geom.resize(mods.scale, center, mods.ratio) │ │ │ │ - } else if (mods.center) { │ │ │ │ - feature.move(mods.center.getBounds().getCenterLonLat()) │ │ │ │ + readRestriction: function(node, obj) { │ │ │ │ + var children = node.childNodes; │ │ │ │ + var child, nodeName, value; │ │ │ │ + for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ + child = children[i]; │ │ │ │ + if (child.nodeType == 1) { │ │ │ │ + nodeName = child.nodeName.split(":").pop(); │ │ │ │ + value = child.getAttribute("value"); │ │ │ │ + if (!obj[nodeName]) { │ │ │ │ + obj[nodeName] = value │ │ │ │ + } else { │ │ │ │ + if (typeof obj[nodeName] == "string") { │ │ │ │ + obj[nodeName] = [obj[nodeName]] │ │ │ │ + } │ │ │ │ + obj[nodeName].push(value) │ │ │ │ } │ │ │ │ - geom.rotate(this.rotation, center); │ │ │ │ - this.layer.drawFeature(feature); │ │ │ │ - feature.toState(OpenLayers.State.UPDATE); │ │ │ │ - this.events.triggerEvent("transform", mods) │ │ │ │ } │ │ │ │ } │ │ │ │ - this.layer.drawFeature(this.box, this.renderIntent); │ │ │ │ - this.drawHandles() │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - var geom; │ │ │ │ - for (var i = 0; i < 8; ++i) { │ │ │ │ - geom = this.box.geometry.components[i]; │ │ │ │ - geom._handle.destroy(); │ │ │ │ - geom._handle = null; │ │ │ │ - geom._rotationHandle && geom._rotationHandle.destroy(); │ │ │ │ - geom._rotationHandle = null │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]) │ │ │ │ } │ │ │ │ - this.center = null; │ │ │ │ - this.feature = null; │ │ │ │ - this.handles = null; │ │ │ │ - this.rotationHandleSymbolizer = null; │ │ │ │ - this.rotationHandles = null; │ │ │ │ - this.box.destroy(); │ │ │ │ - this.box = null; │ │ │ │ - this.layer = null; │ │ │ │ - this.dragControl.destroy(); │ │ │ │ - this.dragControl = null; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ + if (data && data.nodeType == 9) { │ │ │ │ + data = data.documentElement │ │ │ │ + } │ │ │ │ + var schema = {}; │ │ │ │ + if (data.nodeName.split(":").pop() === "ExceptionReport") { │ │ │ │ + var parser = new OpenLayers.Format.OGCExceptionReport; │ │ │ │ + schema.error = parser.read(data) │ │ │ │ + } else { │ │ │ │ + this.readNode(data, schema) │ │ │ │ + } │ │ │ │ + return schema │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.TransformFeature" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WFSDescribeFeatureType" │ │ │ │ }); │ │ │ │ -OpenLayers.Control.DrawFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - layer: null, │ │ │ │ - callbacks: null, │ │ │ │ - multi: false, │ │ │ │ - featureAdded: function() {}, │ │ │ │ - initialize: function(layer, handler, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.callbacks = OpenLayers.Util.extend({ │ │ │ │ - done: this.drawFeature, │ │ │ │ - modify: function(vertex, feature) { │ │ │ │ - this.layer.events.triggerEvent("sketchmodified", { │ │ │ │ - vertex: vertex, │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - create: function(vertex, feature) { │ │ │ │ - this.layer.events.triggerEvent("sketchstarted", { │ │ │ │ - vertex: vertex, │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ +OpenLayers.Format.WCSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + defaultVersion: "1.1.0", │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WCSCapabilities" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.GeoRSS = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + rssns: "http://backend.userland.com/rss2", │ │ │ │ + featureNS: "http://mapserver.gis.umn.edu/mapserver", │ │ │ │ + georssns: "http://www.georss.org/georss", │ │ │ │ + geons: "http://www.w3.org/2003/01/geo/wgs84_pos#", │ │ │ │ + featureTitle: "Untitled", │ │ │ │ + featureDescription: "No Description", │ │ │ │ + gmlParser: null, │ │ │ │ + xy: false, │ │ │ │ + createGeometryFromItem: function(item) { │ │ │ │ + var point = this.getElementsByTagNameNS(item, this.georssns, "point"); │ │ │ │ + var lat = this.getElementsByTagNameNS(item, this.geons, "lat"); │ │ │ │ + var lon = this.getElementsByTagNameNS(item, this.geons, "long"); │ │ │ │ + var line = this.getElementsByTagNameNS(item, this.georssns, "line"); │ │ │ │ + var polygon = this.getElementsByTagNameNS(item, this.georssns, "polygon"); │ │ │ │ + var where = this.getElementsByTagNameNS(item, this.georssns, "where"); │ │ │ │ + var box = this.getElementsByTagNameNS(item, this.georssns, "box"); │ │ │ │ + if (point.length > 0 || lat.length > 0 && lon.length > 0) { │ │ │ │ + var location; │ │ │ │ + if (point.length > 0) { │ │ │ │ + location = OpenLayers.String.trim(point[0].firstChild.nodeValue).split(/\s+/); │ │ │ │ + if (location.length != 2) { │ │ │ │ + location = OpenLayers.String.trim(point[0].firstChild.nodeValue).split(/\s*,\s*/) │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + location = [parseFloat(lat[0].firstChild.nodeValue), parseFloat(lon[0].firstChild.nodeValue)] │ │ │ │ } │ │ │ │ - }, this.callbacks); │ │ │ │ - this.layer = layer; │ │ │ │ - this.handlerOptions = this.handlerOptions || {}; │ │ │ │ - this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions, { │ │ │ │ - renderers: layer.renderers, │ │ │ │ - rendererOptions: layer.rendererOptions │ │ │ │ - }); │ │ │ │ - if (!("multi" in this.handlerOptions)) { │ │ │ │ - this.handlerOptions.multi = this.multi │ │ │ │ - } │ │ │ │ - var sketchStyle = this.layer.styleMap && this.layer.styleMap.styles.temporary; │ │ │ │ - if (sketchStyle) { │ │ │ │ - this.handlerOptions.layerOptions = OpenLayers.Util.applyDefaults(this.handlerOptions.layerOptions, { │ │ │ │ - styleMap: new OpenLayers.StyleMap({ │ │ │ │ - default: sketchStyle │ │ │ │ + var geometry = new OpenLayers.Geometry.Point(location[1], location[0]) │ │ │ │ + } else if (line.length > 0) { │ │ │ │ + var coords = OpenLayers.String.trim(this.getChildValue(line[0])).split(/\s+/); │ │ │ │ + var components = []; │ │ │ │ + var point; │ │ │ │ + for (var i = 0, len = coords.length; i < len; i += 2) { │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[i + 1], coords[i]); │ │ │ │ + components.push(point) │ │ │ │ + } │ │ │ │ + geometry = new OpenLayers.Geometry.LineString(components) │ │ │ │ + } else if (polygon.length > 0) { │ │ │ │ + var coords = OpenLayers.String.trim(this.getChildValue(polygon[0])).split(/\s+/); │ │ │ │ + var components = []; │ │ │ │ + var point; │ │ │ │ + for (var i = 0, len = coords.length; i < len; i += 2) { │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[i + 1], coords[i]); │ │ │ │ + components.push(point) │ │ │ │ + } │ │ │ │ + geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]) │ │ │ │ + } else if (where.length > 0) { │ │ │ │ + if (!this.gmlParser) { │ │ │ │ + this.gmlParser = new OpenLayers.Format.GML({ │ │ │ │ + xy: this.xy │ │ │ │ }) │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - this.handler = new handler(this, this.callbacks, this.handlerOptions) │ │ │ │ - }, │ │ │ │ - drawFeature: function(geometry) { │ │ │ │ - var feature = new OpenLayers.Feature.Vector(geometry); │ │ │ │ - var proceed = this.layer.events.triggerEvent("sketchcomplete", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (proceed !== false) { │ │ │ │ - feature.state = OpenLayers.State.INSERT; │ │ │ │ - this.layer.addFeatures([feature]); │ │ │ │ - this.featureAdded(feature); │ │ │ │ - this.events.triggerEvent("featureadded", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - insertXY: function(x, y) { │ │ │ │ - if (this.handler && this.handler.line) { │ │ │ │ - this.handler.insertXY(x, y) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - insertDeltaXY: function(dx, dy) { │ │ │ │ - if (this.handler && this.handler.line) { │ │ │ │ - this.handler.insertDeltaXY(dx, dy) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - insertDirectionLength: function(direction, length) { │ │ │ │ - if (this.handler && this.handler.line) { │ │ │ │ - this.handler.insertDirectionLength(direction, length) │ │ │ │ + } │ │ │ │ + var feature = this.gmlParser.parseFeature(where[0]); │ │ │ │ + geometry = feature.geometry │ │ │ │ + } else if (box.length > 0) { │ │ │ │ + var coords = OpenLayers.String.trim(box[0].firstChild.nodeValue).split(/\s+/); │ │ │ │ + var components = []; │ │ │ │ + var point; │ │ │ │ + if (coords.length > 3) { │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[1], coords[0]); │ │ │ │ + components.push(point); │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[1], coords[2]); │ │ │ │ + components.push(point); │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[3], coords[2]); │ │ │ │ + components.push(point); │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[3], coords[0]); │ │ │ │ + components.push(point); │ │ │ │ + point = new OpenLayers.Geometry.Point(coords[1], coords[0]); │ │ │ │ + components.push(point) │ │ │ │ + } │ │ │ │ + geometry = new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing(components)]) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - insertDeflectionLength: function(deflection, length) { │ │ │ │ - if (this.handler && this.handler.line) { │ │ │ │ - this.handler.insertDeflectionLength(deflection, length) │ │ │ │ + if (geometry && this.internalProjection && this.externalProjection) { │ │ │ │ + geometry.transform(this.externalProjection, this.internalProjection) │ │ │ │ } │ │ │ │ + return geometry │ │ │ │ }, │ │ │ │ - undo: function() { │ │ │ │ - return this.handler.undo && this.handler.undo() │ │ │ │ - }, │ │ │ │ - redo: function() { │ │ │ │ - return this.handler.redo && this.handler.redo() │ │ │ │ - }, │ │ │ │ - finishSketch: function() { │ │ │ │ - this.handler.finishGeometry() │ │ │ │ - }, │ │ │ │ - cancel: function() { │ │ │ │ - this.handler.cancel() │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.DrawFeature" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.ZoomOut = OpenLayers.Class(OpenLayers.Control.Button, { │ │ │ │ - trigger: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.zoomOut() │ │ │ │ + createFeatureFromItem: function(item) { │ │ │ │ + var geometry = this.createGeometryFromItem(item); │ │ │ │ + var title = this._getChildValue(item, "*", "title", this.featureTitle); │ │ │ │ + var description = this._getChildValue(item, "*", "description", this._getChildValue(item, "*", "content", this._getChildValue(item, "*", "summary", this.featureDescription))); │ │ │ │ + var link = this._getChildValue(item, "*", "link"); │ │ │ │ + if (!link) { │ │ │ │ + try { │ │ │ │ + link = this.getElementsByTagNameNS(item, "*", "link")[0].getAttribute("href") │ │ │ │ + } catch (e) { │ │ │ │ + link = null │ │ │ │ + } │ │ │ │ } │ │ │ │ + var id = this._getChildValue(item, "*", "id", null); │ │ │ │ + var data = { │ │ │ │ + title: title, │ │ │ │ + description: description, │ │ │ │ + link: link │ │ │ │ + }; │ │ │ │ + var feature = new OpenLayers.Feature.Vector(geometry, data); │ │ │ │ + feature.fid = id; │ │ │ │ + return feature │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ZoomOut" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.UTFGrid = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - autoActivate: true, │ │ │ │ - layers: null, │ │ │ │ - defaultHandlerOptions: { │ │ │ │ - delay: 300, │ │ │ │ - pixelTolerance: 4, │ │ │ │ - stopMove: false, │ │ │ │ - single: true, │ │ │ │ - double: false, │ │ │ │ - stopSingle: false, │ │ │ │ - stopDouble: false │ │ │ │ - }, │ │ │ │ - handlerMode: "click", │ │ │ │ - setHandler: function(hm) { │ │ │ │ - this.handlerMode = hm; │ │ │ │ - this.resetHandler() │ │ │ │ - }, │ │ │ │ - resetHandler: function() { │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.deactivate(); │ │ │ │ - this.handler.destroy(); │ │ │ │ - this.handler = null │ │ │ │ - } │ │ │ │ - if (this.handlerMode == "hover") { │ │ │ │ - this.handler = new OpenLayers.Handler.Hover(this, { │ │ │ │ - pause: this.handleEvent, │ │ │ │ - move: this.reset │ │ │ │ - }, this.handlerOptions) │ │ │ │ - } else if (this.handlerMode == "click") { │ │ │ │ - this.handler = new OpenLayers.Handler.Click(this, { │ │ │ │ - click: this.handleEvent │ │ │ │ - }, this.handlerOptions) │ │ │ │ - } else if (this.handlerMode == "move") { │ │ │ │ - this.handler = new OpenLayers.Handler.Hover(this, { │ │ │ │ - pause: this.handleEvent, │ │ │ │ - move: this.handleEvent │ │ │ │ - }, this.handlerOptions) │ │ │ │ - } │ │ │ │ - if (this.handler) { │ │ │ │ - return true │ │ │ │ + _getChildValue: function(node, nsuri, name, def) { │ │ │ │ + var value; │ │ │ │ + var eles = this.getElementsByTagNameNS(node, nsuri, name); │ │ │ │ + if (eles && eles[0] && eles[0].firstChild && eles[0].firstChild.nodeValue) { │ │ │ │ + value = this.getChildValue(eles[0]) │ │ │ │ } else { │ │ │ │ - return false │ │ │ │ + value = def == undefined ? "" : def │ │ │ │ } │ │ │ │ + return value │ │ │ │ }, │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - options.handlerOptions = options.handlerOptions || this.defaultHandlerOptions; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.resetHandler() │ │ │ │ - }, │ │ │ │ - handleEvent: function(evt) { │ │ │ │ - if (evt == null) { │ │ │ │ - this.reset(); │ │ │ │ - return │ │ │ │ + read: function(doc) { │ │ │ │ + if (typeof doc == "string") { │ │ │ │ + doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]) │ │ │ │ } │ │ │ │ - var lonLat = this.map.getLonLatFromPixel(evt.xy); │ │ │ │ - if (!lonLat) { │ │ │ │ - return │ │ │ │ + var itemlist = null; │ │ │ │ + itemlist = this.getElementsByTagNameNS(doc, "*", "item"); │ │ │ │ + if (itemlist.length == 0) { │ │ │ │ + itemlist = this.getElementsByTagNameNS(doc, "*", "entry") │ │ │ │ } │ │ │ │ - var layers = this.findLayers(); │ │ │ │ - if (layers.length > 0) { │ │ │ │ - var infoLookup = {}; │ │ │ │ - var layer, idx; │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - layer = layers[i]; │ │ │ │ - idx = OpenLayers.Util.indexOf(this.map.layers, layer); │ │ │ │ - infoLookup[idx] = layer.getFeatureInfo(lonLat) │ │ │ │ - } │ │ │ │ - this.callback(infoLookup, lonLat, evt.xy) │ │ │ │ + var numItems = itemlist.length; │ │ │ │ + var features = new Array(numItems); │ │ │ │ + for (var i = 0; i < numItems; i++) { │ │ │ │ + features[i] = this.createFeatureFromItem(itemlist[i]) │ │ │ │ } │ │ │ │ + return features │ │ │ │ }, │ │ │ │ - callback: function(infoLookup) {}, │ │ │ │ - reset: function(evt) { │ │ │ │ - this.callback(null) │ │ │ │ - }, │ │ │ │ - findLayers: function() { │ │ │ │ - var candidates = this.layers || this.map.layers; │ │ │ │ - var layers = []; │ │ │ │ - var layer; │ │ │ │ - for (var i = candidates.length - 1; i >= 0; --i) { │ │ │ │ - layer = candidates[i]; │ │ │ │ - if (layer instanceof OpenLayers.Layer.UTFGrid) { │ │ │ │ - layers.push(layer) │ │ │ │ + write: function(features) { │ │ │ │ + var georss; │ │ │ │ + if (OpenLayers.Util.isArray(features)) { │ │ │ │ + georss = this.createElementNS(this.rssns, "rss"); │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ + georss.appendChild(this.createFeatureXML(features[i])) │ │ │ │ } │ │ │ │ + } else { │ │ │ │ + georss = this.createFeatureXML(features) │ │ │ │ } │ │ │ │ - return layers │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [georss]) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.UTFGrid" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.EditingToolbar = OpenLayers.Class(OpenLayers.Control.Panel, { │ │ │ │ - citeCompliant: false, │ │ │ │ - initialize: function(layer, options) { │ │ │ │ - OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); │ │ │ │ - this.addControls([new OpenLayers.Control.Navigation]); │ │ │ │ - var controls = [new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Point, { │ │ │ │ - displayClass: "olControlDrawFeaturePoint", │ │ │ │ - handlerOptions: { │ │ │ │ - citeCompliant: this.citeCompliant │ │ │ │ - } │ │ │ │ - }), new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Path, { │ │ │ │ - displayClass: "olControlDrawFeaturePath", │ │ │ │ - handlerOptions: { │ │ │ │ - citeCompliant: this.citeCompliant │ │ │ │ + createFeatureXML: function(feature) { │ │ │ │ + var geometryNode = this.buildGeometryNode(feature.geometry); │ │ │ │ + var featureNode = this.createElementNS(this.rssns, "item"); │ │ │ │ + var titleNode = this.createElementNS(this.rssns, "title"); │ │ │ │ + titleNode.appendChild(this.createTextNode(feature.attributes.title ? feature.attributes.title : "")); │ │ │ │ + var descNode = this.createElementNS(this.rssns, "description"); │ │ │ │ + descNode.appendChild(this.createTextNode(feature.attributes.description ? feature.attributes.description : "")); │ │ │ │ + featureNode.appendChild(titleNode); │ │ │ │ + featureNode.appendChild(descNode); │ │ │ │ + if (feature.attributes.link) { │ │ │ │ + var linkNode = this.createElementNS(this.rssns, "link"); │ │ │ │ + linkNode.appendChild(this.createTextNode(feature.attributes.link)); │ │ │ │ + featureNode.appendChild(linkNode) │ │ │ │ + } │ │ │ │ + for (var attr in feature.attributes) { │ │ │ │ + if (attr == "link" || attr == "title" || attr == "description") { │ │ │ │ + continue │ │ │ │ } │ │ │ │ - }), new OpenLayers.Control.DrawFeature(layer, OpenLayers.Handler.Polygon, { │ │ │ │ - displayClass: "olControlDrawFeaturePolygon", │ │ │ │ - handlerOptions: { │ │ │ │ - citeCompliant: this.citeCompliant │ │ │ │ + var attrText = this.createTextNode(feature.attributes[attr]); │ │ │ │ + var nodename = attr; │ │ │ │ + if (attr.search(":") != -1) { │ │ │ │ + nodename = attr.split(":")[1] │ │ │ │ } │ │ │ │ - })]; │ │ │ │ - this.addControls(controls) │ │ │ │ - }, │ │ │ │ - draw: function() { │ │ │ │ - var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments); │ │ │ │ - if (this.defaultControl === null) { │ │ │ │ - this.defaultControl = this.controls[0] │ │ │ │ - } │ │ │ │ - return div │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.EditingToolbar" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Zoom = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - zoomInText: "+", │ │ │ │ - zoomInId: "olZoomInLink", │ │ │ │ - zoomOutText: "−", │ │ │ │ - zoomOutId: "olZoomOutLink", │ │ │ │ - draw: function() { │ │ │ │ - var div = OpenLayers.Control.prototype.draw.apply(this), │ │ │ │ - links = this.getOrCreateLinks(div), │ │ │ │ - zoomIn = links.zoomIn, │ │ │ │ - zoomOut = links.zoomOut, │ │ │ │ - eventsInstance = this.map.events; │ │ │ │ - if (zoomOut.parentNode !== div) { │ │ │ │ - eventsInstance = this.events; │ │ │ │ - eventsInstance.attachToElement(zoomOut.parentNode) │ │ │ │ + var attrContainer = this.createElementNS(this.featureNS, "feature:" + nodename); │ │ │ │ + attrContainer.appendChild(attrText); │ │ │ │ + featureNode.appendChild(attrContainer) │ │ │ │ } │ │ │ │ - eventsInstance.register("buttonclick", this, this.onZoomClick); │ │ │ │ - this.zoomInLink = zoomIn; │ │ │ │ - this.zoomOutLink = zoomOut; │ │ │ │ - return div │ │ │ │ + featureNode.appendChild(geometryNode); │ │ │ │ + return featureNode │ │ │ │ }, │ │ │ │ - getOrCreateLinks: function(el) { │ │ │ │ - var zoomIn = document.getElementById(this.zoomInId), │ │ │ │ - zoomOut = document.getElementById(this.zoomOutId); │ │ │ │ - if (!zoomIn) { │ │ │ │ - zoomIn = document.createElement("a"); │ │ │ │ - zoomIn.href = "#zoomIn"; │ │ │ │ - zoomIn.appendChild(document.createTextNode(this.zoomInText)); │ │ │ │ - zoomIn.className = "olControlZoomIn"; │ │ │ │ - el.appendChild(zoomIn) │ │ │ │ - } │ │ │ │ - OpenLayers.Element.addClass(zoomIn, "olButton"); │ │ │ │ - if (!zoomOut) { │ │ │ │ - zoomOut = document.createElement("a"); │ │ │ │ - zoomOut.href = "#zoomOut"; │ │ │ │ - zoomOut.appendChild(document.createTextNode(this.zoomOutText)); │ │ │ │ - zoomOut.className = "olControlZoomOut"; │ │ │ │ - el.appendChild(zoomOut) │ │ │ │ - } │ │ │ │ - OpenLayers.Element.addClass(zoomOut, "olButton"); │ │ │ │ - return { │ │ │ │ - zoomIn: zoomIn, │ │ │ │ - zoomOut: zoomOut │ │ │ │ + buildGeometryNode: function(geometry) { │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry = geometry.clone(); │ │ │ │ + geometry.transform(this.internalProjection, this.externalProjection) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - onZoomClick: function(evt) { │ │ │ │ - var button = evt.buttonElement; │ │ │ │ - if (button === this.zoomInLink) { │ │ │ │ - this.map.zoomIn() │ │ │ │ - } else if (button === this.zoomOutLink) { │ │ │ │ - this.map.zoomOut() │ │ │ │ + var node; │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") { │ │ │ │ + node = this.createElementNS(this.georssns, "georss:polygon"); │ │ │ │ + node.appendChild(this.buildCoordinatesNode(geometry.components[0])) │ │ │ │ + } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") { │ │ │ │ + node = this.createElementNS(this.georssns, "georss:line"); │ │ │ │ + node.appendChild(this.buildCoordinatesNode(geometry)) │ │ │ │ + } else if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ + node = this.createElementNS(this.georssns, "georss:point"); │ │ │ │ + node.appendChild(this.buildCoordinatesNode(geometry)) │ │ │ │ + } else { │ │ │ │ + throw "Couldn't parse " + geometry.CLASS_NAME │ │ │ │ } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.unregister("buttonclick", this, this.onZoomClick) │ │ │ │ + buildCoordinatesNode: function(geometry) { │ │ │ │ + var points = null; │ │ │ │ + if (geometry.components) { │ │ │ │ + points = geometry.components │ │ │ │ } │ │ │ │ - delete this.zoomInLink; │ │ │ │ - delete this.zoomOutLink; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this) │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Zoom" │ │ │ │ -}); │ │ │ │ -OpenLayers.Handler.Keyboard = OpenLayers.Class(OpenLayers.Handler, { │ │ │ │ - KEY_EVENTS: ["keydown", "keyup"], │ │ │ │ - eventListener: null, │ │ │ │ - observeElement: null, │ │ │ │ - initialize: function(control, callbacks, options) { │ │ │ │ - OpenLayers.Handler.prototype.initialize.apply(this, arguments); │ │ │ │ - this.eventListener = OpenLayers.Function.bindAsEventListener(this.handleKeyEvent, this) │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - this.eventListener = null; │ │ │ │ - OpenLayers.Handler.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Handler.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.observeElement = this.observeElement || document; │ │ │ │ - for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) { │ │ │ │ - OpenLayers.Event.observe(this.observeElement, this.KEY_EVENTS[i], this.eventListener) │ │ │ │ + var path; │ │ │ │ + if (points) { │ │ │ │ + var numPoints = points.length; │ │ │ │ + var parts = new Array(numPoints); │ │ │ │ + for (var i = 0; i < numPoints; i++) { │ │ │ │ + parts[i] = points[i].y + " " + points[i].x │ │ │ │ } │ │ │ │ - return true │ │ │ │ + path = parts.join(" ") │ │ │ │ } else { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - for (var i = 0, len = this.KEY_EVENTS.length; i < len; i++) { │ │ │ │ - OpenLayers.Event.stopObserving(this.observeElement, this.KEY_EVENTS[i], this.eventListener) │ │ │ │ - } │ │ │ │ - deactivated = true │ │ │ │ - } │ │ │ │ - return deactivated │ │ │ │ - }, │ │ │ │ - handleKeyEvent: function(evt) { │ │ │ │ - if (this.checkModifiers(evt)) { │ │ │ │ - this.callback(evt.type, [evt]) │ │ │ │ + path = geometry.y + " " + geometry.x │ │ │ │ } │ │ │ │ + return this.createTextNode(path) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Handler.Keyboard" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.GeoRSS" │ │ │ │ }); │ │ │ │ -OpenLayers.Control.ModifyFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - documentDrag: false, │ │ │ │ - geometryTypes: null, │ │ │ │ - clickout: true, │ │ │ │ - toggle: true, │ │ │ │ - standalone: false, │ │ │ │ - layer: null, │ │ │ │ - feature: null, │ │ │ │ - vertex: null, │ │ │ │ - vertices: null, │ │ │ │ - virtualVertices: null, │ │ │ │ - handlers: null, │ │ │ │ - deleteCodes: null, │ │ │ │ - virtualStyle: null, │ │ │ │ - vertexRenderIntent: null, │ │ │ │ - mode: null, │ │ │ │ - createVertices: true, │ │ │ │ - modified: false, │ │ │ │ - radiusHandle: null, │ │ │ │ - dragHandle: null, │ │ │ │ - onModificationStart: function() {}, │ │ │ │ - onModification: function() {}, │ │ │ │ - onModificationEnd: function() {}, │ │ │ │ - initialize: function(layer, options) { │ │ │ │ - options = options || {}; │ │ │ │ - this.layer = layer; │ │ │ │ - this.vertices = []; │ │ │ │ - this.virtualVertices = []; │ │ │ │ - this.virtualStyle = OpenLayers.Util.extend({}, this.layer.style || this.layer.styleMap.createSymbolizer(null, options.vertexRenderIntent)); │ │ │ │ - this.virtualStyle.fillOpacity = .3; │ │ │ │ - this.virtualStyle.strokeOpacity = .3; │ │ │ │ - this.deleteCodes = [46, 68]; │ │ │ │ - this.mode = OpenLayers.Control.ModifyFeature.RESHAPE; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - if (!OpenLayers.Util.isArray(this.deleteCodes)) { │ │ │ │ - this.deleteCodes = [this.deleteCodes] │ │ │ │ - } │ │ │ │ - var dragCallbacks = { │ │ │ │ - down: function(pixel) { │ │ │ │ - this.vertex = null; │ │ │ │ - var feature = this.layer.getFeatureFromEvent(this.handlers.drag.evt); │ │ │ │ - if (feature) { │ │ │ │ - this.dragStart(feature) │ │ │ │ - } else if (this.clickout) { │ │ │ │ - this._unselect = this.feature │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - move: function(pixel) { │ │ │ │ - delete this._unselect; │ │ │ │ - if (this.vertex) { │ │ │ │ - this.dragVertex(this.vertex, pixel) │ │ │ │ - } │ │ │ │ - }, │ │ │ │ - up: function() { │ │ │ │ - this.handlers.drag.stopDown = false; │ │ │ │ - if (this._unselect) { │ │ │ │ - this.unselectFeature(this._unselect); │ │ │ │ - delete this._unselect │ │ │ │ +OpenLayers.Format.XLS = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + defaultVersion: "1.1.0", │ │ │ │ + stringifyOutput: true, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.XLS" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.CQL = function() { │ │ │ │ + var tokens = ["PROPERTY", "COMPARISON", "VALUE", "LOGICAL"], │ │ │ │ + patterns = { │ │ │ │ + PROPERTY: /^[_a-zA-Z]\w*/, │ │ │ │ + COMPARISON: /^(=|<>|<=|<|>=|>|LIKE)/i, │ │ │ │ + IS_NULL: /^IS NULL/i, │ │ │ │ + COMMA: /^,/, │ │ │ │ + LOGICAL: /^(AND|OR)/i, │ │ │ │ + VALUE: /^('([^']|'')*'|\d+(\.\d*)?|\.\d+)/, │ │ │ │ + LPAREN: /^\(/, │ │ │ │ + RPAREN: /^\)/, │ │ │ │ + SPATIAL: /^(BBOX|INTERSECTS|DWITHIN|WITHIN|CONTAINS)/i, │ │ │ │ + NOT: /^NOT/i, │ │ │ │ + BETWEEN: /^BETWEEN/i, │ │ │ │ + GEOMETRY: function(text) { │ │ │ │ + var type = /^(POINT|LINESTRING|POLYGON|MULTIPOINT|MULTILINESTRING|MULTIPOLYGON|GEOMETRYCOLLECTION)/.exec(text); │ │ │ │ + if (type) { │ │ │ │ + var len = text.length; │ │ │ │ + var idx = text.indexOf("(", type[0].length); │ │ │ │ + if (idx > -1) { │ │ │ │ + var depth = 1; │ │ │ │ + while (idx < len && depth > 0) { │ │ │ │ + idx++; │ │ │ │ + switch (text.charAt(idx)) { │ │ │ │ + case "(": │ │ │ │ + depth++; │ │ │ │ + break; │ │ │ │ + case ")": │ │ │ │ + depth--; │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + } │ │ │ │ + } │ │ │ │ + } │ │ │ │ + return [text.substr(0, idx + 1)] │ │ │ │ } │ │ │ │ }, │ │ │ │ - done: function(pixel) { │ │ │ │ - if (this.vertex) { │ │ │ │ - this.dragComplete(this.vertex) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - }; │ │ │ │ - var dragOptions = { │ │ │ │ - documentDrag: this.documentDrag, │ │ │ │ - stopDown: false │ │ │ │ - }; │ │ │ │ - var keyboardOptions = { │ │ │ │ - keydown: this.handleKeypress │ │ │ │ + END: /^$/ │ │ │ │ + }, │ │ │ │ + follows = { │ │ │ │ + LPAREN: ["GEOMETRY", "SPATIAL", "PROPERTY", "VALUE", "LPAREN"], │ │ │ │ + RPAREN: ["NOT", "LOGICAL", "END", "RPAREN"], │ │ │ │ + PROPERTY: ["COMPARISON", "BETWEEN", "COMMA", "IS_NULL"], │ │ │ │ + BETWEEN: ["VALUE"], │ │ │ │ + IS_NULL: ["END"], │ │ │ │ + COMPARISON: ["VALUE"], │ │ │ │ + COMMA: ["GEOMETRY", "VALUE", "PROPERTY"], │ │ │ │ + VALUE: ["LOGICAL", "COMMA", "RPAREN", "END"], │ │ │ │ + SPATIAL: ["LPAREN"], │ │ │ │ + LOGICAL: ["NOT", "VALUE", "SPATIAL", "PROPERTY", "LPAREN"], │ │ │ │ + NOT: ["PROPERTY", "LPAREN"], │ │ │ │ + GEOMETRY: ["COMMA", "RPAREN"] │ │ │ │ + }, │ │ │ │ + operators = { │ │ │ │ + "=": OpenLayers.Filter.Comparison.EQUAL_TO, │ │ │ │ + "<>": OpenLayers.Filter.Comparison.NOT_EQUAL_TO, │ │ │ │ + "<": OpenLayers.Filter.Comparison.LESS_THAN, │ │ │ │ + "<=": OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO, │ │ │ │ + ">": OpenLayers.Filter.Comparison.GREATER_THAN, │ │ │ │ + ">=": OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO, │ │ │ │ + LIKE: OpenLayers.Filter.Comparison.LIKE, │ │ │ │ + BETWEEN: OpenLayers.Filter.Comparison.BETWEEN, │ │ │ │ + "IS NULL": OpenLayers.Filter.Comparison.IS_NULL │ │ │ │ + }, │ │ │ │ + operatorReverse = {}, │ │ │ │ + logicals = { │ │ │ │ + AND: OpenLayers.Filter.Logical.AND, │ │ │ │ + OR: OpenLayers.Filter.Logical.OR │ │ │ │ + }, │ │ │ │ + logicalReverse = {}, │ │ │ │ + precedence = { │ │ │ │ + RPAREN: 3, │ │ │ │ + LOGICAL: 2, │ │ │ │ + COMPARISON: 1 │ │ │ │ }; │ │ │ │ - this.handlers = { │ │ │ │ - keyboard: new OpenLayers.Handler.Keyboard(this, keyboardOptions), │ │ │ │ - drag: new OpenLayers.Handler.Drag(this, dragCallbacks, dragOptions) │ │ │ │ + var i; │ │ │ │ + for (i in operators) { │ │ │ │ + if (operators.hasOwnProperty(i)) { │ │ │ │ + operatorReverse[operators[i]] = i │ │ │ │ } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.un({ │ │ │ │ - removelayer: this.handleMapEvents, │ │ │ │ - changelayer: this.handleMapEvents, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ + } │ │ │ │ + for (i in logicals) { │ │ │ │ + if (logicals.hasOwnProperty(i)) { │ │ │ │ + logicalReverse[logicals[i]] = i │ │ │ │ } │ │ │ │ - this.layer = null; │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, []) │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - this.moveLayerToTop(); │ │ │ │ - this.map.events.on({ │ │ │ │ - removelayer: this.handleMapEvents, │ │ │ │ - changelayer: this.handleMapEvents, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - return this.handlers.keyboard.activate() && this.handlers.drag.activate() && OpenLayers.Control.prototype.activate.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = false; │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.moveLayerBack(); │ │ │ │ - this.map.events.un({ │ │ │ │ - removelayer: this.handleMapEvents, │ │ │ │ - changelayer: this.handleMapEvents, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.layer.removeFeatures(this.vertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.layer.removeFeatures(this.virtualVertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.vertices = []; │ │ │ │ - this.handlers.drag.deactivate(); │ │ │ │ - this.handlers.keyboard.deactivate(); │ │ │ │ - var feature = this.feature; │ │ │ │ - if (feature && feature.geometry && feature.layer) { │ │ │ │ - this.unselectFeature(feature) │ │ │ │ + } │ │ │ │ + │ │ │ │ + function tryToken(text, pattern) { │ │ │ │ + if (pattern instanceof RegExp) { │ │ │ │ + return pattern.exec(text) │ │ │ │ + } else { │ │ │ │ + return pattern(text) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + │ │ │ │ + function nextToken(text, tokens) { │ │ │ │ + var i, token, len = tokens.length; │ │ │ │ + for (i = 0; i < len; i++) { │ │ │ │ + token = tokens[i]; │ │ │ │ + var pat = patterns[token]; │ │ │ │ + var matches = tryToken(text, pat); │ │ │ │ + if (matches) { │ │ │ │ + var match = matches[0]; │ │ │ │ + var remainder = text.substr(match.length).replace(/^\s*/, ""); │ │ │ │ + return { │ │ │ │ + type: token, │ │ │ │ + text: match, │ │ │ │ + remainder: remainder │ │ │ │ + } │ │ │ │ } │ │ │ │ - deactivated = true │ │ │ │ } │ │ │ │ - return deactivated │ │ │ │ - }, │ │ │ │ - beforeSelectFeature: function(feature) { │ │ │ │ - return this.layer.events.triggerEvent("beforefeaturemodified", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - selectFeature: function(feature) { │ │ │ │ - if (this.feature === feature || this.geometryTypes && OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) == -1) { │ │ │ │ - return │ │ │ │ + var msg = "ERROR: In parsing: [" + text + "], expected one of: "; │ │ │ │ + for (i = 0; i < len; i++) { │ │ │ │ + token = tokens[i]; │ │ │ │ + msg += "\n " + token + ": " + patterns[token] │ │ │ │ } │ │ │ │ - if (this.beforeSelectFeature(feature) !== false) { │ │ │ │ - if (this.feature) { │ │ │ │ - this.unselectFeature(this.feature) │ │ │ │ + throw new Error(msg) │ │ │ │ + } │ │ │ │ + │ │ │ │ + function tokenize(text) { │ │ │ │ + var results = []; │ │ │ │ + var token, expect = ["NOT", "GEOMETRY", "SPATIAL", "PROPERTY", "LPAREN"]; │ │ │ │ + do { │ │ │ │ + token = nextToken(text, expect); │ │ │ │ + text = token.remainder; │ │ │ │ + expect = follows[token.type]; │ │ │ │ + if (token.type != "END" && !expect) { │ │ │ │ + throw new Error("No follows list for " + token.type) │ │ │ │ + } │ │ │ │ + results.push(token) │ │ │ │ + } while (token.type != "END"); │ │ │ │ + return results │ │ │ │ + } │ │ │ │ + │ │ │ │ + function buildAst(tokens) { │ │ │ │ + var operatorStack = [], │ │ │ │ + postfix = []; │ │ │ │ + while (tokens.length) { │ │ │ │ + var tok = tokens.shift(); │ │ │ │ + switch (tok.type) { │ │ │ │ + case "PROPERTY": │ │ │ │ + case "GEOMETRY": │ │ │ │ + case "VALUE": │ │ │ │ + postfix.push(tok); │ │ │ │ + break; │ │ │ │ + case "COMPARISON": │ │ │ │ + case "BETWEEN": │ │ │ │ + case "IS_NULL": │ │ │ │ + case "LOGICAL": │ │ │ │ + var p = precedence[tok.type]; │ │ │ │ + while (operatorStack.length > 0 && precedence[operatorStack[operatorStack.length - 1].type] <= p) { │ │ │ │ + postfix.push(operatorStack.pop()) │ │ │ │ + } │ │ │ │ + operatorStack.push(tok); │ │ │ │ + break; │ │ │ │ + case "SPATIAL": │ │ │ │ + case "NOT": │ │ │ │ + case "LPAREN": │ │ │ │ + operatorStack.push(tok); │ │ │ │ + break; │ │ │ │ + case "RPAREN": │ │ │ │ + while (operatorStack.length > 0 && operatorStack[operatorStack.length - 1].type != "LPAREN") { │ │ │ │ + postfix.push(operatorStack.pop()) │ │ │ │ + } │ │ │ │ + operatorStack.pop(); │ │ │ │ + if (operatorStack.length > 0 && operatorStack[operatorStack.length - 1].type == "SPATIAL") { │ │ │ │ + postfix.push(operatorStack.pop()) │ │ │ │ + } │ │ │ │ + case "COMMA": │ │ │ │ + case "END": │ │ │ │ + break; │ │ │ │ + default: │ │ │ │ + throw new Error("Unknown token type " + tok.type) │ │ │ │ } │ │ │ │ - this.feature = feature; │ │ │ │ - this.layer.selectedFeatures.push(feature); │ │ │ │ - this.layer.drawFeature(feature, "select"); │ │ │ │ - this.modified = false; │ │ │ │ - this.resetVertices(); │ │ │ │ - this.onModificationStart(this.feature) │ │ │ │ - } │ │ │ │ - var modified = feature.modified; │ │ │ │ - if (feature.geometry && !(modified && modified.geometry)) { │ │ │ │ - this._originalGeometry = feature.geometry.clone() │ │ │ │ } │ │ │ │ - }, │ │ │ │ - unselectFeature: function(feature) { │ │ │ │ - this.layer.removeFeatures(this.vertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.vertices = []; │ │ │ │ - this.layer.destroyFeatures(this.virtualVertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.virtualVertices = []; │ │ │ │ - if (this.dragHandle) { │ │ │ │ - this.layer.destroyFeatures([this.dragHandle], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - delete this.dragHandle │ │ │ │ + while (operatorStack.length > 0) { │ │ │ │ + postfix.push(operatorStack.pop()) │ │ │ │ } │ │ │ │ - if (this.radiusHandle) { │ │ │ │ - this.layer.destroyFeatures([this.radiusHandle], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - delete this.radiusHandle │ │ │ │ - } │ │ │ │ - this.layer.drawFeature(this.feature, "default"); │ │ │ │ - this.feature = null; │ │ │ │ - OpenLayers.Util.removeItem(this.layer.selectedFeatures, feature); │ │ │ │ - this.onModificationEnd(feature); │ │ │ │ - this.layer.events.triggerEvent("afterfeaturemodified", { │ │ │ │ - feature: feature, │ │ │ │ - modified: this.modified │ │ │ │ - }); │ │ │ │ - this.modified = false │ │ │ │ - }, │ │ │ │ - dragStart: function(feature) { │ │ │ │ - var isPoint = feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point"; │ │ │ │ - if (!this.standalone && (!feature._sketch && isPoint || !feature._sketch)) { │ │ │ │ - if (this.toggle && this.feature === feature) { │ │ │ │ - this._unselect = feature │ │ │ │ + │ │ │ │ + function buildTree() { │ │ │ │ + var tok = postfix.pop(); │ │ │ │ + switch (tok.type) { │ │ │ │ + case "LOGICAL": │ │ │ │ + var rhs = buildTree(), │ │ │ │ + lhs = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Logical({ │ │ │ │ + filters: [lhs, rhs], │ │ │ │ + type: logicals[tok.text.toUpperCase()] │ │ │ │ + }); │ │ │ │ + case "NOT": │ │ │ │ + var operand = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Logical({ │ │ │ │ + filters: [operand], │ │ │ │ + type: OpenLayers.Filter.Logical.NOT │ │ │ │ + }); │ │ │ │ + case "BETWEEN": │ │ │ │ + var min, max, property; │ │ │ │ + postfix.pop(); │ │ │ │ + max = buildTree(); │ │ │ │ + min = buildTree(); │ │ │ │ + property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Comparison({ │ │ │ │ + property: property, │ │ │ │ + lowerBoundary: min, │ │ │ │ + upperBoundary: max, │ │ │ │ + type: OpenLayers.Filter.Comparison.BETWEEN │ │ │ │ + }); │ │ │ │ + case "COMPARISON": │ │ │ │ + var value = buildTree(), │ │ │ │ + property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Comparison({ │ │ │ │ + property: property, │ │ │ │ + value: value, │ │ │ │ + type: operators[tok.text.toUpperCase()] │ │ │ │ + }); │ │ │ │ + case "IS_NULL": │ │ │ │ + var property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Comparison({ │ │ │ │ + property: property, │ │ │ │ + type: operators[tok.text.toUpperCase()] │ │ │ │ + }); │ │ │ │ + case "VALUE": │ │ │ │ + var match = tok.text.match(/^'(.*)'$/); │ │ │ │ + if (match) { │ │ │ │ + return match[1].replace(/''/g, "'") │ │ │ │ + } else { │ │ │ │ + return Number(tok.text) │ │ │ │ + } │ │ │ │ + case "SPATIAL": │ │ │ │ + switch (tok.text.toUpperCase()) { │ │ │ │ + case "BBOX": │ │ │ │ + var maxy = buildTree(), │ │ │ │ + maxx = buildTree(), │ │ │ │ + miny = buildTree(), │ │ │ │ + minx = buildTree(), │ │ │ │ + prop = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ + property: prop, │ │ │ │ + value: OpenLayers.Bounds.fromArray([minx, miny, maxx, maxy]) │ │ │ │ + }); │ │ │ │ + case "INTERSECTS": │ │ │ │ + var value = buildTree(), │ │ │ │ + property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.INTERSECTS, │ │ │ │ + property: property, │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + case "WITHIN": │ │ │ │ + var value = buildTree(), │ │ │ │ + property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.WITHIN, │ │ │ │ + property: property, │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + case "CONTAINS": │ │ │ │ + var value = buildTree(), │ │ │ │ + property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.CONTAINS, │ │ │ │ + property: property, │ │ │ │ + value: value │ │ │ │ + }); │ │ │ │ + case "DWITHIN": │ │ │ │ + var distance = buildTree(), │ │ │ │ + value = buildTree(), │ │ │ │ + property = buildTree(); │ │ │ │ + return new OpenLayers.Filter.Spatial({ │ │ │ │ + type: OpenLayers.Filter.Spatial.DWITHIN, │ │ │ │ + value: value, │ │ │ │ + property: property, │ │ │ │ + distance: Number(distance) │ │ │ │ + }) │ │ │ │ + } │ │ │ │ + case "GEOMETRY": │ │ │ │ + return OpenLayers.Geometry.fromWKT(tok.text); │ │ │ │ + default: │ │ │ │ + return tok.text │ │ │ │ } │ │ │ │ - this.selectFeature(feature) │ │ │ │ } │ │ │ │ - if (feature._sketch || isPoint) { │ │ │ │ - this.vertex = feature; │ │ │ │ - this.handlers.drag.stopDown = true │ │ │ │ + var result = buildTree(); │ │ │ │ + if (postfix.length > 0) { │ │ │ │ + var msg = "Remaining tokens after building AST: \n"; │ │ │ │ + for (var i = postfix.length - 1; i >= 0; i--) { │ │ │ │ + msg += postfix[i].type + ": " + postfix[i].text + "\n" │ │ │ │ + } │ │ │ │ + throw new Error(msg) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - dragVertex: function(vertex, pixel) { │ │ │ │ - var pos = this.map.getLonLatFromViewPortPx(pixel); │ │ │ │ - var geom = vertex.geometry; │ │ │ │ - geom.move(pos.lon - geom.x, pos.lat - geom.y); │ │ │ │ - this.modified = true; │ │ │ │ - if (this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - this.layer.events.triggerEvent("vertexmodified", { │ │ │ │ - vertex: vertex.geometry, │ │ │ │ - feature: this.feature, │ │ │ │ - pixel: pixel │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - if (vertex._index) { │ │ │ │ - vertex.geometry.parent.addComponent(vertex.geometry, vertex._index); │ │ │ │ - delete vertex._index; │ │ │ │ - OpenLayers.Util.removeItem(this.virtualVertices, vertex); │ │ │ │ - this.vertices.push(vertex) │ │ │ │ - } else if (vertex == this.dragHandle) { │ │ │ │ - this.layer.removeFeatures(this.vertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.vertices = []; │ │ │ │ - if (this.radiusHandle) { │ │ │ │ - this.layer.destroyFeatures([this.radiusHandle], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.radiusHandle = null │ │ │ │ - } │ │ │ │ - } else if (vertex !== this.radiusHandle) { │ │ │ │ - this.layer.events.triggerEvent("vertexmodified", { │ │ │ │ - vertex: vertex.geometry, │ │ │ │ - feature: this.feature, │ │ │ │ - pixel: pixel │ │ │ │ - }) │ │ │ │ + return result │ │ │ │ + } │ │ │ │ + return OpenLayers.Class(OpenLayers.Format, { │ │ │ │ + read: function(text) { │ │ │ │ + var result = buildAst(tokenize(text)); │ │ │ │ + if (this.keepData) { │ │ │ │ + this.data = result │ │ │ │ } │ │ │ │ - if (this.virtualVertices.length > 0) { │ │ │ │ - this.layer.destroyFeatures(this.virtualVertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.virtualVertices = [] │ │ │ │ + return result │ │ │ │ + }, │ │ │ │ + write: function(filter) { │ │ │ │ + if (filter instanceof OpenLayers.Geometry) { │ │ │ │ + return filter.toString() │ │ │ │ } │ │ │ │ - this.layer.drawFeature(this.feature, this.standalone ? undefined : "select") │ │ │ │ - } │ │ │ │ - this.layer.drawFeature(vertex) │ │ │ │ - }, │ │ │ │ - dragComplete: function(vertex) { │ │ │ │ - this.resetVertices(); │ │ │ │ - this.setFeatureState(); │ │ │ │ - this.onModification(this.feature); │ │ │ │ - this.layer.events.triggerEvent("featuremodified", { │ │ │ │ - feature: this.feature │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - setFeatureState: function() { │ │ │ │ - if (this.feature.state != OpenLayers.State.INSERT && this.feature.state != OpenLayers.State.DELETE) { │ │ │ │ - this.feature.state = OpenLayers.State.UPDATE; │ │ │ │ - if (this.modified && this._originalGeometry) { │ │ │ │ - var feature = this.feature; │ │ │ │ - feature.modified = OpenLayers.Util.extend(feature.modified, { │ │ │ │ - geometry: this._originalGeometry │ │ │ │ - }); │ │ │ │ - delete this._originalGeometry │ │ │ │ + switch (filter.CLASS_NAME) { │ │ │ │ + case "OpenLayers.Filter.Spatial": │ │ │ │ + switch (filter.type) { │ │ │ │ + case OpenLayers.Filter.Spatial.BBOX: │ │ │ │ + return "BBOX(" + filter.property + "," + filter.value.toBBOX() + ")"; │ │ │ │ + case OpenLayers.Filter.Spatial.DWITHIN: │ │ │ │ + return "DWITHIN(" + filter.property + ", " + this.write(filter.value) + ", " + filter.distance + ")"; │ │ │ │ + case OpenLayers.Filter.Spatial.WITHIN: │ │ │ │ + return "WITHIN(" + filter.property + ", " + this.write(filter.value) + ")"; │ │ │ │ + case OpenLayers.Filter.Spatial.INTERSECTS: │ │ │ │ + return "INTERSECTS(" + filter.property + ", " + this.write(filter.value) + ")"; │ │ │ │ + case OpenLayers.Filter.Spatial.CONTAINS: │ │ │ │ + return "CONTAINS(" + filter.property + ", " + this.write(filter.value) + ")"; │ │ │ │ + default: │ │ │ │ + throw new Error("Unknown spatial filter type: " + filter.type) │ │ │ │ + } │ │ │ │ + case "OpenLayers.Filter.Logical": │ │ │ │ + if (filter.type == OpenLayers.Filter.Logical.NOT) { │ │ │ │ + return "NOT (" + this.write(filter.filters[0]) + ")" │ │ │ │ + } else { │ │ │ │ + var res = "("; │ │ │ │ + var first = true; │ │ │ │ + for (var i = 0; i < filter.filters.length; i++) { │ │ │ │ + if (first) { │ │ │ │ + first = false │ │ │ │ + } else { │ │ │ │ + res += ") " + logicalReverse[filter.type] + " (" │ │ │ │ + } │ │ │ │ + res += this.write(filter.filters[i]) │ │ │ │ + } │ │ │ │ + return res + ")" │ │ │ │ + } │ │ │ │ + case "OpenLayers.Filter.Comparison": │ │ │ │ + if (filter.type == OpenLayers.Filter.Comparison.BETWEEN) { │ │ │ │ + return filter.property + " BETWEEN " + this.write(filter.lowerBoundary) + " AND " + this.write(filter.upperBoundary) │ │ │ │ + } else { │ │ │ │ + return filter.value !== null ? filter.property + " " + operatorReverse[filter.type] + " " + this.write(filter.value) : filter.property + " " + operatorReverse[filter.type] │ │ │ │ + } │ │ │ │ + case undefined: │ │ │ │ + if (typeof filter === "string") { │ │ │ │ + return "'" + filter.replace(/'/g, "''") + "'" │ │ │ │ + } else if (typeof filter === "number") { │ │ │ │ + return String(filter) │ │ │ │ + } │ │ │ │ + default: │ │ │ │ + throw new Error("Can't encode: " + filter.CLASS_NAME + " " + filter) │ │ │ │ } │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.CQL" │ │ │ │ + }) │ │ │ │ +}(); │ │ │ │ +OpenLayers.Format.OWSContext = OpenLayers.Class(OpenLayers.Format.Context, { │ │ │ │ + defaultVersion: "0.3.1", │ │ │ │ + getVersion: function(root, options) { │ │ │ │ + var version = OpenLayers.Format.XML.VersionedOGC.prototype.getVersion.apply(this, arguments); │ │ │ │ + if (version === "0.3.0") { │ │ │ │ + version = this.defaultVersion │ │ │ │ } │ │ │ │ + return version │ │ │ │ }, │ │ │ │ - resetVertices: function() { │ │ │ │ - if (this.vertices.length > 0) { │ │ │ │ - this.layer.removeFeatures(this.vertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.vertices = [] │ │ │ │ - } │ │ │ │ - if (this.virtualVertices.length > 0) { │ │ │ │ - this.layer.removeFeatures(this.virtualVertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.virtualVertices = [] │ │ │ │ - } │ │ │ │ - if (this.dragHandle) { │ │ │ │ - this.layer.destroyFeatures([this.dragHandle], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.dragHandle = null │ │ │ │ + toContext: function(obj) { │ │ │ │ + var context = {}; │ │ │ │ + if (obj.CLASS_NAME == "OpenLayers.Map") { │ │ │ │ + context.bounds = obj.getExtent(); │ │ │ │ + context.maxExtent = obj.maxExtent; │ │ │ │ + context.projection = obj.projection; │ │ │ │ + context.size = obj.getSize(); │ │ │ │ + context.layers = obj.layers │ │ │ │ } │ │ │ │ - if (this.radiusHandle) { │ │ │ │ - this.layer.destroyFeatures([this.radiusHandle], { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.radiusHandle = null │ │ │ │ + return context │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.OWSContext" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.GPX = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + defaultDesc: "No description available", │ │ │ │ + extractWaypoints: true, │ │ │ │ + extractTracks: true, │ │ │ │ + extractRoutes: true, │ │ │ │ + extractAttributes: true, │ │ │ │ + namespaces: { │ │ │ │ + gpx: "http://www.topografix.com/GPX/1/1", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + }, │ │ │ │ + schemaLocation: "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd", │ │ │ │ + creator: "OpenLayers", │ │ │ │ + initialize: function(options) { │ │ │ │ + this.externalProjection = new OpenLayers.Projection("EPSG:4326"); │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]) │ │ │ │ + }, │ │ │ │ + read: function(doc) { │ │ │ │ + if (typeof doc == "string") { │ │ │ │ + doc = OpenLayers.Format.XML.prototype.read.apply(this, [doc]) │ │ │ │ } │ │ │ │ - if (this.feature && this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") { │ │ │ │ - if (this.mode & OpenLayers.Control.ModifyFeature.DRAG) { │ │ │ │ - this.collectDragHandle() │ │ │ │ + var features = []; │ │ │ │ + if (this.extractTracks) { │ │ │ │ + var tracks = doc.getElementsByTagName("trk"); │ │ │ │ + for (var i = 0, len = tracks.length; i < len; i++) { │ │ │ │ + var attrs = {}; │ │ │ │ + if (this.extractAttributes) { │ │ │ │ + attrs = this.parseAttributes(tracks[i]) │ │ │ │ + } │ │ │ │ + var segs = this.getElementsByTagNameNS(tracks[i], tracks[i].namespaceURI, "trkseg"); │ │ │ │ + for (var j = 0, seglen = segs.length; j < seglen; j++) { │ │ │ │ + var track = this.extractSegment(segs[j], "trkpt"); │ │ │ │ + features.push(new OpenLayers.Feature.Vector(track, attrs)) │ │ │ │ + } │ │ │ │ } │ │ │ │ - if (this.mode & (OpenLayers.Control.ModifyFeature.ROTATE | OpenLayers.Control.ModifyFeature.RESIZE)) { │ │ │ │ - this.collectRadiusHandle() │ │ │ │ + } │ │ │ │ + if (this.extractRoutes) { │ │ │ │ + var routes = doc.getElementsByTagName("rte"); │ │ │ │ + for (var k = 0, klen = routes.length; k < klen; k++) { │ │ │ │ + var attrs = {}; │ │ │ │ + if (this.extractAttributes) { │ │ │ │ + attrs = this.parseAttributes(routes[k]) │ │ │ │ + } │ │ │ │ + var route = this.extractSegment(routes[k], "rtept"); │ │ │ │ + features.push(new OpenLayers.Feature.Vector(route, attrs)) │ │ │ │ } │ │ │ │ - if (this.mode & OpenLayers.Control.ModifyFeature.RESHAPE) { │ │ │ │ - if (!(this.mode & OpenLayers.Control.ModifyFeature.RESIZE)) { │ │ │ │ - this.collectVertices() │ │ │ │ + } │ │ │ │ + if (this.extractWaypoints) { │ │ │ │ + var waypoints = doc.getElementsByTagName("wpt"); │ │ │ │ + for (var l = 0, len = waypoints.length; l < len; l++) { │ │ │ │ + var attrs = {}; │ │ │ │ + if (this.extractAttributes) { │ │ │ │ + attrs = this.parseAttributes(waypoints[l]) │ │ │ │ } │ │ │ │ + var wpt = new OpenLayers.Geometry.Point(waypoints[l].getAttribute("lon"), waypoints[l].getAttribute("lat")); │ │ │ │ + features.push(new OpenLayers.Feature.Vector(wpt, attrs)) │ │ │ │ } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - handleKeypress: function(evt) { │ │ │ │ - var code = evt.keyCode; │ │ │ │ - if (this.feature && OpenLayers.Util.indexOf(this.deleteCodes, code) != -1) { │ │ │ │ - var vertex = this.layer.getFeatureFromEvent(this.handlers.drag.evt); │ │ │ │ - if (vertex && OpenLayers.Util.indexOf(this.vertices, vertex) != -1 && !this.handlers.drag.dragging && vertex.geometry.parent) { │ │ │ │ - vertex.geometry.parent.removeComponent(vertex.geometry); │ │ │ │ - this.layer.events.triggerEvent("vertexremoved", { │ │ │ │ - vertex: vertex.geometry, │ │ │ │ - feature: this.feature, │ │ │ │ - pixel: evt.xy │ │ │ │ - }); │ │ │ │ - this.layer.drawFeature(this.feature, this.standalone ? undefined : "select"); │ │ │ │ - this.modified = true; │ │ │ │ - this.resetVertices(); │ │ │ │ - this.setFeatureState(); │ │ │ │ - this.onModification(this.feature); │ │ │ │ - this.layer.events.triggerEvent("featuremodified", { │ │ │ │ - feature: this.feature │ │ │ │ - }) │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + for (var g = 0, featLength = features.length; g < featLength; g++) { │ │ │ │ + features[g].geometry.transform(this.externalProjection, this.internalProjection) │ │ │ │ } │ │ │ │ } │ │ │ │ + return features │ │ │ │ }, │ │ │ │ - collectVertices: function() { │ │ │ │ - this.vertices = []; │ │ │ │ - this.virtualVertices = []; │ │ │ │ - var control = this; │ │ │ │ - │ │ │ │ - function collectComponentVertices(geometry) { │ │ │ │ - var i, vertex, component, len; │ │ │ │ - if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - vertex = new OpenLayers.Feature.Vector(geometry); │ │ │ │ - vertex._sketch = true; │ │ │ │ - vertex.renderIntent = control.vertexRenderIntent; │ │ │ │ - control.vertices.push(vertex) │ │ │ │ - } else { │ │ │ │ - var numVert = geometry.components.length; │ │ │ │ - if (geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { │ │ │ │ - numVert -= 1 │ │ │ │ - } │ │ │ │ - for (i = 0; i < numVert; ++i) { │ │ │ │ - component = geometry.components[i]; │ │ │ │ - if (component.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - vertex = new OpenLayers.Feature.Vector(component); │ │ │ │ - vertex._sketch = true; │ │ │ │ - vertex.renderIntent = control.vertexRenderIntent; │ │ │ │ - control.vertices.push(vertex) │ │ │ │ - } else { │ │ │ │ - collectComponentVertices(component) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (control.createVertices && geometry.CLASS_NAME != "OpenLayers.Geometry.MultiPoint") { │ │ │ │ - for (i = 0, len = geometry.components.length; i < len - 1; ++i) { │ │ │ │ - var prevVertex = geometry.components[i]; │ │ │ │ - var nextVertex = geometry.components[i + 1]; │ │ │ │ - if (prevVertex.CLASS_NAME == "OpenLayers.Geometry.Point" && nextVertex.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ - var x = (prevVertex.x + nextVertex.x) / 2; │ │ │ │ - var y = (prevVertex.y + nextVertex.y) / 2; │ │ │ │ - var point = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(x, y), null, control.virtualStyle); │ │ │ │ - point.geometry.parent = geometry; │ │ │ │ - point._index = i + 1; │ │ │ │ - point._sketch = true; │ │ │ │ - control.virtualVertices.push(point) │ │ │ │ - } │ │ │ │ + extractSegment: function(segment, segmentType) { │ │ │ │ + var points = this.getElementsByTagNameNS(segment, segment.namespaceURI, segmentType); │ │ │ │ + var point_features = []; │ │ │ │ + for (var i = 0, len = points.length; i < len; i++) { │ │ │ │ + point_features.push(new OpenLayers.Geometry.Point(points[i].getAttribute("lon"), points[i].getAttribute("lat"))) │ │ │ │ + } │ │ │ │ + return new OpenLayers.Geometry.LineString(point_features) │ │ │ │ + }, │ │ │ │ + parseAttributes: function(node) { │ │ │ │ + var attributes = {}; │ │ │ │ + var attrNode = node.firstChild, │ │ │ │ + value, name; │ │ │ │ + while (attrNode) { │ │ │ │ + if (attrNode.nodeType == 1 && attrNode.firstChild) { │ │ │ │ + value = attrNode.firstChild; │ │ │ │ + if (value.nodeType == 3 || value.nodeType == 4) { │ │ │ │ + name = attrNode.prefix ? attrNode.nodeName.split(":")[1] : attrNode.nodeName; │ │ │ │ + if (name != "trkseg" && name != "rtept") { │ │ │ │ + attributes[name] = value.nodeValue │ │ │ │ } │ │ │ │ } │ │ │ │ } │ │ │ │ + attrNode = attrNode.nextSibling │ │ │ │ } │ │ │ │ - collectComponentVertices.call(this, this.feature.geometry); │ │ │ │ - this.layer.addFeatures(this.virtualVertices, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.layer.addFeatures(this.vertices, { │ │ │ │ - silent: true │ │ │ │ - }) │ │ │ │ + return attributes │ │ │ │ }, │ │ │ │ - collectDragHandle: function() { │ │ │ │ - var geometry = this.feature.geometry; │ │ │ │ - var center = geometry.getBounds().getCenterLonLat(); │ │ │ │ - var originGeometry = new OpenLayers.Geometry.Point(center.lon, center.lat); │ │ │ │ - var origin = new OpenLayers.Feature.Vector(originGeometry); │ │ │ │ - originGeometry.move = function(x, y) { │ │ │ │ - OpenLayers.Geometry.Point.prototype.move.call(this, x, y); │ │ │ │ - geometry.move(x, y) │ │ │ │ - }; │ │ │ │ - origin._sketch = true; │ │ │ │ - this.dragHandle = origin; │ │ │ │ - this.dragHandle.renderIntent = this.vertexRenderIntent; │ │ │ │ - this.layer.addFeatures([this.dragHandle], { │ │ │ │ - silent: true │ │ │ │ - }) │ │ │ │ + write: function(features, metadata) { │ │ │ │ + features = OpenLayers.Util.isArray(features) ? features : [features]; │ │ │ │ + var gpx = this.createElementNS(this.namespaces.gpx, "gpx"); │ │ │ │ + gpx.setAttribute("version", "1.1"); │ │ │ │ + gpx.setAttribute("creator", this.creator); │ │ │ │ + this.setAttributes(gpx, { │ │ │ │ + "xsi:schemaLocation": this.schemaLocation │ │ │ │ + }); │ │ │ │ + if (metadata && typeof metadata == "object") { │ │ │ │ + gpx.appendChild(this.buildMetadataNode(metadata)) │ │ │ │ + } │ │ │ │ + for (var i = 0, len = features.length; i < len; i++) { │ │ │ │ + gpx.appendChild(this.buildFeatureNode(features[i])) │ │ │ │ + } │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [gpx]) │ │ │ │ }, │ │ │ │ - collectRadiusHandle: function() { │ │ │ │ - var geometry = this.feature.geometry; │ │ │ │ - var bounds = geometry.getBounds(); │ │ │ │ - var center = bounds.getCenterLonLat(); │ │ │ │ - var originGeometry = new OpenLayers.Geometry.Point(center.lon, center.lat); │ │ │ │ - var radiusGeometry = new OpenLayers.Geometry.Point(bounds.right, bounds.bottom); │ │ │ │ - var radius = new OpenLayers.Feature.Vector(radiusGeometry); │ │ │ │ - var resize = this.mode & OpenLayers.Control.ModifyFeature.RESIZE; │ │ │ │ - var reshape = this.mode & OpenLayers.Control.ModifyFeature.RESHAPE; │ │ │ │ - var rotate = this.mode & OpenLayers.Control.ModifyFeature.ROTATE; │ │ │ │ - radiusGeometry.move = function(x, y) { │ │ │ │ - OpenLayers.Geometry.Point.prototype.move.call(this, x, y); │ │ │ │ - var dx1 = this.x - originGeometry.x; │ │ │ │ - var dy1 = this.y - originGeometry.y; │ │ │ │ - var dx0 = dx1 - x; │ │ │ │ - var dy0 = dy1 - y; │ │ │ │ - if (rotate) { │ │ │ │ - var a0 = Math.atan2(dy0, dx0); │ │ │ │ - var a1 = Math.atan2(dy1, dx1); │ │ │ │ - var angle = a1 - a0; │ │ │ │ - angle *= 180 / Math.PI; │ │ │ │ - geometry.rotate(angle, originGeometry) │ │ │ │ - } │ │ │ │ - if (resize) { │ │ │ │ - var scale, ratio; │ │ │ │ - if (reshape) { │ │ │ │ - scale = dy1 / dy0; │ │ │ │ - ratio = dx1 / dx0 / scale │ │ │ │ - } else { │ │ │ │ - var l0 = Math.sqrt(dx0 * dx0 + dy0 * dy0); │ │ │ │ - var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1); │ │ │ │ - scale = l1 / l0 │ │ │ │ - } │ │ │ │ - geometry.resize(scale, originGeometry, ratio) │ │ │ │ + buildMetadataNode: function(metadata) { │ │ │ │ + var types = ["name", "desc", "author"], │ │ │ │ + node = this.createElementNS(this.namespaces.gpx, "metadata"); │ │ │ │ + for (var i = 0; i < types.length; i++) { │ │ │ │ + var type = types[i]; │ │ │ │ + if (metadata[type]) { │ │ │ │ + var n = this.createElementNS(this.namespaces.gpx, type); │ │ │ │ + n.appendChild(this.createTextNode(metadata[type])); │ │ │ │ + node.appendChild(n) │ │ │ │ } │ │ │ │ - }; │ │ │ │ - radius._sketch = true; │ │ │ │ - this.radiusHandle = radius; │ │ │ │ - this.radiusHandle.renderIntent = this.vertexRenderIntent; │ │ │ │ - this.layer.addFeatures([this.radiusHandle], { │ │ │ │ - silent: true │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - setMap: function(map) { │ │ │ │ - this.handlers.drag.setMap(map); │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - handleMapEvents: function(evt) { │ │ │ │ - if (evt.type == "removelayer" || evt.property == "order") { │ │ │ │ - this.moveLayerToTop() │ │ │ │ } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - moveLayerToTop: function() { │ │ │ │ - var index = Math.max(this.map.Z_INDEX_BASE["Feature"] - 1, this.layer.getZIndex()) + 1; │ │ │ │ - this.layer.setZIndex(index) │ │ │ │ - }, │ │ │ │ - moveLayerBack: function() { │ │ │ │ - var index = this.layer.getZIndex() - 1; │ │ │ │ - if (index >= this.map.Z_INDEX_BASE["Feature"]) { │ │ │ │ - this.layer.setZIndex(index) │ │ │ │ + buildFeatureNode: function(feature) { │ │ │ │ + var geometry = feature.geometry; │ │ │ │ + geometry = geometry.clone(); │ │ │ │ + if (this.internalProjection && this.externalProjection) { │ │ │ │ + geometry.transform(this.internalProjection, this.externalProjection) │ │ │ │ + } │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { │ │ │ │ + var wpt = this.buildWptNode(geometry); │ │ │ │ + this.appendAttributesNode(wpt, feature); │ │ │ │ + return wpt │ │ │ │ } else { │ │ │ │ - this.map.setLayerZIndex(this.layer, this.map.getLayerIndex(this.layer)) │ │ │ │ + var trkNode = this.createElementNS(this.namespaces.gpx, "trk"); │ │ │ │ + this.appendAttributesNode(trkNode, feature); │ │ │ │ + var trkSegNodes = this.buildTrkSegNode(geometry); │ │ │ │ + trkSegNodes = OpenLayers.Util.isArray(trkSegNodes) ? trkSegNodes : [trkSegNodes]; │ │ │ │ + for (var i = 0, len = trkSegNodes.length; i < len; i++) { │ │ │ │ + trkNode.appendChild(trkSegNodes[i]) │ │ │ │ + } │ │ │ │ + return trkNode │ │ │ │ } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ModifyFeature" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.ModifyFeature.RESHAPE = 1; │ │ │ │ -OpenLayers.Control.ModifyFeature.RESIZE = 2; │ │ │ │ -OpenLayers.Control.ModifyFeature.ROTATE = 4; │ │ │ │ -OpenLayers.Control.ModifyFeature.DRAG = 8; │ │ │ │ -OpenLayers.Control.Pan = OpenLayers.Class(OpenLayers.Control.Button, { │ │ │ │ - slideFactor: 50, │ │ │ │ - slideRatio: null, │ │ │ │ - direction: null, │ │ │ │ - initialize: function(direction, options) { │ │ │ │ - this.direction = direction; │ │ │ │ - this.CLASS_NAME += this.direction; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]) │ │ │ │ - }, │ │ │ │ - trigger: function() { │ │ │ │ - if (this.map) { │ │ │ │ - var getSlideFactor = OpenLayers.Function.bind(function(dim) { │ │ │ │ - return this.slideRatio ? this.map.getSize()[dim] * this.slideRatio : this.slideFactor │ │ │ │ - }, this); │ │ │ │ - switch (this.direction) { │ │ │ │ - case OpenLayers.Control.Pan.NORTH: │ │ │ │ - this.map.pan(0, -getSlideFactor("h")); │ │ │ │ - break; │ │ │ │ - case OpenLayers.Control.Pan.SOUTH: │ │ │ │ - this.map.pan(0, getSlideFactor("h")); │ │ │ │ - break; │ │ │ │ - case OpenLayers.Control.Pan.WEST: │ │ │ │ - this.map.pan(-getSlideFactor("w"), 0); │ │ │ │ - break; │ │ │ │ - case OpenLayers.Control.Pan.EAST: │ │ │ │ - this.map.pan(getSlideFactor("w"), 0); │ │ │ │ - break │ │ │ │ + buildTrkSegNode: function(geometry) { │ │ │ │ + var node, i, len, point, nodes; │ │ │ │ + if (geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" || geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { │ │ │ │ + node = this.createElementNS(this.namespaces.gpx, "trkseg"); │ │ │ │ + for (i = 0, len = geometry.components.length; i < len; i++) { │ │ │ │ + point = geometry.components[i]; │ │ │ │ + node.appendChild(this.buildTrkPtNode(point)) │ │ │ │ + } │ │ │ │ + return node │ │ │ │ + } else { │ │ │ │ + nodes = []; │ │ │ │ + for (i = 0, len = geometry.components.length; i < len; i++) { │ │ │ │ + nodes.push(this.buildTrkSegNode(geometry.components[i])) │ │ │ │ } │ │ │ │ + return nodes │ │ │ │ } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Pan" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Pan.NORTH = "North"; │ │ │ │ -OpenLayers.Control.Pan.SOUTH = "South"; │ │ │ │ -OpenLayers.Control.Pan.EAST = "East"; │ │ │ │ -OpenLayers.Control.Pan.WEST = "West"; │ │ │ │ -OpenLayers.Control.PanPanel = OpenLayers.Class(OpenLayers.Control.Panel, { │ │ │ │ - slideFactor: 50, │ │ │ │ - slideRatio: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); │ │ │ │ - var options = { │ │ │ │ - slideFactor: this.slideFactor, │ │ │ │ - slideRatio: this.slideRatio │ │ │ │ - }; │ │ │ │ - this.addControls([new OpenLayers.Control.Pan(OpenLayers.Control.Pan.NORTH, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.SOUTH, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.EAST, options), new OpenLayers.Control.Pan(OpenLayers.Control.Pan.WEST, options)]) │ │ │ │ + buildTrkPtNode: function(point) { │ │ │ │ + var node = this.createElementNS(this.namespaces.gpx, "trkpt"); │ │ │ │ + node.setAttribute("lon", point.x); │ │ │ │ + node.setAttribute("lat", point.y); │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.PanPanel" │ │ │ │ + buildWptNode: function(geometry) { │ │ │ │ + var node = this.createElementNS(this.namespaces.gpx, "wpt"); │ │ │ │ + node.setAttribute("lon", geometry.x); │ │ │ │ + node.setAttribute("lat", geometry.y); │ │ │ │ + return node │ │ │ │ + }, │ │ │ │ + appendAttributesNode: function(node, feature) { │ │ │ │ + var name = this.createElementNS(this.namespaces.gpx, "name"); │ │ │ │ + name.appendChild(this.createTextNode(feature.attributes.name || feature.id)); │ │ │ │ + node.appendChild(name); │ │ │ │ + var desc = this.createElementNS(this.namespaces.gpx, "desc"); │ │ │ │ + desc.appendChild(this.createTextNode(feature.attributes.description || this.defaultDesc)); │ │ │ │ + node.appendChild(desc) │ │ │ │ + }, │ │ │ │ + CLASS_NAME: "OpenLayers.Format.GPX" │ │ │ │ }); │ │ │ │ -OpenLayers.Control.GetFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - protocol: null, │ │ │ │ - multipleKey: null, │ │ │ │ - toggleKey: null, │ │ │ │ - modifiers: null, │ │ │ │ - multiple: false, │ │ │ │ - click: true, │ │ │ │ - single: true, │ │ │ │ - clickout: true, │ │ │ │ - toggle: false, │ │ │ │ - clickTolerance: 5, │ │ │ │ - hover: false, │ │ │ │ - box: false, │ │ │ │ - maxFeatures: 10, │ │ │ │ - features: null, │ │ │ │ - hoverFeature: null, │ │ │ │ - handlers: null, │ │ │ │ - hoverResponse: null, │ │ │ │ - filterType: OpenLayers.Filter.Spatial.BBOX, │ │ │ │ - initialize: function(options) { │ │ │ │ - options.handlerOptions = options.handlerOptions || {}; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.features = {}; │ │ │ │ - this.handlers = {}; │ │ │ │ - if (this.click) { │ │ │ │ - this.handlers.click = new OpenLayers.Handler.Click(this, { │ │ │ │ - click: this.selectClick │ │ │ │ - }, this.handlerOptions.click || {}) │ │ │ │ +OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + defaultVersion: "1.0.0", │ │ │ │ + yx: { │ │ │ │ + "urn:ogc:def:crs:EPSG::4326": true │ │ │ │ + }, │ │ │ │ + createLayer: function(capabilities, config) { │ │ │ │ + var layer; │ │ │ │ + if (!("layer" in config)) { │ │ │ │ + throw new Error("Missing property 'layer' in configuration.") │ │ │ │ } │ │ │ │ - if (this.box) { │ │ │ │ - this.handlers.box = new OpenLayers.Handler.Box(this, { │ │ │ │ - done: this.selectBox │ │ │ │ - }, OpenLayers.Util.extend(this.handlerOptions.box, { │ │ │ │ - boxDivClassName: "olHandlerBoxSelectFeature" │ │ │ │ - })) │ │ │ │ + var contents = capabilities.contents; │ │ │ │ + var layers = contents.layers; │ │ │ │ + var layerDef; │ │ │ │ + for (var i = 0, ii = contents.layers.length; i < ii; ++i) { │ │ │ │ + if (contents.layers[i].identifier === config.layer) { │ │ │ │ + layerDef = contents.layers[i]; │ │ │ │ + break │ │ │ │ + } │ │ │ │ } │ │ │ │ - if (this.hover) { │ │ │ │ - this.handlers.hover = new OpenLayers.Handler.Hover(this, { │ │ │ │ - move: this.cancelHover, │ │ │ │ - pause: this.selectHover │ │ │ │ - }, OpenLayers.Util.extend(this.handlerOptions.hover, { │ │ │ │ - delay: 250, │ │ │ │ - pixelTolerance: 2 │ │ │ │ - })) │ │ │ │ + if (!layerDef) { │ │ │ │ + throw new Error("Layer not found") │ │ │ │ } │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - if (!this.active) { │ │ │ │ - for (var i in this.handlers) { │ │ │ │ - this.handlers[i].activate() │ │ │ │ + var format = config.format; │ │ │ │ + if (!format && layerDef.formats && layerDef.formats.length) { │ │ │ │ + format = layerDef.formats[0] │ │ │ │ + } │ │ │ │ + var matrixSet; │ │ │ │ + if (config.matrixSet) { │ │ │ │ + matrixSet = contents.tileMatrixSets[config.matrixSet] │ │ │ │ + } else if (layerDef.tileMatrixSetLinks.length >= 1) { │ │ │ │ + matrixSet = contents.tileMatrixSets[layerDef.tileMatrixSetLinks[0].tileMatrixSet] │ │ │ │ + } │ │ │ │ + if (!matrixSet) { │ │ │ │ + throw new Error("matrixSet not found") │ │ │ │ + } │ │ │ │ + var style; │ │ │ │ + for (var i = 0, ii = layerDef.styles.length; i < ii; ++i) { │ │ │ │ + style = layerDef.styles[i]; │ │ │ │ + if (style.isDefault) { │ │ │ │ + break │ │ │ │ } │ │ │ │ } │ │ │ │ - return OpenLayers.Control.prototype.activate.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - for (var i in this.handlers) { │ │ │ │ - this.handlers[i].deactivate() │ │ │ │ + var requestEncoding = config.requestEncoding; │ │ │ │ + if (!requestEncoding) { │ │ │ │ + requestEncoding = "KVP"; │ │ │ │ + if (capabilities.operationsMetadata.GetTile.dcp.http) { │ │ │ │ + var http = capabilities.operationsMetadata.GetTile.dcp.http; │ │ │ │ + if (http.get[0].constraints) { │ │ │ │ + var constraints = http.get[0].constraints; │ │ │ │ + var allowedValues = constraints.GetEncoding.allowedValues; │ │ │ │ + if (!allowedValues.KVP && (allowedValues.REST || allowedValues.RESTful)) { │ │ │ │ + requestEncoding = "REST" │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ } │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - selectClick: function(evt) { │ │ │ │ - var bounds = this.pixelToBounds(evt.xy); │ │ │ │ - this.setModifiers(evt); │ │ │ │ - this.request(bounds, { │ │ │ │ - single: this.single │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - selectBox: function(position) { │ │ │ │ - var bounds; │ │ │ │ - if (position instanceof OpenLayers.Bounds) { │ │ │ │ - var minXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.left, │ │ │ │ - y: position.bottom │ │ │ │ - }); │ │ │ │ - var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.right, │ │ │ │ - y: position.top │ │ │ │ - }); │ │ │ │ - bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat) │ │ │ │ - } else { │ │ │ │ - if (this.click) { │ │ │ │ - return │ │ │ │ + var dimensions = []; │ │ │ │ + var params = config.params || {}; │ │ │ │ + delete config.params; │ │ │ │ + for (var id = 0, ld = layerDef.dimensions.length; id < ld; id++) { │ │ │ │ + var dimension = layerDef.dimensions[id]; │ │ │ │ + dimensions.push(dimension.identifier); │ │ │ │ + if (!params.hasOwnProperty(dimension.identifier)) { │ │ │ │ + params[dimension.identifier] = dimension["default"] │ │ │ │ } │ │ │ │ - bounds = this.pixelToBounds(position) │ │ │ │ } │ │ │ │ - this.setModifiers(this.handlers.box.dragHandler.evt); │ │ │ │ - this.request(bounds) │ │ │ │ - }, │ │ │ │ - selectHover: function(evt) { │ │ │ │ - var bounds = this.pixelToBounds(evt.xy); │ │ │ │ - this.request(bounds, { │ │ │ │ - single: true, │ │ │ │ - hover: true │ │ │ │ - }) │ │ │ │ - }, │ │ │ │ - cancelHover: function() { │ │ │ │ - if (this.hoverResponse) { │ │ │ │ - this.protocol.abort(this.hoverResponse); │ │ │ │ - this.hoverResponse = null; │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait") │ │ │ │ + var projection = config.projection || matrixSet.supportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, "$1:$3"); │ │ │ │ + var units = config.units || (projection === "EPSG:4326" ? "degrees" : "m"); │ │ │ │ + var resolutions = []; │ │ │ │ + for (var mid in matrixSet.matrixIds) { │ │ │ │ + if (matrixSet.matrixIds.hasOwnProperty(mid)) { │ │ │ │ + resolutions.push(matrixSet.matrixIds[mid].scaleDenominator * 28e-5 / OpenLayers.METERS_PER_INCH / OpenLayers.INCHES_PER_UNIT[units]) │ │ │ │ + } │ │ │ │ } │ │ │ │ - }, │ │ │ │ - request: function(bounds, options) { │ │ │ │ - options = options || {}; │ │ │ │ - var filter = new OpenLayers.Filter.Spatial({ │ │ │ │ - type: this.filterType, │ │ │ │ - value: bounds │ │ │ │ - }); │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ - var response = this.protocol.read({ │ │ │ │ - maxFeatures: options.single == true ? this.maxFeatures : undefined, │ │ │ │ - filter: filter, │ │ │ │ - callback: function(result) { │ │ │ │ - if (result.success()) { │ │ │ │ - if (result.features.length) { │ │ │ │ - if (options.single == true) { │ │ │ │ - this.selectBestFeature(result.features, bounds.getCenterLonLat(), options) │ │ │ │ - } else { │ │ │ │ - this.select(result.features) │ │ │ │ - } │ │ │ │ - } else if (options.hover) { │ │ │ │ - this.hoverSelect() │ │ │ │ - } else { │ │ │ │ - this.events.triggerEvent("clickout"); │ │ │ │ - if (this.clickout) { │ │ │ │ - this.unselectAll() │ │ │ │ - } │ │ │ │ - } │ │ │ │ + var url; │ │ │ │ + if (requestEncoding === "REST" && layerDef.resourceUrls) { │ │ │ │ + url = []; │ │ │ │ + var resourceUrls = layerDef.resourceUrls, │ │ │ │ + resourceUrl; │ │ │ │ + for (var t = 0, tt = layerDef.resourceUrls.length; t < tt; ++t) { │ │ │ │ + resourceUrl = layerDef.resourceUrls[t]; │ │ │ │ + if (resourceUrl.format === format && resourceUrl.resourceType === "tile") { │ │ │ │ + url.push(resourceUrl.template) │ │ │ │ } │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait") │ │ │ │ - }, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - if (options.hover == true) { │ │ │ │ - this.hoverResponse = response │ │ │ │ + } │ │ │ │ + } else { │ │ │ │ + var httpGet = capabilities.operationsMetadata.GetTile.dcp.http.get; │ │ │ │ + url = []; │ │ │ │ + var constraint; │ │ │ │ + for (var i = 0, ii = httpGet.length; i < ii; i++) { │ │ │ │ + constraint = httpGet[i].constraints; │ │ │ │ + if (!constraint || constraint && constraint.GetEncoding.allowedValues[requestEncoding]) { │ │ │ │ + url.push(httpGet[i].url) │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ + return new OpenLayers.Layer.WMTS(OpenLayers.Util.applyDefaults(config, { │ │ │ │ + url: url, │ │ │ │ + requestEncoding: requestEncoding, │ │ │ │ + name: layerDef.title, │ │ │ │ + style: style.identifier, │ │ │ │ + format: format, │ │ │ │ + matrixIds: matrixSet.matrixIds, │ │ │ │ + matrixSet: matrixSet.identifier, │ │ │ │ + projection: projection, │ │ │ │ + units: units, │ │ │ │ + resolutions: config.isBaseLayer === false ? undefined : resolutions, │ │ │ │ + serverResolutions: resolutions, │ │ │ │ + tileFullExtent: matrixSet.bounds, │ │ │ │ + dimensions: dimensions, │ │ │ │ + params: params │ │ │ │ + })) │ │ │ │ }, │ │ │ │ - selectBestFeature: function(features, clickPosition, options) { │ │ │ │ - options = options || {}; │ │ │ │ - if (features.length) { │ │ │ │ - var point = new OpenLayers.Geometry.Point(clickPosition.lon, clickPosition.lat); │ │ │ │ - var feature, resultFeature, dist; │ │ │ │ - var minDist = Number.MAX_VALUE; │ │ │ │ - for (var i = 0; i < features.length; ++i) { │ │ │ │ - feature = features[i]; │ │ │ │ - if (feature.geometry) { │ │ │ │ - dist = point.distanceTo(feature.geometry, { │ │ │ │ - edge: false │ │ │ │ - }); │ │ │ │ - if (dist < minDist) { │ │ │ │ - minDist = dist; │ │ │ │ - resultFeature = feature; │ │ │ │ - if (minDist == 0) { │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMTSCapabilities" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.WMSDescribeLayer = OpenLayers.Class(OpenLayers.Format.XML.VersionedOGC, { │ │ │ │ + defaultVersion: "1.1.1", │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMSDescribeLayer" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.WMC.v1 = OpenLayers.Class(OpenLayers.Format.XML, { │ │ │ │ + namespaces: { │ │ │ │ + ol: "http://openlayers.org/context", │ │ │ │ + wmc: "http://www.opengis.net/context", │ │ │ │ + sld: "http://www.opengis.net/sld", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ + }, │ │ │ │ + schemaLocation: "", │ │ │ │ + getNamespacePrefix: function(uri) { │ │ │ │ + var prefix = null; │ │ │ │ + if (uri == null) { │ │ │ │ + prefix = this.namespaces[this.defaultPrefix] │ │ │ │ + } else { │ │ │ │ + for (prefix in this.namespaces) { │ │ │ │ + if (this.namespaces[prefix] == uri) { │ │ │ │ + break │ │ │ │ } │ │ │ │ } │ │ │ │ - if (options.hover == true) { │ │ │ │ - this.hoverSelect(resultFeature) │ │ │ │ - } else { │ │ │ │ - this.select(resultFeature || features) │ │ │ │ - } │ │ │ │ } │ │ │ │ + return prefix │ │ │ │ }, │ │ │ │ - setModifiers: function(evt) { │ │ │ │ - this.modifiers = { │ │ │ │ - multiple: this.multiple || this.multipleKey && evt[this.multipleKey], │ │ │ │ - toggle: this.toggle || this.toggleKey && evt[this.toggleKey] │ │ │ │ - } │ │ │ │ + defaultPrefix: "wmc", │ │ │ │ + rootPrefix: null, │ │ │ │ + defaultStyleName: "", │ │ │ │ + defaultStyleTitle: "Default", │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.XML.prototype.initialize.apply(this, [options]) │ │ │ │ }, │ │ │ │ - select: function(features) { │ │ │ │ - if (!this.modifiers.multiple && !this.modifiers.toggle) { │ │ │ │ - this.unselectAll() │ │ │ │ - } │ │ │ │ - if (!OpenLayers.Util.isArray(features)) { │ │ │ │ - features = [features] │ │ │ │ + read: function(data) { │ │ │ │ + if (typeof data == "string") { │ │ │ │ + data = OpenLayers.Format.XML.prototype.read.apply(this, [data]) │ │ │ │ } │ │ │ │ - var cont = this.events.triggerEvent("beforefeaturesselected", { │ │ │ │ - features: features │ │ │ │ - }); │ │ │ │ - if (cont !== false) { │ │ │ │ - var selectedFeatures = []; │ │ │ │ - var feature; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - feature = features[i]; │ │ │ │ - if (this.features[feature.fid || feature.id]) { │ │ │ │ - if (this.modifiers.toggle) { │ │ │ │ - this.unselect(this.features[feature.fid || feature.id]) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - cont = this.events.triggerEvent("beforefeatureselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (cont !== false) { │ │ │ │ - this.features[feature.fid || feature.id] = feature; │ │ │ │ - selectedFeatures.push(feature); │ │ │ │ - this.events.triggerEvent("featureselected", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ - } │ │ │ │ + var root = data.documentElement; │ │ │ │ + this.rootPrefix = root.prefix; │ │ │ │ + var context = { │ │ │ │ + version: root.getAttribute("version") │ │ │ │ + }; │ │ │ │ + this.runChildNodes(context, root); │ │ │ │ + return context │ │ │ │ + }, │ │ │ │ + runChildNodes: function(obj, node) { │ │ │ │ + var children = node.childNodes; │ │ │ │ + var childNode, processor, prefix, local; │ │ │ │ + for (var i = 0, len = children.length; i < len; ++i) { │ │ │ │ + childNode = children[i]; │ │ │ │ + if (childNode.nodeType == 1) { │ │ │ │ + prefix = this.getNamespacePrefix(childNode.namespaceURI); │ │ │ │ + local = childNode.nodeName.split(":").pop(); │ │ │ │ + processor = this["read_" + prefix + "_" + local]; │ │ │ │ + if (processor) { │ │ │ │ + processor.apply(this, [obj, childNode]) │ │ │ │ } │ │ │ │ } │ │ │ │ - this.events.triggerEvent("featuresselected", { │ │ │ │ - features: selectedFeatures │ │ │ │ - }) │ │ │ │ } │ │ │ │ }, │ │ │ │ - hoverSelect: function(feature) { │ │ │ │ - var fid = feature ? feature.fid || feature.id : null; │ │ │ │ - var hfid = this.hoverFeature ? this.hoverFeature.fid || this.hoverFeature.id : null; │ │ │ │ - if (hfid && hfid != fid) { │ │ │ │ - this.events.triggerEvent("outfeature", { │ │ │ │ - feature: this.hoverFeature │ │ │ │ - }); │ │ │ │ - this.hoverFeature = null │ │ │ │ - } │ │ │ │ - if (fid && fid != hfid) { │ │ │ │ - this.events.triggerEvent("hoverfeature", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.hoverFeature = feature │ │ │ │ - } │ │ │ │ + read_wmc_General: function(context, node) { │ │ │ │ + this.runChildNodes(context, node) │ │ │ │ }, │ │ │ │ - unselect: function(feature) { │ │ │ │ - delete this.features[feature.fid || feature.id]; │ │ │ │ - this.events.triggerEvent("featureunselected", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ + read_wmc_BoundingBox: function(context, node) { │ │ │ │ + context.projection = node.getAttribute("SRS"); │ │ │ │ + context.bounds = new OpenLayers.Bounds(node.getAttribute("minx"), node.getAttribute("miny"), node.getAttribute("maxx"), node.getAttribute("maxy")) │ │ │ │ }, │ │ │ │ - unselectAll: function() { │ │ │ │ - for (var fid in this.features) { │ │ │ │ - this.unselect(this.features[fid]) │ │ │ │ - } │ │ │ │ + read_wmc_LayerList: function(context, node) { │ │ │ │ + context.layersContext = []; │ │ │ │ + this.runChildNodes(context, node) │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - for (var i in this.handlers) { │ │ │ │ - this.handlers[i].setMap(map) │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments) │ │ │ │ + read_wmc_Layer: function(context, node) { │ │ │ │ + var layerContext = { │ │ │ │ + visibility: node.getAttribute("hidden") != "1", │ │ │ │ + queryable: node.getAttribute("queryable") == "1", │ │ │ │ + formats: [], │ │ │ │ + styles: [], │ │ │ │ + metadata: {} │ │ │ │ + }; │ │ │ │ + this.runChildNodes(layerContext, node); │ │ │ │ + context.layersContext.push(layerContext) │ │ │ │ }, │ │ │ │ - pixelToBounds: function(pixel) { │ │ │ │ - var llPx = pixel.add(-this.clickTolerance / 2, this.clickTolerance / 2); │ │ │ │ - var urPx = pixel.add(this.clickTolerance / 2, -this.clickTolerance / 2); │ │ │ │ - var ll = this.map.getLonLatFromPixel(llPx); │ │ │ │ - var ur = this.map.getLonLatFromPixel(urPx); │ │ │ │ - return new OpenLayers.Bounds(ll.lon, ll.lat, ur.lon, ur.lat) │ │ │ │ + read_wmc_Extension: function(obj, node) { │ │ │ │ + this.runChildNodes(obj, node) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.GetFeature" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control.Button, { │ │ │ │ - trigger: function() { │ │ │ │ - if (this.map) { │ │ │ │ - this.map.zoomToMaxExtent() │ │ │ │ - } │ │ │ │ + read_ol_units: function(layerContext, node) { │ │ │ │ + layerContext.units = this.getChildValue(node) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ZoomToMaxExtent" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.CacheRead = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - fetchEvent: "tileloadstart", │ │ │ │ - layers: null, │ │ │ │ - autoActivate: true, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - var i, layers = this.layers || map.layers; │ │ │ │ - for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ - this.addLayer({ │ │ │ │ - layer: layers[i] │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - if (!this.layers) { │ │ │ │ - map.events.on({ │ │ │ │ - addlayer: this.addLayer, │ │ │ │ - removeLayer: this.removeLayer, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - } │ │ │ │ + read_ol_maxExtent: function(obj, node) { │ │ │ │ + var bounds = new OpenLayers.Bounds(node.getAttribute("minx"), node.getAttribute("miny"), node.getAttribute("maxx"), node.getAttribute("maxy")); │ │ │ │ + obj.maxExtent = bounds │ │ │ │ }, │ │ │ │ - addLayer: function(evt) { │ │ │ │ - evt.layer.events.register(this.fetchEvent, this, this.fetch) │ │ │ │ + read_ol_transparent: function(layerContext, node) { │ │ │ │ + layerContext.transparent = this.getChildValue(node) │ │ │ │ }, │ │ │ │ - removeLayer: function(evt) { │ │ │ │ - evt.layer.events.unregister(this.fetchEvent, this, this.fetch) │ │ │ │ + read_ol_numZoomLevels: function(layerContext, node) { │ │ │ │ + layerContext.numZoomLevels = parseInt(this.getChildValue(node)) │ │ │ │ }, │ │ │ │ - fetch: function(evt) { │ │ │ │ - if (this.active && window.localStorage && evt.tile instanceof OpenLayers.Tile.Image) { │ │ │ │ - var tile = evt.tile, │ │ │ │ - url = tile.url; │ │ │ │ - if (!tile.layer.crossOriginKeyword && OpenLayers.ProxyHost && url.indexOf(OpenLayers.ProxyHost) === 0) { │ │ │ │ - url = OpenLayers.Control.CacheWrite.urlMap[url] │ │ │ │ - } │ │ │ │ - var dataURI = window.localStorage.getItem("olCache_" + url); │ │ │ │ - if (dataURI) { │ │ │ │ - tile.url = dataURI; │ │ │ │ - if (evt.type === "tileerror") { │ │ │ │ - tile.setImgSrc(dataURI) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + read_ol_opacity: function(layerContext, node) { │ │ │ │ + layerContext.opacity = parseFloat(this.getChildValue(node)) │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.layers || this.map) { │ │ │ │ - var i, layers = this.layers || this.map.layers; │ │ │ │ - for (i = layers.length - 1; i >= 0; --i) { │ │ │ │ - this.removeLayer({ │ │ │ │ - layer: layers[i] │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.map) { │ │ │ │ - this.map.events.un({ │ │ │ │ - addlayer: this.addLayer, │ │ │ │ - removeLayer: this.removeLayer, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ + read_ol_singleTile: function(layerContext, node) { │ │ │ │ + layerContext.singleTile = this.getChildValue(node) == "true" │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.CacheRead" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.LayerSwitcher = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - layerStates: null, │ │ │ │ - layersDiv: null, │ │ │ │ - baseLayersDiv: null, │ │ │ │ - baseLayers: null, │ │ │ │ - dataLbl: null, │ │ │ │ - dataLayersDiv: null, │ │ │ │ - dataLayers: null, │ │ │ │ - minimizeDiv: null, │ │ │ │ - maximizeDiv: null, │ │ │ │ - ascending: true, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments); │ │ │ │ - this.layerStates = [] │ │ │ │ + read_ol_tileSize: function(layerContext, node) { │ │ │ │ + var obj = { │ │ │ │ + width: node.getAttribute("width"), │ │ │ │ + height: node.getAttribute("height") │ │ │ │ + }; │ │ │ │ + layerContext.tileSize = obj │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - this.clearLayersArray("base"); │ │ │ │ - this.clearLayersArray("data"); │ │ │ │ - this.map.events.un({ │ │ │ │ - buttonclick: this.onButtonClick, │ │ │ │ - addlayer: this.redraw, │ │ │ │ - changelayer: this.redraw, │ │ │ │ - removelayer: this.redraw, │ │ │ │ - changebaselayer: this.redraw, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.events.unregister("buttonclick", this, this.onButtonClick); │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ + read_ol_isBaseLayer: function(layerContext, node) { │ │ │ │ + layerContext.isBaseLayer = this.getChildValue(node) == "true" │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments); │ │ │ │ - this.map.events.on({ │ │ │ │ - addlayer: this.redraw, │ │ │ │ - changelayer: this.redraw, │ │ │ │ - removelayer: this.redraw, │ │ │ │ - changebaselayer: this.redraw, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - if (this.outsideViewport) { │ │ │ │ - this.events.attachToElement(this.div); │ │ │ │ - this.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ - } else { │ │ │ │ - this.map.events.register("buttonclick", this, this.onButtonClick) │ │ │ │ - } │ │ │ │ + read_ol_displayInLayerSwitcher: function(layerContext, node) { │ │ │ │ + layerContext.displayInLayerSwitcher = this.getChildValue(node) == "true" │ │ │ │ }, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this); │ │ │ │ - this.loadContents(); │ │ │ │ - if (!this.outsideViewport) { │ │ │ │ - this.minimizeControl() │ │ │ │ - } │ │ │ │ - this.redraw(); │ │ │ │ - return this.div │ │ │ │ + read_wmc_Server: function(layerContext, node) { │ │ │ │ + layerContext.version = node.getAttribute("version"); │ │ │ │ + layerContext.url = this.getOnlineResource_href(node); │ │ │ │ + layerContext.metadata.servertitle = node.getAttribute("title") │ │ │ │ }, │ │ │ │ - onButtonClick: function(evt) { │ │ │ │ - var button = evt.buttonElement; │ │ │ │ - if (button === this.minimizeDiv) { │ │ │ │ - this.minimizeControl() │ │ │ │ - } else if (button === this.maximizeDiv) { │ │ │ │ - this.maximizeControl() │ │ │ │ - } else if (button._layerSwitcher === this.id) { │ │ │ │ - if (button["for"]) { │ │ │ │ - button = document.getElementById(button["for"]) │ │ │ │ - } │ │ │ │ - if (!button.disabled) { │ │ │ │ - if (button.type == "radio") { │ │ │ │ - button.checked = true; │ │ │ │ - this.map.setBaseLayer(this.map.getLayer(button._layer)) │ │ │ │ - } else { │ │ │ │ - button.checked = !button.checked; │ │ │ │ - this.updateMap() │ │ │ │ - } │ │ │ │ - } │ │ │ │ + read_wmc_FormatList: function(layerContext, node) { │ │ │ │ + this.runChildNodes(layerContext, node) │ │ │ │ + }, │ │ │ │ + read_wmc_Format: function(layerContext, node) { │ │ │ │ + var format = { │ │ │ │ + value: this.getChildValue(node) │ │ │ │ + }; │ │ │ │ + if (node.getAttribute("current") == "1") { │ │ │ │ + format.current = true │ │ │ │ } │ │ │ │ + layerContext.formats.push(format) │ │ │ │ }, │ │ │ │ - clearLayersArray: function(layersType) { │ │ │ │ - this[layersType + "LayersDiv"].innerHTML = ""; │ │ │ │ - this[layersType + "Layers"] = [] │ │ │ │ + read_wmc_StyleList: function(layerContext, node) { │ │ │ │ + this.runChildNodes(layerContext, node) │ │ │ │ }, │ │ │ │ - checkRedraw: function() { │ │ │ │ - if (!this.layerStates.length || this.map.layers.length != this.layerStates.length) { │ │ │ │ - return true │ │ │ │ - } │ │ │ │ - for (var i = 0, len = this.layerStates.length; i < len; i++) { │ │ │ │ - var layerState = this.layerStates[i]; │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - if (layerState.name != layer.name || layerState.inRange != layer.inRange || layerState.id != layer.id || layerState.visibility != layer.visibility) { │ │ │ │ - return true │ │ │ │ - } │ │ │ │ + read_wmc_Style: function(layerContext, node) { │ │ │ │ + var style = {}; │ │ │ │ + this.runChildNodes(style, node); │ │ │ │ + if (node.getAttribute("current") == "1") { │ │ │ │ + style.current = true │ │ │ │ } │ │ │ │ - return false │ │ │ │ + layerContext.styles.push(style) │ │ │ │ }, │ │ │ │ - redraw: function() { │ │ │ │ - if (!this.checkRedraw()) { │ │ │ │ - return this.div │ │ │ │ - } │ │ │ │ - this.clearLayersArray("base"); │ │ │ │ - this.clearLayersArray("data"); │ │ │ │ - var containsOverlays = false; │ │ │ │ - var containsBaseLayers = false; │ │ │ │ - var len = this.map.layers.length; │ │ │ │ - this.layerStates = new Array(len); │ │ │ │ - for (var i = 0; i < len; i++) { │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - this.layerStates[i] = { │ │ │ │ - name: layer.name, │ │ │ │ - visibility: layer.visibility, │ │ │ │ - inRange: layer.inRange, │ │ │ │ - id: layer.id │ │ │ │ - } │ │ │ │ - } │ │ │ │ - var layers = this.map.layers.slice(); │ │ │ │ - if (!this.ascending) { │ │ │ │ - layers.reverse() │ │ │ │ - } │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - var layer = layers[i]; │ │ │ │ - var baseLayer = layer.isBaseLayer; │ │ │ │ - if (layer.displayInLayerSwitcher) { │ │ │ │ - if (baseLayer) { │ │ │ │ - containsBaseLayers = true │ │ │ │ - } else { │ │ │ │ - containsOverlays = true │ │ │ │ - } │ │ │ │ - var checked = baseLayer ? layer == this.map.baseLayer : layer.getVisibility(); │ │ │ │ - var inputElem = document.createElement("input"), │ │ │ │ - inputId = OpenLayers.Util.createUniqueID(this.id + "_input_"); │ │ │ │ - inputElem.id = inputId; │ │ │ │ - inputElem.name = baseLayer ? this.id + "_baseLayers" : layer.name; │ │ │ │ - inputElem.type = baseLayer ? "radio" : "checkbox"; │ │ │ │ - inputElem.value = layer.name; │ │ │ │ - inputElem.checked = checked; │ │ │ │ - inputElem.defaultChecked = checked; │ │ │ │ - inputElem.className = "olButton"; │ │ │ │ - inputElem._layer = layer.id; │ │ │ │ - inputElem._layerSwitcher = this.id; │ │ │ │ - if (!baseLayer && !layer.inRange) { │ │ │ │ - inputElem.disabled = true │ │ │ │ - } │ │ │ │ - var labelSpan = document.createElement("label"); │ │ │ │ - labelSpan["for"] = inputElem.id; │ │ │ │ - OpenLayers.Element.addClass(labelSpan, "labelSpan olButton"); │ │ │ │ - labelSpan._layer = layer.id; │ │ │ │ - labelSpan._layerSwitcher = this.id; │ │ │ │ - if (!baseLayer && !layer.inRange) { │ │ │ │ - labelSpan.style.color = "gray" │ │ │ │ - } │ │ │ │ - labelSpan.innerHTML = layer.name; │ │ │ │ - labelSpan.style.verticalAlign = baseLayer ? "bottom" : "baseline"; │ │ │ │ - var br = document.createElement("br"); │ │ │ │ - var groupArray = baseLayer ? this.baseLayers : this.dataLayers; │ │ │ │ - groupArray.push({ │ │ │ │ - layer: layer, │ │ │ │ - inputElem: inputElem, │ │ │ │ - labelSpan: labelSpan │ │ │ │ - }); │ │ │ │ - var groupDiv = baseLayer ? this.baseLayersDiv : this.dataLayersDiv; │ │ │ │ - groupDiv.appendChild(inputElem); │ │ │ │ - groupDiv.appendChild(labelSpan); │ │ │ │ - groupDiv.appendChild(br) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.dataLbl.style.display = containsOverlays ? "" : "none"; │ │ │ │ - this.baseLbl.style.display = containsBaseLayers ? "" : "none"; │ │ │ │ - return this.div │ │ │ │ + read_wmc_SLD: function(style, node) { │ │ │ │ + this.runChildNodes(style, node) │ │ │ │ }, │ │ │ │ - updateMap: function() { │ │ │ │ - for (var i = 0, len = this.baseLayers.length; i < len; i++) { │ │ │ │ - var layerEntry = this.baseLayers[i]; │ │ │ │ - if (layerEntry.inputElem.checked) { │ │ │ │ - this.map.setBaseLayer(layerEntry.layer, false) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - for (var i = 0, len = this.dataLayers.length; i < len; i++) { │ │ │ │ - var layerEntry = this.dataLayers[i]; │ │ │ │ - layerEntry.layer.setVisibility(layerEntry.inputElem.checked) │ │ │ │ - } │ │ │ │ + read_sld_StyledLayerDescriptor: function(sld, node) { │ │ │ │ + var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ + sld.body = xml │ │ │ │ }, │ │ │ │ - maximizeControl: function(e) { │ │ │ │ - this.div.style.width = ""; │ │ │ │ - this.div.style.height = ""; │ │ │ │ - this.showControls(false); │ │ │ │ - if (e != null) { │ │ │ │ - OpenLayers.Event.stop(e) │ │ │ │ + read_sld_FeatureTypeStyle: function(sld, node) { │ │ │ │ + var xml = OpenLayers.Format.XML.prototype.write.apply(this, [node]); │ │ │ │ + sld.body = xml │ │ │ │ + }, │ │ │ │ + read_wmc_OnlineResource: function(obj, node) { │ │ │ │ + obj.href = this.getAttributeNS(node, this.namespaces.xlink, "href") │ │ │ │ + }, │ │ │ │ + read_wmc_Name: function(obj, node) { │ │ │ │ + var name = this.getChildValue(node); │ │ │ │ + if (name) { │ │ │ │ + obj.name = name │ │ │ │ } │ │ │ │ }, │ │ │ │ - minimizeControl: function(e) { │ │ │ │ - this.div.style.width = "0px"; │ │ │ │ - this.div.style.height = "0px"; │ │ │ │ - this.showControls(true); │ │ │ │ - if (e != null) { │ │ │ │ - OpenLayers.Event.stop(e) │ │ │ │ + read_wmc_Title: function(obj, node) { │ │ │ │ + var title = this.getChildValue(node); │ │ │ │ + if (title) { │ │ │ │ + obj.title = title │ │ │ │ } │ │ │ │ }, │ │ │ │ - showControls: function(minimize) { │ │ │ │ - this.maximizeDiv.style.display = minimize ? "" : "none"; │ │ │ │ - this.minimizeDiv.style.display = minimize ? "none" : ""; │ │ │ │ - this.layersDiv.style.display = minimize ? "none" : "" │ │ │ │ + read_wmc_MetadataURL: function(layerContext, node) { │ │ │ │ + layerContext.metadataURL = this.getOnlineResource_href(node) │ │ │ │ }, │ │ │ │ - loadContents: function() { │ │ │ │ - this.layersDiv = document.createElement("div"); │ │ │ │ - this.layersDiv.id = this.id + "_layersDiv"; │ │ │ │ - OpenLayers.Element.addClass(this.layersDiv, "layersDiv"); │ │ │ │ - this.baseLbl = document.createElement("div"); │ │ │ │ - this.baseLbl.innerHTML = OpenLayers.i18n("Base Layer"); │ │ │ │ - OpenLayers.Element.addClass(this.baseLbl, "baseLbl"); │ │ │ │ - this.baseLayersDiv = document.createElement("div"); │ │ │ │ - OpenLayers.Element.addClass(this.baseLayersDiv, "baseLayersDiv"); │ │ │ │ - this.dataLbl = document.createElement("div"); │ │ │ │ - this.dataLbl.innerHTML = OpenLayers.i18n("Overlays"); │ │ │ │ - OpenLayers.Element.addClass(this.dataLbl, "dataLbl"); │ │ │ │ - this.dataLayersDiv = document.createElement("div"); │ │ │ │ - OpenLayers.Element.addClass(this.dataLayersDiv, "dataLayersDiv"); │ │ │ │ - if (this.ascending) { │ │ │ │ - this.layersDiv.appendChild(this.baseLbl); │ │ │ │ - this.layersDiv.appendChild(this.baseLayersDiv); │ │ │ │ - this.layersDiv.appendChild(this.dataLbl); │ │ │ │ - this.layersDiv.appendChild(this.dataLayersDiv) │ │ │ │ - } else { │ │ │ │ - this.layersDiv.appendChild(this.dataLbl); │ │ │ │ - this.layersDiv.appendChild(this.dataLayersDiv); │ │ │ │ - this.layersDiv.appendChild(this.baseLbl); │ │ │ │ - this.layersDiv.appendChild(this.baseLayersDiv) │ │ │ │ - } │ │ │ │ - this.div.appendChild(this.layersDiv); │ │ │ │ - var img = OpenLayers.Util.getImageLocation("layer-switcher-maximize.png"); │ │ │ │ - this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MaximizeDiv", null, null, img, "absolute"); │ │ │ │ - OpenLayers.Element.addClass(this.maximizeDiv, "maximizeDiv olButton"); │ │ │ │ - this.maximizeDiv.style.display = "none"; │ │ │ │ - this.div.appendChild(this.maximizeDiv); │ │ │ │ - var img = OpenLayers.Util.getImageLocation("layer-switcher-minimize.png"); │ │ │ │ - this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv("OpenLayers_Control_MinimizeDiv", null, null, img, "absolute"); │ │ │ │ - OpenLayers.Element.addClass(this.minimizeDiv, "minimizeDiv olButton"); │ │ │ │ - this.minimizeDiv.style.display = "none"; │ │ │ │ - this.div.appendChild(this.minimizeDiv) │ │ │ │ + read_wmc_KeywordList: function(context, node) { │ │ │ │ + context.keywords = []; │ │ │ │ + this.runChildNodes(context.keywords, node) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.LayerSwitcher" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.ZoomPanel = OpenLayers.Class(OpenLayers.Control.Panel, { │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.Panel.prototype.initialize.apply(this, [options]); │ │ │ │ - this.addControls([new OpenLayers.Control.ZoomIn, new OpenLayers.Control.ZoomToMaxExtent, new OpenLayers.Control.ZoomOut]) │ │ │ │ + read_wmc_Keyword: function(keywords, node) { │ │ │ │ + keywords.push(this.getChildValue(node)) │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.ZoomPanel" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.WMTSGetFeatureInfo = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - hover: false, │ │ │ │ - requestEncoding: "KVP", │ │ │ │ - drillDown: false, │ │ │ │ - maxFeatures: 10, │ │ │ │ - clickCallback: "click", │ │ │ │ - layers: null, │ │ │ │ - queryVisible: true, │ │ │ │ - infoFormat: "text/html", │ │ │ │ - vendorParams: {}, │ │ │ │ - format: null, │ │ │ │ - formatOptions: null, │ │ │ │ - handler: null, │ │ │ │ - hoverRequest: null, │ │ │ │ - pending: 0, │ │ │ │ - initialize: function(options) { │ │ │ │ - options = options || {}; │ │ │ │ - options.handlerOptions = options.handlerOptions || {}; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - if (!this.format) { │ │ │ │ - this.format = new OpenLayers.Format.WMSGetFeatureInfo(options.formatOptions) │ │ │ │ - } │ │ │ │ - if (this.drillDown === true) { │ │ │ │ - this.hover = false │ │ │ │ + read_wmc_Abstract: function(obj, node) { │ │ │ │ + var abst = this.getChildValue(node); │ │ │ │ + if (abst) { │ │ │ │ + obj["abstract"] = abst │ │ │ │ } │ │ │ │ - if (this.hover) { │ │ │ │ - this.handler = new OpenLayers.Handler.Hover(this, { │ │ │ │ - move: this.cancelHover, │ │ │ │ - pause: this.getInfoForHover │ │ │ │ - }, OpenLayers.Util.extend(this.handlerOptions.hover || {}, { │ │ │ │ - delay: 250 │ │ │ │ - })) │ │ │ │ - } else { │ │ │ │ - var callbacks = {}; │ │ │ │ - callbacks[this.clickCallback] = this.getInfoForClick; │ │ │ │ - this.handler = new OpenLayers.Handler.Click(this, callbacks, this.handlerOptions.click || {}) │ │ │ │ + }, │ │ │ │ + read_wmc_LogoURL: function(context, node) { │ │ │ │ + context.logo = { │ │ │ │ + width: node.getAttribute("width"), │ │ │ │ + height: node.getAttribute("height"), │ │ │ │ + format: node.getAttribute("format"), │ │ │ │ + href: this.getOnlineResource_href(node) │ │ │ │ } │ │ │ │ }, │ │ │ │ - getInfoForClick: function(evt) { │ │ │ │ - this.request(evt.xy, {}) │ │ │ │ + read_wmc_DescriptionURL: function(context, node) { │ │ │ │ + context.descriptionURL = this.getOnlineResource_href(node) │ │ │ │ }, │ │ │ │ - getInfoForHover: function(evt) { │ │ │ │ - this.request(evt.xy, { │ │ │ │ - hover: true │ │ │ │ - }) │ │ │ │ + read_wmc_ContactInformation: function(obj, node) { │ │ │ │ + var contact = {}; │ │ │ │ + this.runChildNodes(contact, node); │ │ │ │ + obj.contactInformation = contact │ │ │ │ }, │ │ │ │ - cancelHover: function() { │ │ │ │ - if (this.hoverRequest) { │ │ │ │ - --this.pending; │ │ │ │ - if (this.pending <= 0) { │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ - this.pending = 0 │ │ │ │ - } │ │ │ │ - this.hoverRequest.abort(); │ │ │ │ - this.hoverRequest = null │ │ │ │ - } │ │ │ │ + read_wmc_ContactPersonPrimary: function(contact, node) { │ │ │ │ + var personPrimary = {}; │ │ │ │ + this.runChildNodes(personPrimary, node); │ │ │ │ + contact.personPrimary = personPrimary │ │ │ │ }, │ │ │ │ - findLayers: function() { │ │ │ │ - var candidates = this.layers || this.map.layers; │ │ │ │ - var layers = []; │ │ │ │ - var layer; │ │ │ │ - for (var i = candidates.length - 1; i >= 0; --i) { │ │ │ │ - layer = candidates[i]; │ │ │ │ - if (layer instanceof OpenLayers.Layer.WMTS && layer.requestEncoding === this.requestEncoding && (!this.queryVisible || layer.getVisibility())) { │ │ │ │ - layers.push(layer); │ │ │ │ - if (!this.drillDown || this.hover) { │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ + read_wmc_ContactPerson: function(primaryPerson, node) { │ │ │ │ + var person = this.getChildValue(node); │ │ │ │ + if (person) { │ │ │ │ + primaryPerson.person = person │ │ │ │ } │ │ │ │ - return layers │ │ │ │ }, │ │ │ │ - buildRequestOptions: function(layer, xy) { │ │ │ │ - var loc = this.map.getLonLatFromPixel(xy); │ │ │ │ - var getTileUrl = layer.getURL(new OpenLayers.Bounds(loc.lon, loc.lat, loc.lon, loc.lat)); │ │ │ │ - var params = OpenLayers.Util.getParameters(getTileUrl); │ │ │ │ - var tileInfo = layer.getTileInfo(loc); │ │ │ │ - OpenLayers.Util.extend(params, { │ │ │ │ - service: "WMTS", │ │ │ │ - version: layer.version, │ │ │ │ - request: "GetFeatureInfo", │ │ │ │ - infoFormat: this.infoFormat, │ │ │ │ - i: tileInfo.i, │ │ │ │ - j: tileInfo.j │ │ │ │ - }); │ │ │ │ - OpenLayers.Util.applyDefaults(params, this.vendorParams); │ │ │ │ - return { │ │ │ │ - url: OpenLayers.Util.isArray(layer.url) ? layer.url[0] : layer.url, │ │ │ │ - params: OpenLayers.Util.upperCaseObject(params), │ │ │ │ - callback: function(request) { │ │ │ │ - this.handleResponse(xy, request, layer) │ │ │ │ - }, │ │ │ │ - scope: this │ │ │ │ + read_wmc_ContactOrganization: function(primaryPerson, node) { │ │ │ │ + var organization = this.getChildValue(node); │ │ │ │ + if (organization) { │ │ │ │ + primaryPerson.organization = organization │ │ │ │ } │ │ │ │ }, │ │ │ │ - request: function(xy, options) { │ │ │ │ - options = options || {}; │ │ │ │ - var layers = this.findLayers(); │ │ │ │ - if (layers.length > 0) { │ │ │ │ - var issue, layer; │ │ │ │ - for (var i = 0, len = layers.length; i < len; i++) { │ │ │ │ - layer = layers[i]; │ │ │ │ - issue = this.events.triggerEvent("beforegetfeatureinfo", { │ │ │ │ - xy: xy, │ │ │ │ - layer: layer │ │ │ │ - }); │ │ │ │ - if (issue !== false) { │ │ │ │ - ++this.pending; │ │ │ │ - var requestOptions = this.buildRequestOptions(layer, xy); │ │ │ │ - var request = OpenLayers.Request.GET(requestOptions); │ │ │ │ - if (options.hover === true) { │ │ │ │ - this.hoverRequest = request │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (this.pending > 0) { │ │ │ │ - OpenLayers.Element.addClass(this.map.viewPortDiv, "olCursorWait") │ │ │ │ - } │ │ │ │ + read_wmc_ContactPosition: function(contact, node) { │ │ │ │ + var position = this.getChildValue(node); │ │ │ │ + if (position) { │ │ │ │ + contact.position = position │ │ │ │ } │ │ │ │ }, │ │ │ │ - handleResponse: function(xy, request, layer) { │ │ │ │ - --this.pending; │ │ │ │ - if (this.pending <= 0) { │ │ │ │ - OpenLayers.Element.removeClass(this.map.viewPortDiv, "olCursorWait"); │ │ │ │ - this.pending = 0 │ │ │ │ + read_wmc_ContactAddress: function(contact, node) { │ │ │ │ + var contactAddress = {}; │ │ │ │ + this.runChildNodes(contactAddress, node); │ │ │ │ + contact.contactAddress = contactAddress │ │ │ │ + }, │ │ │ │ + read_wmc_AddressType: function(contactAddress, node) { │ │ │ │ + var type = this.getChildValue(node); │ │ │ │ + if (type) { │ │ │ │ + contactAddress.type = type │ │ │ │ } │ │ │ │ - if (request.status && (request.status < 200 || request.status >= 300)) { │ │ │ │ - this.events.triggerEvent("exception", { │ │ │ │ - xy: xy, │ │ │ │ - request: request, │ │ │ │ - layer: layer │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - var doc = request.responseXML; │ │ │ │ - if (!doc || !doc.documentElement) { │ │ │ │ - doc = request.responseText │ │ │ │ - } │ │ │ │ - var features, except; │ │ │ │ - try { │ │ │ │ - features = this.format.read(doc) │ │ │ │ - } catch (error) { │ │ │ │ - except = true; │ │ │ │ - this.events.triggerEvent("exception", { │ │ │ │ - xy: xy, │ │ │ │ - request: request, │ │ │ │ - error: error, │ │ │ │ - layer: layer │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - if (!except) { │ │ │ │ - this.events.triggerEvent("getfeatureinfo", { │ │ │ │ - text: request.responseText, │ │ │ │ - features: features, │ │ │ │ - request: request, │ │ │ │ - xy: xy, │ │ │ │ - layer: layer │ │ │ │ - }) │ │ │ │ - } │ │ │ │ + }, │ │ │ │ + read_wmc_Address: function(contactAddress, node) { │ │ │ │ + var address = this.getChildValue(node); │ │ │ │ + if (address) { │ │ │ │ + contactAddress.address = address │ │ │ │ } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.WMTSGetFeatureInfo" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Attribution = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - separator: ", ", │ │ │ │ - template: "${layers}", │ │ │ │ - destroy: function() { │ │ │ │ - this.map.events.un({ │ │ │ │ - removelayer: this.updateAttribution, │ │ │ │ - addlayer: this.updateAttribution, │ │ │ │ - changelayer: this.updateAttribution, │ │ │ │ - changebaselayer: this.updateAttribution, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ + read_wmc_City: function(contactAddress, node) { │ │ │ │ + var city = this.getChildValue(node); │ │ │ │ + if (city) { │ │ │ │ + contactAddress.city = city │ │ │ │ + } │ │ │ │ }, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - this.map.events.on({ │ │ │ │ - changebaselayer: this.updateAttribution, │ │ │ │ - changelayer: this.updateAttribution, │ │ │ │ - addlayer: this.updateAttribution, │ │ │ │ - removelayer: this.updateAttribution, │ │ │ │ - scope: this │ │ │ │ - }); │ │ │ │ - this.updateAttribution(); │ │ │ │ - return this.div │ │ │ │ + read_wmc_StateOrProvince: function(contactAddress, node) { │ │ │ │ + var stateOrProvince = this.getChildValue(node); │ │ │ │ + if (stateOrProvince) { │ │ │ │ + contactAddress.stateOrProvince = stateOrProvince │ │ │ │ + } │ │ │ │ }, │ │ │ │ - updateAttribution: function() { │ │ │ │ - var attributions = []; │ │ │ │ - if (this.map && this.map.layers) { │ │ │ │ - for (var i = 0, len = this.map.layers.length; i < len; i++) { │ │ │ │ - var layer = this.map.layers[i]; │ │ │ │ - if (layer.attribution && layer.getVisibility()) { │ │ │ │ - if (OpenLayers.Util.indexOf(attributions, layer.attribution) === -1) { │ │ │ │ - attributions.push(layer.attribution) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.div.innerHTML = OpenLayers.String.format(this.template, { │ │ │ │ - layers: attributions.join(this.separator) │ │ │ │ - }) │ │ │ │ + read_wmc_PostCode: function(contactAddress, node) { │ │ │ │ + var postcode = this.getChildValue(node); │ │ │ │ + if (postcode) { │ │ │ │ + contactAddress.postcode = postcode │ │ │ │ } │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Attribution" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.Split = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - layer: null, │ │ │ │ - source: null, │ │ │ │ - sourceOptions: null, │ │ │ │ - tolerance: null, │ │ │ │ - edge: true, │ │ │ │ - deferDelete: false, │ │ │ │ - mutual: true, │ │ │ │ - targetFilter: null, │ │ │ │ - sourceFilter: null, │ │ │ │ - handler: null, │ │ │ │ - initialize: function(options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - this.options = options || {}; │ │ │ │ - if (this.options.source) { │ │ │ │ - this.setSource(this.options.source) │ │ │ │ + read_wmc_Country: function(contactAddress, node) { │ │ │ │ + var country = this.getChildValue(node); │ │ │ │ + if (country) { │ │ │ │ + contactAddress.country = country │ │ │ │ } │ │ │ │ }, │ │ │ │ - setSource: function(layer) { │ │ │ │ - if (this.active) { │ │ │ │ - this.deactivate(); │ │ │ │ - if (this.handler) { │ │ │ │ - this.handler.destroy(); │ │ │ │ - delete this.handler │ │ │ │ - } │ │ │ │ - this.source = layer; │ │ │ │ - this.activate() │ │ │ │ - } else { │ │ │ │ - this.source = layer │ │ │ │ + read_wmc_ContactVoiceTelephone: function(contact, node) { │ │ │ │ + var phone = this.getChildValue(node); │ │ │ │ + if (phone) { │ │ │ │ + contact.phone = phone │ │ │ │ } │ │ │ │ }, │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Control.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - if (!this.source) { │ │ │ │ - if (!this.handler) { │ │ │ │ - this.handler = new OpenLayers.Handler.Path(this, { │ │ │ │ - done: function(geometry) { │ │ │ │ - this.onSketchComplete({ │ │ │ │ - feature: new OpenLayers.Feature.Vector(geometry) │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - }, { │ │ │ │ - layerOptions: this.sourceOptions │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - this.handler.activate() │ │ │ │ - } else if (this.source.events) { │ │ │ │ - this.source.events.on({ │ │ │ │ - sketchcomplete: this.onSketchComplete, │ │ │ │ - afterfeaturemodified: this.afterFeatureModified, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - } │ │ │ │ + read_wmc_ContactFacsimileTelephone: function(contact, node) { │ │ │ │ + var fax = this.getChildValue(node); │ │ │ │ + if (fax) { │ │ │ │ + contact.fax = fax │ │ │ │ } │ │ │ │ - return activated │ │ │ │ }, │ │ │ │ - deactivate: function() { │ │ │ │ - var deactivated = OpenLayers.Control.prototype.deactivate.call(this); │ │ │ │ - if (deactivated) { │ │ │ │ - if (this.source && this.source.events) { │ │ │ │ - this.source.events.un({ │ │ │ │ - sketchcomplete: this.onSketchComplete, │ │ │ │ - afterfeaturemodified: this.afterFeatureModified, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ - } │ │ │ │ + read_wmc_ContactElectronicMailAddress: function(contact, node) { │ │ │ │ + var email = this.getChildValue(node); │ │ │ │ + if (email) { │ │ │ │ + contact.email = email │ │ │ │ } │ │ │ │ - return deactivated │ │ │ │ }, │ │ │ │ - onSketchComplete: function(event) { │ │ │ │ - this.feature = null; │ │ │ │ - return !this.considerSplit(event.feature) │ │ │ │ + read_wmc_DataURL: function(layerContext, node) { │ │ │ │ + layerContext.dataURL = this.getOnlineResource_href(node) │ │ │ │ }, │ │ │ │ - afterFeatureModified: function(event) { │ │ │ │ - if (event.modified) { │ │ │ │ - var feature = event.feature; │ │ │ │ - if (typeof feature.geometry.split === "function") { │ │ │ │ - this.feature = event.feature; │ │ │ │ - this.considerSplit(event.feature) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + read_wmc_LegendURL: function(style, node) { │ │ │ │ + var legend = { │ │ │ │ + width: node.getAttribute("width"), │ │ │ │ + height: node.getAttribute("height"), │ │ │ │ + format: node.getAttribute("format"), │ │ │ │ + href: this.getOnlineResource_href(node) │ │ │ │ + }; │ │ │ │ + style.legend = legend │ │ │ │ }, │ │ │ │ - removeByGeometry: function(features, geometry) { │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - if (features[i].geometry === geometry) { │ │ │ │ - features.splice(i, 1); │ │ │ │ - break │ │ │ │ - } │ │ │ │ - } │ │ │ │ + read_wmc_DimensionList: function(layerContext, node) { │ │ │ │ + layerContext.dimensions = {}; │ │ │ │ + this.runChildNodes(layerContext.dimensions, node) │ │ │ │ }, │ │ │ │ - isEligible: function(target) { │ │ │ │ - if (!target.geometry) { │ │ │ │ - return false │ │ │ │ - } else { │ │ │ │ - return target.state !== OpenLayers.State.DELETE && typeof target.geometry.split === "function" && this.feature !== target && (!this.targetFilter || this.targetFilter.evaluate(target.attributes)) │ │ │ │ + read_wmc_Dimension: function(dimensions, node) { │ │ │ │ + var name = node.getAttribute("name").toLowerCase(); │ │ │ │ + var dim = { │ │ │ │ + name: name, │ │ │ │ + units: node.getAttribute("units") || "", │ │ │ │ + unitSymbol: node.getAttribute("unitSymbol") || "", │ │ │ │ + userValue: node.getAttribute("userValue") || "", │ │ │ │ + nearestValue: node.getAttribute("nearestValue") === "1", │ │ │ │ + multipleValues: node.getAttribute("multipleValues") === "1", │ │ │ │ + current: node.getAttribute("current") === "1", │ │ │ │ + default: node.getAttribute("default") || "" │ │ │ │ + }; │ │ │ │ + var values = this.getChildValue(node); │ │ │ │ + dim.values = values.split(","); │ │ │ │ + dimensions[dim.name] = dim │ │ │ │ + }, │ │ │ │ + write: function(context, options) { │ │ │ │ + var root = this.createElementDefaultNS("ViewContext"); │ │ │ │ + this.setAttributes(root, { │ │ │ │ + version: this.VERSION, │ │ │ │ + id: options && typeof options.id == "string" ? options.id : OpenLayers.Util.createUniqueID("OpenLayers_Context_") │ │ │ │ + }); │ │ │ │ + this.setAttributeNS(root, this.namespaces.xsi, "xsi:schemaLocation", this.schemaLocation); │ │ │ │ + root.appendChild(this.write_wmc_General(context)); │ │ │ │ + root.appendChild(this.write_wmc_LayerList(context)); │ │ │ │ + return OpenLayers.Format.XML.prototype.write.apply(this, [root]) │ │ │ │ + }, │ │ │ │ + createElementDefaultNS: function(name, childValue, attributes) { │ │ │ │ + var node = this.createElementNS(this.namespaces[this.defaultPrefix], name); │ │ │ │ + if (childValue) { │ │ │ │ + node.appendChild(this.createTextNode(childValue)) │ │ │ │ + } │ │ │ │ + if (attributes) { │ │ │ │ + this.setAttributes(node, attributes) │ │ │ │ } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - considerSplit: function(feature) { │ │ │ │ - var sourceSplit = false; │ │ │ │ - var targetSplit = false; │ │ │ │ - if (!this.sourceFilter || this.sourceFilter.evaluate(feature.attributes)) { │ │ │ │ - var features = this.layer && this.layer.features || []; │ │ │ │ - var target, results, proceed; │ │ │ │ - var additions = [], │ │ │ │ - removals = []; │ │ │ │ - var mutual = this.layer === this.source && this.mutual; │ │ │ │ - var options = { │ │ │ │ - edge: this.edge, │ │ │ │ - tolerance: this.tolerance, │ │ │ │ - mutual: mutual │ │ │ │ - }; │ │ │ │ - var sourceParts = [feature.geometry]; │ │ │ │ - var targetFeature, targetParts; │ │ │ │ - var source, parts; │ │ │ │ - for (var i = 0, len = features.length; i < len; ++i) { │ │ │ │ - targetFeature = features[i]; │ │ │ │ - if (this.isEligible(targetFeature)) { │ │ │ │ - targetParts = [targetFeature.geometry]; │ │ │ │ - for (var j = 0; j < sourceParts.length; ++j) { │ │ │ │ - source = sourceParts[j]; │ │ │ │ - for (var k = 0; k < targetParts.length; ++k) { │ │ │ │ - target = targetParts[k]; │ │ │ │ - if (source.getBounds().intersectsBounds(target.getBounds())) { │ │ │ │ - results = source.split(target, options); │ │ │ │ - if (results) { │ │ │ │ - proceed = this.events.triggerEvent("beforesplit", { │ │ │ │ - source: feature, │ │ │ │ - target: targetFeature │ │ │ │ - }); │ │ │ │ - if (proceed !== false) { │ │ │ │ - if (mutual) { │ │ │ │ - parts = results[0]; │ │ │ │ - if (parts.length > 1) { │ │ │ │ - parts.unshift(j, 1); │ │ │ │ - Array.prototype.splice.apply(sourceParts, parts); │ │ │ │ - j += parts.length - 3 │ │ │ │ - } │ │ │ │ - results = results[1] │ │ │ │ - } │ │ │ │ - if (results.length > 1) { │ │ │ │ - results.unshift(k, 1); │ │ │ │ - Array.prototype.splice.apply(targetParts, results); │ │ │ │ - k += results.length - 3 │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (targetParts && targetParts.length > 1) { │ │ │ │ - this.geomsToFeatures(targetFeature, targetParts); │ │ │ │ - this.events.triggerEvent("split", { │ │ │ │ - original: targetFeature, │ │ │ │ - features: targetParts │ │ │ │ - }); │ │ │ │ - Array.prototype.push.apply(additions, targetParts); │ │ │ │ - removals.push(targetFeature); │ │ │ │ - targetSplit = true │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ - if (sourceParts && sourceParts.length > 1) { │ │ │ │ - this.geomsToFeatures(feature, sourceParts); │ │ │ │ - this.events.triggerEvent("split", { │ │ │ │ - original: feature, │ │ │ │ - features: sourceParts │ │ │ │ - }); │ │ │ │ - Array.prototype.push.apply(additions, sourceParts); │ │ │ │ - removals.push(feature); │ │ │ │ - sourceSplit = true │ │ │ │ - } │ │ │ │ - if (sourceSplit || targetSplit) { │ │ │ │ - if (this.deferDelete) { │ │ │ │ - var feat, destroys = []; │ │ │ │ - for (var i = 0, len = removals.length; i < len; ++i) { │ │ │ │ - feat = removals[i]; │ │ │ │ - if (feat.state === OpenLayers.State.INSERT) { │ │ │ │ - destroys.push(feat) │ │ │ │ - } else { │ │ │ │ - feat.state = OpenLayers.State.DELETE; │ │ │ │ - this.layer.drawFeature(feat) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - this.layer.destroyFeatures(destroys, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - for (var i = 0, len = additions.length; i < len; ++i) { │ │ │ │ - additions[i].state = OpenLayers.State.INSERT │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.layer.destroyFeatures(removals, { │ │ │ │ - silent: true │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - this.layer.addFeatures(additions, { │ │ │ │ - silent: true │ │ │ │ - }); │ │ │ │ - this.events.triggerEvent("aftersplit", { │ │ │ │ - source: feature, │ │ │ │ - features: additions │ │ │ │ - }) │ │ │ │ + setAttributes: function(node, obj) { │ │ │ │ + var value; │ │ │ │ + for (var name in obj) { │ │ │ │ + value = obj[name].toString(); │ │ │ │ + if (value.match(/[A-Z]/)) { │ │ │ │ + this.setAttributeNS(node, null, name, value) │ │ │ │ + } else { │ │ │ │ + node.setAttribute(name, value) │ │ │ │ } │ │ │ │ } │ │ │ │ - return sourceSplit │ │ │ │ }, │ │ │ │ - geomsToFeatures: function(feature, geoms) { │ │ │ │ - var clone = feature.clone(); │ │ │ │ - delete clone.geometry; │ │ │ │ - var newFeature; │ │ │ │ - for (var i = 0, len = geoms.length; i < len; ++i) { │ │ │ │ - newFeature = clone.clone(); │ │ │ │ - newFeature.geometry = geoms[i]; │ │ │ │ - newFeature.state = OpenLayers.State.INSERT; │ │ │ │ - geoms[i] = newFeature │ │ │ │ + write_wmc_General: function(context) { │ │ │ │ + var node = this.createElementDefaultNS("General"); │ │ │ │ + if (context.size) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("Window", null, { │ │ │ │ + width: context.size.w, │ │ │ │ + height: context.size.h │ │ │ │ + })) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.deactivate() │ │ │ │ + var bounds = context.bounds; │ │ │ │ + node.appendChild(this.createElementDefaultNS("BoundingBox", null, { │ │ │ │ + minx: bounds.left.toPrecision(18), │ │ │ │ + miny: bounds.bottom.toPrecision(18), │ │ │ │ + maxx: bounds.right.toPrecision(18), │ │ │ │ + maxy: bounds.top.toPrecision(18), │ │ │ │ + SRS: context.projection │ │ │ │ + })); │ │ │ │ + node.appendChild(this.createElementDefaultNS("Title", context.title)); │ │ │ │ + if (context.keywords) { │ │ │ │ + node.appendChild(this.write_wmc_KeywordList(context.keywords)) │ │ │ │ } │ │ │ │ - OpenLayers.Control.prototype.destroy.call(this) │ │ │ │ + if (context["abstract"]) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("Abstract", context["abstract"])) │ │ │ │ + } │ │ │ │ + if (context.logo) { │ │ │ │ + node.appendChild(this.write_wmc_URLType("LogoURL", context.logo.href, context.logo)) │ │ │ │ + } │ │ │ │ + if (context.descriptionURL) { │ │ │ │ + node.appendChild(this.write_wmc_URLType("DescriptionURL", context.descriptionURL)) │ │ │ │ + } │ │ │ │ + if (context.contactInformation) { │ │ │ │ + node.appendChild(this.write_wmc_ContactInformation(context.contactInformation)) │ │ │ │ + } │ │ │ │ + node.appendChild(this.write_ol_MapExtension(context)); │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.Split" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.KeyboardDefaults = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - autoActivate: true, │ │ │ │ - slideFactor: 75, │ │ │ │ - observeElement: null, │ │ │ │ - draw: function() { │ │ │ │ - var observeElement = this.observeElement || document; │ │ │ │ - this.handler = new OpenLayers.Handler.Keyboard(this, { │ │ │ │ - keydown: this.defaultKeyPress │ │ │ │ - }, { │ │ │ │ - observeElement: observeElement │ │ │ │ - }) │ │ │ │ + write_wmc_KeywordList: function(keywords) { │ │ │ │ + var node = this.createElementDefaultNS("KeywordList"); │ │ │ │ + for (var i = 0, len = keywords.length; i < len; i++) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("Keyword", keywords[i])) │ │ │ │ + } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - defaultKeyPress: function(evt) { │ │ │ │ - var size, handled = true; │ │ │ │ - var target = OpenLayers.Event.element(evt); │ │ │ │ - if (target && (target.tagName == "INPUT" || target.tagName == "TEXTAREA" || target.tagName == "SELECT")) { │ │ │ │ - return │ │ │ │ + write_wmc_ContactInformation: function(contact) { │ │ │ │ + var node = this.createElementDefaultNS("ContactInformation"); │ │ │ │ + if (contact.personPrimary) { │ │ │ │ + node.appendChild(this.write_wmc_ContactPersonPrimary(contact.personPrimary)) │ │ │ │ } │ │ │ │ - switch (evt.keyCode) { │ │ │ │ - case OpenLayers.Event.KEY_LEFT: │ │ │ │ - this.map.pan(-this.slideFactor, 0); │ │ │ │ - break; │ │ │ │ - case OpenLayers.Event.KEY_RIGHT: │ │ │ │ - this.map.pan(this.slideFactor, 0); │ │ │ │ - break; │ │ │ │ - case OpenLayers.Event.KEY_UP: │ │ │ │ - this.map.pan(0, -this.slideFactor); │ │ │ │ - break; │ │ │ │ - case OpenLayers.Event.KEY_DOWN: │ │ │ │ - this.map.pan(0, this.slideFactor); │ │ │ │ - break; │ │ │ │ - case 33: │ │ │ │ - size = this.map.getSize(); │ │ │ │ - this.map.pan(0, -.75 * size.h); │ │ │ │ - break; │ │ │ │ - case 34: │ │ │ │ - size = this.map.getSize(); │ │ │ │ - this.map.pan(0, .75 * size.h); │ │ │ │ - break; │ │ │ │ - case 35: │ │ │ │ - size = this.map.getSize(); │ │ │ │ - this.map.pan(.75 * size.w, 0); │ │ │ │ - break; │ │ │ │ - case 36: │ │ │ │ - size = this.map.getSize(); │ │ │ │ - this.map.pan(-.75 * size.w, 0); │ │ │ │ - break; │ │ │ │ - case 43: │ │ │ │ - case 61: │ │ │ │ - case 187: │ │ │ │ - case 107: │ │ │ │ - this.map.zoomIn(); │ │ │ │ - break; │ │ │ │ - case 45: │ │ │ │ - case 109: │ │ │ │ - case 189: │ │ │ │ - case 95: │ │ │ │ - this.map.zoomOut(); │ │ │ │ - break; │ │ │ │ - default: │ │ │ │ - handled = false │ │ │ │ + if (contact.position) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("ContactPosition", contact.position)) │ │ │ │ } │ │ │ │ - if (handled) { │ │ │ │ - OpenLayers.Event.stop(evt) │ │ │ │ + if (contact.contactAddress) { │ │ │ │ + node.appendChild(this.write_wmc_ContactAddress(contact.contactAddress)) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.KeyboardDefaults" │ │ │ │ -}); │ │ │ │ -OpenLayers.Control.SelectFeature = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - multipleKey: null, │ │ │ │ - toggleKey: null, │ │ │ │ - multiple: false, │ │ │ │ - clickout: true, │ │ │ │ - toggle: false, │ │ │ │ - hover: false, │ │ │ │ - highlightOnly: false, │ │ │ │ - box: false, │ │ │ │ - onBeforeSelect: function() {}, │ │ │ │ - onSelect: function() {}, │ │ │ │ - onUnselect: function() {}, │ │ │ │ - scope: null, │ │ │ │ - geometryTypes: null, │ │ │ │ - layer: null, │ │ │ │ - layers: null, │ │ │ │ - callbacks: null, │ │ │ │ - selectStyle: null, │ │ │ │ - renderIntent: "select", │ │ │ │ - handlers: null, │ │ │ │ - initialize: function(layers, options) { │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, [options]); │ │ │ │ - if (this.scope === null) { │ │ │ │ - this.scope = this │ │ │ │ + if (contact.phone) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("ContactVoiceTelephone", contact.phone)) │ │ │ │ } │ │ │ │ - this.initLayer(layers); │ │ │ │ - var callbacks = { │ │ │ │ - click: this.clickFeature, │ │ │ │ - clickout: this.clickoutFeature │ │ │ │ - }; │ │ │ │ - if (this.hover) { │ │ │ │ - callbacks.over = this.overFeature; │ │ │ │ - callbacks.out = this.outFeature │ │ │ │ + if (contact.fax) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("ContactFacsimileTelephone", contact.fax)) │ │ │ │ } │ │ │ │ - this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks); │ │ │ │ - this.handlers = { │ │ │ │ - feature: new OpenLayers.Handler.Feature(this, this.layer, this.callbacks, { │ │ │ │ - geometryTypes: this.geometryTypes │ │ │ │ - }) │ │ │ │ - }; │ │ │ │ - if (this.box) { │ │ │ │ - this.handlers.box = new OpenLayers.Handler.Box(this, { │ │ │ │ - done: this.selectBox │ │ │ │ - }, { │ │ │ │ - boxDivClassName: "olHandlerBoxSelectFeature" │ │ │ │ - }) │ │ │ │ + if (contact.email) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("ContactElectronicMailAddress", contact.email)) │ │ │ │ } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - initLayer: function(layers) { │ │ │ │ - if (OpenLayers.Util.isArray(layers)) { │ │ │ │ - this.layers = layers; │ │ │ │ - this.layer = new OpenLayers.Layer.Vector.RootContainer(this.id + "_container", { │ │ │ │ - layers: layers │ │ │ │ - }) │ │ │ │ - } else { │ │ │ │ - this.layer = layers │ │ │ │ + write_wmc_ContactPersonPrimary: function(personPrimary) { │ │ │ │ + var node = this.createElementDefaultNS("ContactPersonPrimary"); │ │ │ │ + if (personPrimary.person) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("ContactPerson", personPrimary.person)) │ │ │ │ } │ │ │ │ + if (personPrimary.organization) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("ContactOrganization", personPrimary.organization)) │ │ │ │ + } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - if (this.active && this.layers) { │ │ │ │ - this.map.removeLayer(this.layer) │ │ │ │ + write_wmc_ContactAddress: function(contactAddress) { │ │ │ │ + var node = this.createElementDefaultNS("ContactAddress"); │ │ │ │ + if (contactAddress.type) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("AddressType", contactAddress.type)) │ │ │ │ } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments); │ │ │ │ - if (this.layers) { │ │ │ │ - this.layer.destroy() │ │ │ │ + if (contactAddress.address) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("Address", contactAddress.address)) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - if (!this.active) { │ │ │ │ - if (this.layers) { │ │ │ │ - this.map.addLayer(this.layer) │ │ │ │ - } │ │ │ │ - this.handlers.feature.activate(); │ │ │ │ - if (this.box && this.handlers.box) { │ │ │ │ - this.handlers.box.activate() │ │ │ │ - } │ │ │ │ + if (contactAddress.city) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("City", contactAddress.city)) │ │ │ │ } │ │ │ │ - return OpenLayers.Control.prototype.activate.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (this.active) { │ │ │ │ - this.handlers.feature.deactivate(); │ │ │ │ - if (this.handlers.box) { │ │ │ │ - this.handlers.box.deactivate() │ │ │ │ - } │ │ │ │ - if (this.layers) { │ │ │ │ - this.map.removeLayer(this.layer) │ │ │ │ - } │ │ │ │ + if (contactAddress.stateOrProvince) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("StateOrProvince", contactAddress.stateOrProvince)) │ │ │ │ } │ │ │ │ - return OpenLayers.Control.prototype.deactivate.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - unselectAll: function(options) { │ │ │ │ - var layers = this.layers || [this.layer], │ │ │ │ - layer, feature, l, numExcept; │ │ │ │ - for (l = 0; l < layers.length; ++l) { │ │ │ │ - layer = layers[l]; │ │ │ │ - numExcept = 0; │ │ │ │ - if (layer.selectedFeatures != null) { │ │ │ │ - while (layer.selectedFeatures.length > numExcept) { │ │ │ │ - feature = layer.selectedFeatures[numExcept]; │ │ │ │ - if (!options || options.except != feature) { │ │ │ │ - this.unselect(feature) │ │ │ │ - } else { │ │ │ │ - ++numExcept │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } │ │ │ │ + if (contactAddress.postcode) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("PostCode", contactAddress.postcode)) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - clickFeature: function(feature) { │ │ │ │ - if (!this.hover) { │ │ │ │ - var selected = OpenLayers.Util.indexOf(feature.layer.selectedFeatures, feature) > -1; │ │ │ │ - if (selected) { │ │ │ │ - if (this.toggleSelect()) { │ │ │ │ - this.unselect(feature) │ │ │ │ - } else if (!this.multipleSelect()) { │ │ │ │ - this.unselectAll({ │ │ │ │ - except: feature │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - if (!this.multipleSelect()) { │ │ │ │ - this.unselectAll({ │ │ │ │ - except: feature │ │ │ │ - }) │ │ │ │ - } │ │ │ │ - this.select(feature) │ │ │ │ - } │ │ │ │ + if (contactAddress.country) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("Country", contactAddress.country)) │ │ │ │ } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - multipleSelect: function() { │ │ │ │ - return this.multiple || this.handlers.feature.evt && this.handlers.feature.evt[this.multipleKey] │ │ │ │ + write_ol_MapExtension: function(context) { │ │ │ │ + var node = this.createElementDefaultNS("Extension"); │ │ │ │ + var bounds = context.maxExtent; │ │ │ │ + if (bounds) { │ │ │ │ + var maxExtent = this.createElementNS(this.namespaces.ol, "ol:maxExtent"); │ │ │ │ + this.setAttributes(maxExtent, { │ │ │ │ + minx: bounds.left.toPrecision(18), │ │ │ │ + miny: bounds.bottom.toPrecision(18), │ │ │ │ + maxx: bounds.right.toPrecision(18), │ │ │ │ + maxy: bounds.top.toPrecision(18) │ │ │ │ + }); │ │ │ │ + node.appendChild(maxExtent) │ │ │ │ + } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - toggleSelect: function() { │ │ │ │ - return this.toggle || this.handlers.feature.evt && this.handlers.feature.evt[this.toggleKey] │ │ │ │ + write_wmc_LayerList: function(context) { │ │ │ │ + var list = this.createElementDefaultNS("LayerList"); │ │ │ │ + for (var i = 0, len = context.layersContext.length; i < len; ++i) { │ │ │ │ + list.appendChild(this.write_wmc_Layer(context.layersContext[i])) │ │ │ │ + } │ │ │ │ + return list │ │ │ │ }, │ │ │ │ - clickoutFeature: function(feature) { │ │ │ │ - if (!this.hover && this.clickout) { │ │ │ │ - this.unselectAll() │ │ │ │ + write_wmc_Layer: function(context) { │ │ │ │ + var node = this.createElementDefaultNS("Layer", null, { │ │ │ │ + queryable: context.queryable ? "1" : "0", │ │ │ │ + hidden: context.visibility ? "0" : "1" │ │ │ │ + }); │ │ │ │ + node.appendChild(this.write_wmc_Server(context)); │ │ │ │ + node.appendChild(this.createElementDefaultNS("Name", context.name)); │ │ │ │ + node.appendChild(this.createElementDefaultNS("Title", context.title)); │ │ │ │ + if (context["abstract"]) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("Abstract", context["abstract"])) │ │ │ │ + } │ │ │ │ + if (context.dataURL) { │ │ │ │ + node.appendChild(this.write_wmc_URLType("DataURL", context.dataURL)) │ │ │ │ } │ │ │ │ + if (context.metadataURL) { │ │ │ │ + node.appendChild(this.write_wmc_URLType("MetadataURL", context.metadataURL)) │ │ │ │ + } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - overFeature: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - if (this.hover) { │ │ │ │ - if (this.highlightOnly) { │ │ │ │ - this.highlight(feature) │ │ │ │ - } else if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { │ │ │ │ - this.select(feature) │ │ │ │ + write_wmc_LayerExtension: function(context) { │ │ │ │ + var node = this.createElementDefaultNS("Extension"); │ │ │ │ + var bounds = context.maxExtent; │ │ │ │ + var maxExtent = this.createElementNS(this.namespaces.ol, "ol:maxExtent"); │ │ │ │ + this.setAttributes(maxExtent, { │ │ │ │ + minx: bounds.left.toPrecision(18), │ │ │ │ + miny: bounds.bottom.toPrecision(18), │ │ │ │ + maxx: bounds.right.toPrecision(18), │ │ │ │ + maxy: bounds.top.toPrecision(18) │ │ │ │ + }); │ │ │ │ + node.appendChild(maxExtent); │ │ │ │ + if (context.tileSize && !context.singleTile) { │ │ │ │ + var size = this.createElementNS(this.namespaces.ol, "ol:tileSize"); │ │ │ │ + this.setAttributes(size, context.tileSize); │ │ │ │ + node.appendChild(size) │ │ │ │ + } │ │ │ │ + var properties = ["transparent", "numZoomLevels", "units", "isBaseLayer", "opacity", "displayInLayerSwitcher", "singleTile"]; │ │ │ │ + var child; │ │ │ │ + for (var i = 0, len = properties.length; i < len; ++i) { │ │ │ │ + child = this.createOLPropertyNode(context, properties[i]); │ │ │ │ + if (child) { │ │ │ │ + node.appendChild(child) │ │ │ │ } │ │ │ │ } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - outFeature: function(feature) { │ │ │ │ - if (this.hover) { │ │ │ │ - if (this.highlightOnly) { │ │ │ │ - if (feature._lastHighlighter == this.id) { │ │ │ │ - if (feature._prevHighlighter && feature._prevHighlighter != this.id) { │ │ │ │ - delete feature._lastHighlighter; │ │ │ │ - var control = this.map.getControl(feature._prevHighlighter); │ │ │ │ - if (control) { │ │ │ │ - control.highlight(feature) │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.unhighlight(feature) │ │ │ │ - } │ │ │ │ - } │ │ │ │ - } else { │ │ │ │ - this.unselect(feature) │ │ │ │ - } │ │ │ │ + createOLPropertyNode: function(obj, prop) { │ │ │ │ + var node = null; │ │ │ │ + if (obj[prop] != null) { │ │ │ │ + node = this.createElementNS(this.namespaces.ol, "ol:" + prop); │ │ │ │ + node.appendChild(this.createTextNode(obj[prop].toString())) │ │ │ │ } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - highlight: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - var cont = this.events.triggerEvent("beforefeaturehighlighted", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (cont !== false) { │ │ │ │ - feature._prevHighlighter = feature._lastHighlighter; │ │ │ │ - feature._lastHighlighter = this.id; │ │ │ │ - var style = this.selectStyle || this.renderIntent; │ │ │ │ - layer.drawFeature(feature, style); │ │ │ │ - this.events.triggerEvent("featurehighlighted", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ + write_wmc_Server: function(context) { │ │ │ │ + var server = context.server; │ │ │ │ + var node = this.createElementDefaultNS("Server"); │ │ │ │ + var attributes = { │ │ │ │ + service: "OGC:WMS", │ │ │ │ + version: server.version │ │ │ │ + }; │ │ │ │ + if (server.title) { │ │ │ │ + attributes.title = server.title │ │ │ │ } │ │ │ │ + this.setAttributes(node, attributes); │ │ │ │ + node.appendChild(this.write_wmc_OnlineResource(server.url)); │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - unhighlight: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - if (feature._prevHighlighter == undefined) { │ │ │ │ - delete feature._lastHighlighter │ │ │ │ - } else if (feature._prevHighlighter == this.id) { │ │ │ │ - delete feature._prevHighlighter │ │ │ │ - } else { │ │ │ │ - feature._lastHighlighter = feature._prevHighlighter; │ │ │ │ - delete feature._prevHighlighter │ │ │ │ + write_wmc_URLType: function(elName, url, attr) { │ │ │ │ + var node = this.createElementDefaultNS(elName); │ │ │ │ + node.appendChild(this.write_wmc_OnlineResource(url)); │ │ │ │ + if (attr) { │ │ │ │ + var optionalAttributes = ["width", "height", "format"]; │ │ │ │ + for (var i = 0; i < optionalAttributes.length; i++) { │ │ │ │ + if (optionalAttributes[i] in attr) { │ │ │ │ + node.setAttribute(optionalAttributes[i], attr[optionalAttributes[i]]) │ │ │ │ + } │ │ │ │ + } │ │ │ │ } │ │ │ │ - layer.drawFeature(feature, feature.style || feature.layer.style || "default"); │ │ │ │ - this.events.triggerEvent("featureunhighlighted", { │ │ │ │ - feature: feature │ │ │ │ - }) │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - select: function(feature) { │ │ │ │ - var cont = this.onBeforeSelect.call(this.scope, feature); │ │ │ │ - var layer = feature.layer; │ │ │ │ - if (cont !== false) { │ │ │ │ - cont = layer.events.triggerEvent("beforefeatureselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - if (cont !== false) { │ │ │ │ - layer.selectedFeatures.push(feature); │ │ │ │ - this.highlight(feature); │ │ │ │ - if (!this.handlers.feature.lastFeature) { │ │ │ │ - this.handlers.feature.lastFeature = layer.selectedFeatures[0] │ │ │ │ + write_wmc_DimensionList: function(context) { │ │ │ │ + var node = this.createElementDefaultNS("DimensionList"); │ │ │ │ + var required_attributes = { │ │ │ │ + name: true, │ │ │ │ + units: true, │ │ │ │ + unitSymbol: true, │ │ │ │ + userValue: true │ │ │ │ + }; │ │ │ │ + for (var dim in context.dimensions) { │ │ │ │ + var attributes = {}; │ │ │ │ + var dimension = context.dimensions[dim]; │ │ │ │ + for (var name in dimension) { │ │ │ │ + if (typeof dimension[name] == "boolean") { │ │ │ │ + attributes[name] = Number(dimension[name]) │ │ │ │ + } else { │ │ │ │ + attributes[name] = dimension[name] │ │ │ │ } │ │ │ │ - layer.events.triggerEvent("featureselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.onSelect.call(this.scope, feature) │ │ │ │ } │ │ │ │ + var values = ""; │ │ │ │ + if (attributes.values) { │ │ │ │ + values = attributes.values.join(","); │ │ │ │ + delete attributes.values │ │ │ │ + } │ │ │ │ + node.appendChild(this.createElementDefaultNS("Dimension", values, attributes)) │ │ │ │ } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - unselect: function(feature) { │ │ │ │ - var layer = feature.layer; │ │ │ │ - this.unhighlight(feature); │ │ │ │ - OpenLayers.Util.removeItem(layer.selectedFeatures, feature); │ │ │ │ - layer.events.triggerEvent("featureunselected", { │ │ │ │ - feature: feature │ │ │ │ - }); │ │ │ │ - this.onUnselect.call(this.scope, feature) │ │ │ │ + write_wmc_FormatList: function(context) { │ │ │ │ + var node = this.createElementDefaultNS("FormatList"); │ │ │ │ + for (var i = 0, len = context.formats.length; i < len; i++) { │ │ │ │ + var format = context.formats[i]; │ │ │ │ + node.appendChild(this.createElementDefaultNS("Format", format.value, format.current && format.current == true ? { │ │ │ │ + current: "1" │ │ │ │ + } : null)) │ │ │ │ + } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - selectBox: function(position) { │ │ │ │ - if (position instanceof OpenLayers.Bounds) { │ │ │ │ - var minXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.left, │ │ │ │ - y: position.bottom │ │ │ │ - }); │ │ │ │ - var maxXY = this.map.getLonLatFromPixel({ │ │ │ │ - x: position.right, │ │ │ │ - y: position.top │ │ │ │ - }); │ │ │ │ - var bounds = new OpenLayers.Bounds(minXY.lon, minXY.lat, maxXY.lon, maxXY.lat); │ │ │ │ - if (!this.multipleSelect()) { │ │ │ │ - this.unselectAll() │ │ │ │ - } │ │ │ │ - var prevMultiple = this.multiple; │ │ │ │ - this.multiple = true; │ │ │ │ - var layers = this.layers || [this.layer]; │ │ │ │ - this.events.triggerEvent("boxselectionstart", { │ │ │ │ - layers: layers │ │ │ │ - }); │ │ │ │ - var layer; │ │ │ │ - for (var l = 0; l < layers.length; ++l) { │ │ │ │ - layer = layers[l]; │ │ │ │ - for (var i = 0, len = layer.features.length; i < len; ++i) { │ │ │ │ - var feature = layer.features[i]; │ │ │ │ - if (!feature.getVisibility()) { │ │ │ │ - continue │ │ │ │ + write_wmc_StyleList: function(layer) { │ │ │ │ + var node = this.createElementDefaultNS("StyleList"); │ │ │ │ + var styles = layer.styles; │ │ │ │ + if (styles && OpenLayers.Util.isArray(styles)) { │ │ │ │ + var sld; │ │ │ │ + for (var i = 0, len = styles.length; i < len; i++) { │ │ │ │ + var s = styles[i]; │ │ │ │ + var style = this.createElementDefaultNS("Style", null, s.current && s.current == true ? { │ │ │ │ + current: "1" │ │ │ │ + } : null); │ │ │ │ + if (s.href) { │ │ │ │ + sld = this.createElementDefaultNS("SLD"); │ │ │ │ + if (s.name) { │ │ │ │ + sld.appendChild(this.createElementDefaultNS("Name", s.name)) │ │ │ │ } │ │ │ │ - if (this.geometryTypes == null || OpenLayers.Util.indexOf(this.geometryTypes, feature.geometry.CLASS_NAME) > -1) { │ │ │ │ - if (bounds.toGeometry().intersects(feature.geometry)) { │ │ │ │ - if (OpenLayers.Util.indexOf(layer.selectedFeatures, feature) == -1) { │ │ │ │ - this.select(feature) │ │ │ │ - } │ │ │ │ - } │ │ │ │ + if (s.title) { │ │ │ │ + sld.appendChild(this.createElementDefaultNS("Title", s.title)) │ │ │ │ + } │ │ │ │ + if (s.legend) { │ │ │ │ + sld.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend)) │ │ │ │ + } │ │ │ │ + var link = this.write_wmc_OnlineResource(s.href); │ │ │ │ + sld.appendChild(link); │ │ │ │ + style.appendChild(sld) │ │ │ │ + } else if (s.body) { │ │ │ │ + sld = this.createElementDefaultNS("SLD"); │ │ │ │ + if (s.name) { │ │ │ │ + sld.appendChild(this.createElementDefaultNS("Name", s.name)) │ │ │ │ + } │ │ │ │ + if (s.title) { │ │ │ │ + sld.appendChild(this.createElementDefaultNS("Title", s.title)) │ │ │ │ + } │ │ │ │ + if (s.legend) { │ │ │ │ + sld.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend)) │ │ │ │ + } │ │ │ │ + var doc = OpenLayers.Format.XML.prototype.read.apply(this, [s.body]); │ │ │ │ + var imported = doc.documentElement; │ │ │ │ + if (sld.ownerDocument && sld.ownerDocument.importNode) { │ │ │ │ + imported = sld.ownerDocument.importNode(imported, true) │ │ │ │ + } │ │ │ │ + sld.appendChild(imported); │ │ │ │ + style.appendChild(sld) │ │ │ │ + } else { │ │ │ │ + style.appendChild(this.createElementDefaultNS("Name", s.name)); │ │ │ │ + style.appendChild(this.createElementDefaultNS("Title", s.title)); │ │ │ │ + if (s["abstract"]) { │ │ │ │ + style.appendChild(this.createElementDefaultNS("Abstract", s["abstract"])) │ │ │ │ + } │ │ │ │ + if (s.legend) { │ │ │ │ + style.appendChild(this.write_wmc_URLType("LegendURL", s.legend.href, s.legend)) │ │ │ │ } │ │ │ │ } │ │ │ │ + node.appendChild(style) │ │ │ │ } │ │ │ │ - this.multiple = prevMultiple; │ │ │ │ - this.events.triggerEvent("boxselectionend", { │ │ │ │ - layers: layers │ │ │ │ - }) │ │ │ │ } │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - setMap: function(map) { │ │ │ │ - this.handlers.feature.setMap(map); │ │ │ │ - if (this.box) { │ │ │ │ - this.handlers.box.setMap(map) │ │ │ │ - } │ │ │ │ - OpenLayers.Control.prototype.setMap.apply(this, arguments) │ │ │ │ + write_wmc_OnlineResource: function(href) { │ │ │ │ + var node = this.createElementDefaultNS("OnlineResource"); │ │ │ │ + this.setAttributeNS(node, this.namespaces.xlink, "xlink:type", "simple"); │ │ │ │ + this.setAttributeNS(node, this.namespaces.xlink, "xlink:href", href); │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - setLayer: function(layers) { │ │ │ │ - var isActive = this.active; │ │ │ │ - this.unselectAll(); │ │ │ │ - this.deactivate(); │ │ │ │ - if (this.layers) { │ │ │ │ - this.layer.destroy(); │ │ │ │ - this.layers = null │ │ │ │ - } │ │ │ │ - this.initLayer(layers); │ │ │ │ - this.handlers.feature.layer = this.layer; │ │ │ │ - if (isActive) { │ │ │ │ - this.activate() │ │ │ │ + getOnlineResource_href: function(node) { │ │ │ │ + var object = {}; │ │ │ │ + var links = node.getElementsByTagName("OnlineResource"); │ │ │ │ + if (links.length > 0) { │ │ │ │ + this.read_wmc_OnlineResource(object, links[0]) │ │ │ │ } │ │ │ │ + return object.href │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.SelectFeature" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMC.v1" │ │ │ │ }); │ │ │ │ -OpenLayers.Control.MousePosition = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - autoActivate: true, │ │ │ │ - element: null, │ │ │ │ - prefix: "", │ │ │ │ - separator: ", ", │ │ │ │ - suffix: "", │ │ │ │ - numDigits: 5, │ │ │ │ - granularity: 10, │ │ │ │ - emptyString: null, │ │ │ │ - lastXy: null, │ │ │ │ - displayProjection: null, │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ - }, │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.map.events.register("mousemove", this, this.redraw); │ │ │ │ - this.map.events.register("mouseout", this, this.reset); │ │ │ │ - this.redraw(); │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ - } │ │ │ │ +OpenLayers.Format.WMC.v1_0_0 = OpenLayers.Class(OpenLayers.Format.WMC.v1, { │ │ │ │ + VERSION: "1.0.0", │ │ │ │ + schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.0.0/context.xsd", │ │ │ │ + initialize: function(options) { │ │ │ │ + OpenLayers.Format.WMC.v1.prototype.initialize.apply(this, [options]) │ │ │ │ }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.map.events.unregister("mousemove", this, this.redraw); │ │ │ │ - this.map.events.unregister("mouseout", this, this.reset); │ │ │ │ - this.element.innerHTML = ""; │ │ │ │ - return true │ │ │ │ - } else { │ │ │ │ - return false │ │ │ │ + read_wmc_SRS: function(layerContext, node) { │ │ │ │ + var srs = this.getChildValue(node); │ │ │ │ + if (typeof layerContext.projections != "object") { │ │ │ │ + layerContext.projections = {} │ │ │ │ } │ │ │ │ - }, │ │ │ │ - draw: function() { │ │ │ │ - OpenLayers.Control.prototype.draw.apply(this, arguments); │ │ │ │ - if (!this.element) { │ │ │ │ - this.div.left = ""; │ │ │ │ - this.div.top = ""; │ │ │ │ - this.element = this.div │ │ │ │ + var values = srs.split(/ +/); │ │ │ │ + for (var i = 0, len = values.length; i < len; i++) { │ │ │ │ + layerContext.projections[values[i]] = true │ │ │ │ } │ │ │ │ - return this.div │ │ │ │ }, │ │ │ │ - redraw: function(evt) { │ │ │ │ - var lonLat; │ │ │ │ - if (evt == null) { │ │ │ │ - this.reset(); │ │ │ │ - return │ │ │ │ - } else { │ │ │ │ - if (this.lastXy == null || Math.abs(evt.xy.x - this.lastXy.x) > this.granularity || Math.abs(evt.xy.y - this.lastXy.y) > this.granularity) { │ │ │ │ - this.lastXy = evt.xy; │ │ │ │ - return │ │ │ │ - } │ │ │ │ - lonLat = this.map.getLonLatFromPixel(evt.xy); │ │ │ │ - if (!lonLat) { │ │ │ │ - return │ │ │ │ - } │ │ │ │ - if (this.displayProjection) { │ │ │ │ - lonLat.transform(this.map.getProjectionObject(), this.displayProjection) │ │ │ │ + write_wmc_Layer: function(context) { │ │ │ │ + var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply(this, [context]); │ │ │ │ + if (context.srs) { │ │ │ │ + var projections = []; │ │ │ │ + for (var name in context.srs) { │ │ │ │ + projections.push(name) │ │ │ │ } │ │ │ │ - this.lastXy = evt.xy │ │ │ │ - } │ │ │ │ - var newHtml = this.formatOutput(lonLat); │ │ │ │ - if (newHtml != this.element.innerHTML) { │ │ │ │ - this.element.innerHTML = newHtml │ │ │ │ + node.appendChild(this.createElementDefaultNS("SRS", projections.join(" "))) │ │ │ │ } │ │ │ │ - }, │ │ │ │ - reset: function(evt) { │ │ │ │ - if (this.emptyString != null) { │ │ │ │ - this.element.innerHTML = this.emptyString │ │ │ │ + node.appendChild(this.write_wmc_FormatList(context)); │ │ │ │ + node.appendChild(this.write_wmc_StyleList(context)); │ │ │ │ + if (context.dimensions) { │ │ │ │ + node.appendChild(this.write_wmc_DimensionList(context)) │ │ │ │ } │ │ │ │ + node.appendChild(this.write_wmc_LayerExtension(context)) │ │ │ │ }, │ │ │ │ - formatOutput: function(lonLat) { │ │ │ │ - var digits = parseInt(this.numDigits); │ │ │ │ - var newHtml = this.prefix + lonLat.lon.toFixed(digits) + this.separator + lonLat.lat.toFixed(digits) + this.suffix; │ │ │ │ - return newHtml │ │ │ │ - }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.MousePosition" │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMC.v1_0_0" │ │ │ │ }); │ │ │ │ -OpenLayers.Control.TouchNavigation = OpenLayers.Class(OpenLayers.Control, { │ │ │ │ - dragPan: null, │ │ │ │ - dragPanOptions: null, │ │ │ │ - pinchZoom: null, │ │ │ │ - pinchZoomOptions: null, │ │ │ │ - clickHandlerOptions: null, │ │ │ │ - documentDrag: false, │ │ │ │ - autoActivate: true, │ │ │ │ +OpenLayers.Format.WMC.v1_1_0 = OpenLayers.Class(OpenLayers.Format.WMC.v1, { │ │ │ │ + VERSION: "1.1.0", │ │ │ │ + schemaLocation: "http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd", │ │ │ │ initialize: function(options) { │ │ │ │ - this.handlers = {}; │ │ │ │ - OpenLayers.Control.prototype.initialize.apply(this, arguments) │ │ │ │ + OpenLayers.Format.WMC.v1.prototype.initialize.apply(this, [options]) │ │ │ │ }, │ │ │ │ - destroy: function() { │ │ │ │ - this.deactivate(); │ │ │ │ - if (this.dragPan) { │ │ │ │ - this.dragPan.destroy() │ │ │ │ - } │ │ │ │ - this.dragPan = null; │ │ │ │ - if (this.pinchZoom) { │ │ │ │ - this.pinchZoom.destroy(); │ │ │ │ - delete this.pinchZoom │ │ │ │ + read_sld_MinScaleDenominator: function(layerContext, node) { │ │ │ │ + var minScaleDenominator = parseFloat(this.getChildValue(node)); │ │ │ │ + if (minScaleDenominator > 0) { │ │ │ │ + layerContext.maxScale = minScaleDenominator │ │ │ │ } │ │ │ │ - OpenLayers.Control.prototype.destroy.apply(this, arguments) │ │ │ │ }, │ │ │ │ - activate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { │ │ │ │ - this.dragPan.activate(); │ │ │ │ - this.handlers.click.activate(); │ │ │ │ - this.pinchZoom.activate(); │ │ │ │ - return true │ │ │ │ + read_sld_MaxScaleDenominator: function(layerContext, node) { │ │ │ │ + layerContext.minScale = parseFloat(this.getChildValue(node)) │ │ │ │ + }, │ │ │ │ + read_wmc_SRS: function(layerContext, node) { │ │ │ │ + if (!("srs" in layerContext)) { │ │ │ │ + layerContext.srs = {} │ │ │ │ } │ │ │ │ - return false │ │ │ │ + layerContext.srs[this.getChildValue(node)] = true │ │ │ │ }, │ │ │ │ - deactivate: function() { │ │ │ │ - if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { │ │ │ │ - this.dragPan.deactivate(); │ │ │ │ - this.handlers.click.deactivate(); │ │ │ │ - this.pinchZoom.deactivate(); │ │ │ │ - return true │ │ │ │ + write_wmc_Layer: function(context) { │ │ │ │ + var node = OpenLayers.Format.WMC.v1.prototype.write_wmc_Layer.apply(this, [context]); │ │ │ │ + if (context.maxScale) { │ │ │ │ + var minSD = this.createElementNS(this.namespaces.sld, "sld:MinScaleDenominator"); │ │ │ │ + minSD.appendChild(this.createTextNode(context.maxScale.toPrecision(16))); │ │ │ │ + node.appendChild(minSD) │ │ │ │ } │ │ │ │ - return false │ │ │ │ + if (context.minScale) { │ │ │ │ + var maxSD = this.createElementNS(this.namespaces.sld, "sld:MaxScaleDenominator"); │ │ │ │ + maxSD.appendChild(this.createTextNode(context.minScale.toPrecision(16))); │ │ │ │ + node.appendChild(maxSD) │ │ │ │ + } │ │ │ │ + if (context.srs) { │ │ │ │ + for (var name in context.srs) { │ │ │ │ + node.appendChild(this.createElementDefaultNS("SRS", name)) │ │ │ │ + } │ │ │ │ + } │ │ │ │ + node.appendChild(this.write_wmc_FormatList(context)); │ │ │ │ + node.appendChild(this.write_wmc_StyleList(context)); │ │ │ │ + if (context.dimensions) { │ │ │ │ + node.appendChild(this.write_wmc_DimensionList(context)) │ │ │ │ + } │ │ │ │ + node.appendChild(this.write_wmc_LayerExtension(context)); │ │ │ │ + return node │ │ │ │ }, │ │ │ │ - draw: function() { │ │ │ │ - var clickCallbacks = { │ │ │ │ - click: this.defaultClick, │ │ │ │ - dblclick: this.defaultDblClick │ │ │ │ - }; │ │ │ │ - var clickOptions = OpenLayers.Util.extend({ │ │ │ │ - double: true, │ │ │ │ - stopDouble: true, │ │ │ │ - pixelTolerance: 2 │ │ │ │ - }, this.clickHandlerOptions); │ │ │ │ - this.handlers.click = new OpenLayers.Handler.Click(this, clickCallbacks, clickOptions); │ │ │ │ - this.dragPan = new OpenLayers.Control.DragPan(OpenLayers.Util.extend({ │ │ │ │ - map: this.map, │ │ │ │ - documentDrag: this.documentDrag │ │ │ │ - }, this.dragPanOptions)); │ │ │ │ - this.dragPan.draw(); │ │ │ │ - this.pinchZoom = new OpenLayers.Control.PinchZoom(OpenLayers.Util.extend({ │ │ │ │ - map: this.map │ │ │ │ - }, this.pinchZoomOptions)) │ │ │ │ + CLASS_NAME: "OpenLayers.Format.WMC.v1_1_0" │ │ │ │ +}); │ │ │ │ +OpenLayers.Format.SLD.v1 = OpenLayers.Class(OpenLayers.Format.Filter.v1_0_0, { │ │ │ │ + namespaces: { │ │ │ │ + sld: "http://www.opengis.net/sld", │ │ │ │ + ogc: "http://www.opengis.net/ogc", │ │ │ │ + gml: "http://www.opengis.net/gml", │ │ │ │ + xlink: "http://www.w3.org/1999/xlink", │ │ │ │ + xsi: "http://www.w3.org/2001/XMLSchema-instance" │ │ │ │ }, │ │ │ │ - defaultClick: function(evt) { │ │ │ │ - if (evt.lastTouches && evt.lastTouches.length == 2) { │ │ │ │ - this.map.zoomOut() │ │ │ │ - } │ │ │ │ + defaultPrefix: "sld", │ │ │ │ + schemaLocation: null, │ │ │ │ + multipleSymbolizers: false, │ │ │ │ + featureTypeCounter: null, │ │ │ │ + defaultSymbolizer: { │ │ │ │ + fillColor: "#808080", │ │ │ │ + fillOpacity: 1, │ │ │ │ + strokeColor: "#000000", │ │ │ │ + strokeOpacity: 1, │ │ │ │ + strokeWidth: 1, │ │ │ │ + strokeDashstyle: "solid", │ │ │ │ + pointRadius: 3, │ │ │ │ + graphicName: "square" │ │ │ │ }, │ │ │ │ - defaultDblClick: function(evt) { │ │ │ │ - this.map.zoomTo(this.map.zoom + 1, evt.xy) │ │ │ │ + read: function(data, options) { │ │ │ │ + options = OpenLayers.Util.applyDefaults(options, this.options); │ │ │ │ + var sld = { │ │ │ │ + namedLayers: options.namedLayersAsArray === true ? [] : {} │ │ │ │ + }; │ │ │ │ + this.readChildNodes(data, sld); │ │ │ │ + return sld │ │ │ │ }, │ │ │ │ - CLASS_NAME: "OpenLayers.Control.TouchNavigation" │ │ │ │ -}); │ │ │ │ -OpenLayers.Strategy.Cluster = OpenLayers.Class(OpenLayers.Strategy, { │ │ │ │ - distance: 20, │ │ │ │ - threshold: null, │ │ │ │ - features: null, │ │ │ │ - clusters: null, │ │ │ │ - clustering: false, │ │ │ │ - resolution: null, │ │ │ │ - activate: function() { │ │ │ │ - var activated = OpenLayers.Strategy.prototype.activate.call(this); │ │ │ │ - if (activated) { │ │ │ │ - this.layer.events.on({ │ │ │ │ - beforefeaturesadded: this.cacheFeatures, │ │ │ │ - featuresremoved: this.clearCache, │ │ │ │ - moveend: this.cluster, │ │ │ │ - scope: this │ │ │ │ - }) │ │ │ │ + readers: OpenLayers.Util.applyDefaults({ │ │ │ │ + sld: { │ │ │ │ + StyledLayerDescriptor: function(node, sld) { │ │ │ │ + sld.version = node.getAttribute("version"); │ │ │ │ + this.readChildNodes(node, sld) │ │ │ │ + }, │ │ │ │ + Name: function(node, obj) { │ │ │ │ + TRUNCATED DUE TO SIZE LIMIT: 10485760 bytes